Merge "Add date(+weather)(+alarm+dnd) view to lockscreen if decoupling enabled." into tm-qpr-dev

This commit is contained in:
Xiaowen Lei
2023-01-31 16:23:23 +00:00
committed by Android (Google) Code Review
6 changed files with 145 additions and 10 deletions

View File

@@ -91,6 +91,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
private ViewGroup mStatusArea; private ViewGroup mStatusArea;
// If the SMARTSPACE flag is set, keyguard_slice_view is replaced by the following views. // If the SMARTSPACE flag is set, keyguard_slice_view is replaced by the following views.
private ViewGroup mDateWeatherView;
private View mWeatherView; private View mWeatherView;
private View mSmartspaceView; private View mSmartspaceView;
@@ -201,7 +202,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
// TODO(b/261757708): add content observer for the Settings toggle and add/remove // TODO(b/261757708): add content observer for the Settings toggle and add/remove
// weather according to the Settings. // weather according to the Settings.
if (mSmartspaceController.isDateWeatherDecoupled()) { if (mSmartspaceController.isDateWeatherDecoupled()) {
addWeatherView(viewIndex); addDateWeatherView(viewIndex);
viewIndex += 1; viewIndex += 1;
} }
@@ -239,6 +240,14 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
void onLocaleListChanged() { void onLocaleListChanged() {
if (mSmartspaceController.isEnabled()) { if (mSmartspaceController.isEnabled()) {
if (mSmartspaceController.isDateWeatherDecoupled()) {
mDateWeatherView.removeView(mWeatherView);
int index = mStatusArea.indexOfChild(mDateWeatherView);
if (index >= 0) {
mStatusArea.removeView(mDateWeatherView);
addDateWeatherView(index);
}
}
int index = mStatusArea.indexOfChild(mSmartspaceView); int index = mStatusArea.indexOfChild(mSmartspaceView);
if (index >= 0) { if (index >= 0) {
mStatusArea.removeView(mSmartspaceView); mStatusArea.removeView(mSmartspaceView);
@@ -247,16 +256,28 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
} }
} }
private void addWeatherView(int index) { private void addDateWeatherView(int index) {
mWeatherView = mSmartspaceController.buildAndConnectWeatherView(mView); mDateWeatherView = (ViewGroup) mSmartspaceController.buildAndConnectDateView(mView);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
MATCH_PARENT, WRAP_CONTENT); MATCH_PARENT, WRAP_CONTENT);
mStatusArea.addView(mWeatherView, index, lp); mStatusArea.addView(mDateWeatherView, index, lp);
int startPadding = getContext().getResources().getDimensionPixelSize( int startPadding = getContext().getResources().getDimensionPixelSize(
R.dimen.below_clock_padding_start); R.dimen.below_clock_padding_start);
int endPadding = getContext().getResources().getDimensionPixelSize( int endPadding = getContext().getResources().getDimensionPixelSize(
R.dimen.below_clock_padding_end); R.dimen.below_clock_padding_end);
mWeatherView.setPaddingRelative(startPadding, 0, endPadding, 0); mDateWeatherView.setPaddingRelative(startPadding, 0, endPadding, 0);
addWeatherView();
}
private void addWeatherView() {
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
WRAP_CONTENT, WRAP_CONTENT);
mWeatherView = mSmartspaceController.buildAndConnectWeatherView(mView);
// Place weather right after the date, before the extras
final int index = mDateWeatherView.getChildCount() == 0 ? 0 : 1;
mDateWeatherView.addView(mWeatherView, index, lp);
mWeatherView.setPaddingRelative(0, 0, 4, 0);
} }
private void addSmartspaceView(int index) { private void addSmartspaceView(int index) {

View File

@@ -217,6 +217,10 @@ public abstract class SystemUIModule {
@BindsOptionalOf @BindsOptionalOf
abstract BcSmartspaceConfigPlugin optionalBcSmartspaceConfigPlugin(); abstract BcSmartspaceConfigPlugin optionalBcSmartspaceConfigPlugin();
@BindsOptionalOf
@Named(SmartspaceModule.DATE_SMARTSPACE_DATA_PLUGIN)
abstract BcSmartspaceDataPlugin optionalDateSmartspaceConfigPlugin();
@BindsOptionalOf @BindsOptionalOf
@Named(SmartspaceModule.WEATHER_SMARTSPACE_DATA_PLUGIN) @Named(SmartspaceModule.WEATHER_SMARTSPACE_DATA_PLUGIN)
abstract BcSmartspaceDataPlugin optionalWeatherSmartspaceConfigPlugin(); abstract BcSmartspaceDataPlugin optionalWeatherSmartspaceConfigPlugin();

View File

@@ -43,6 +43,11 @@ abstract class SmartspaceModule {
*/ */
const val DREAM_SMARTSPACE_PRECONDITION = "dream_smartspace_precondition" const val DREAM_SMARTSPACE_PRECONDITION = "dream_smartspace_precondition"
/**
* The BcSmartspaceDataPlugin for the standalone date (+alarm+dnd).
*/
const val DATE_SMARTSPACE_DATA_PLUGIN = "date_smartspace_data_plugin"
/** /**
* The BcSmartspaceDataPlugin for the standalone weather. * The BcSmartspaceDataPlugin for the standalone weather.
*/ */

View File

@@ -52,6 +52,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.settings.UserTracker import com.android.systemui.settings.UserTracker
import com.android.systemui.shared.regionsampling.RegionSampler import com.android.systemui.shared.regionsampling.RegionSampler
import com.android.systemui.shared.regionsampling.UpdateColorCallback import com.android.systemui.shared.regionsampling.UpdateColorCallback
import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.DATE_SMARTSPACE_DATA_PLUGIN
import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.WEATHER_SMARTSPACE_DATA_PLUGIN import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.WEATHER_SMARTSPACE_DATA_PLUGIN
import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.ConfigurationController
@@ -84,6 +85,8 @@ constructor(
@Main private val uiExecutor: Executor, @Main private val uiExecutor: Executor,
@Background private val bgExecutor: Executor, @Background private val bgExecutor: Executor,
@Main private val handler: Handler, @Main private val handler: Handler,
@Named(DATE_SMARTSPACE_DATA_PLUGIN)
optionalDatePlugin: Optional<BcSmartspaceDataPlugin>,
@Named(WEATHER_SMARTSPACE_DATA_PLUGIN) @Named(WEATHER_SMARTSPACE_DATA_PLUGIN)
optionalWeatherPlugin: Optional<BcSmartspaceDataPlugin>, optionalWeatherPlugin: Optional<BcSmartspaceDataPlugin>,
optionalPlugin: Optional<BcSmartspaceDataPlugin>, optionalPlugin: Optional<BcSmartspaceDataPlugin>,
@@ -94,6 +97,7 @@ constructor(
} }
private var session: SmartspaceSession? = null private var session: SmartspaceSession? = null
private val datePlugin: BcSmartspaceDataPlugin? = optionalDatePlugin.orElse(null)
private val weatherPlugin: BcSmartspaceDataPlugin? = optionalWeatherPlugin.orElse(null) private val weatherPlugin: BcSmartspaceDataPlugin? = optionalWeatherPlugin.orElse(null)
private val plugin: BcSmartspaceDataPlugin? = optionalPlugin.orElse(null) private val plugin: BcSmartspaceDataPlugin? = optionalPlugin.orElse(null)
private val configPlugin: BcSmartspaceConfigPlugin? = optionalConfigPlugin.orElse(null) private val configPlugin: BcSmartspaceConfigPlugin? = optionalConfigPlugin.orElse(null)
@@ -222,7 +226,7 @@ constructor(
execution.assertIsMainThread() execution.assertIsMainThread()
return featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED) && return featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED) &&
weatherPlugin != null datePlugin != null && weatherPlugin != null
} }
private fun updateBypassEnabled() { private fun updateBypassEnabled() {
@@ -230,6 +234,25 @@ constructor(
smartspaceViews.forEach { it.setKeyguardBypassEnabled(bypassEnabled) } smartspaceViews.forEach { it.setKeyguardBypassEnabled(bypassEnabled) }
} }
/**
* Constructs the date view and connects it to the smartspace service.
*/
fun buildAndConnectDateView(parent: ViewGroup): View? {
execution.assertIsMainThread()
if (!isEnabled()) {
throw RuntimeException("Cannot build view when not enabled")
}
if (!isDateWeatherDecoupled()) {
throw RuntimeException("Cannot build date view when not decoupled")
}
val view = buildView(parent, datePlugin)
connectSession()
return view
}
/** /**
* Constructs the weather view and connects it to the smartspace service. * Constructs the weather view and connects it to the smartspace service.
*/ */
@@ -308,7 +331,7 @@ constructor(
} }
private fun connectSession() { private fun connectSession() {
if (weatherPlugin == null && plugin == null) return if (datePlugin == null && weatherPlugin == null && plugin == null) return
if (session != null || smartspaceViews.isEmpty()) { if (session != null || smartspaceViews.isEmpty()) {
return return
} }
@@ -346,6 +369,7 @@ constructor(
statusBarStateController.addCallback(statusBarStateListener) statusBarStateController.addCallback(statusBarStateListener)
bypassController.registerOnBypassStateChangedListener(bypassStateChangedListener) bypassController.registerOnBypassStateChangedListener(bypassStateChangedListener)
datePlugin?.registerSmartspaceEventNotifier { e -> session?.notifySmartspaceEvent(e) }
weatherPlugin?.registerSmartspaceEventNotifier { e -> session?.notifySmartspaceEvent(e) } weatherPlugin?.registerSmartspaceEventNotifier { e -> session?.notifySmartspaceEvent(e) }
plugin?.registerSmartspaceEventNotifier { e -> session?.notifySmartspaceEvent(e) } plugin?.registerSmartspaceEventNotifier { e -> session?.notifySmartspaceEvent(e) }
@@ -383,6 +407,8 @@ constructor(
bypassController.unregisterOnBypassStateChangedListener(bypassStateChangedListener) bypassController.unregisterOnBypassStateChangedListener(bypassStateChangedListener)
session = null session = null
datePlugin?.registerSmartspaceEventNotifier(null)
weatherPlugin?.registerSmartspaceEventNotifier(null) weatherPlugin?.registerSmartspaceEventNotifier(null)
weatherPlugin?.onTargetsAvailable(emptyList()) weatherPlugin?.onTargetsAvailable(emptyList())

View File

@@ -33,6 +33,7 @@ import android.os.UserHandle;
import android.provider.Settings; import android.provider.Settings;
import android.testing.AndroidTestingRunner; import android.testing.AndroidTestingRunner;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
@@ -118,6 +119,11 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
@Mock @Mock
private LogBuffer mLogBuffer; private LogBuffer mLogBuffer;
private final View mFakeDateView = (View) (new ViewGroup(mContext) {
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {}
});
private final View mFakeWeatherView = new View(mContext);
private final View mFakeSmartspaceView = new View(mContext); private final View mFakeSmartspaceView = new View(mContext);
private KeyguardClockSwitchController mController; private KeyguardClockSwitchController mController;
@@ -145,6 +151,8 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
when(mLargeClockView.getContext()).thenReturn(getContext()); when(mLargeClockView.getContext()).thenReturn(getContext());
when(mView.isAttachedToWindow()).thenReturn(true); when(mView.isAttachedToWindow()).thenReturn(true);
when(mSmartspaceController.buildAndConnectDateView(any())).thenReturn(mFakeDateView);
when(mSmartspaceController.buildAndConnectWeatherView(any())).thenReturn(mFakeWeatherView);
when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mFakeSmartspaceView); when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mFakeSmartspaceView);
mExecutor = new FakeExecutor(new FakeSystemClock()); mExecutor = new FakeExecutor(new FakeSystemClock());
mController = new KeyguardClockSwitchController( mController = new KeyguardClockSwitchController(
@@ -251,6 +259,19 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
verify(mSmartspaceController, times(2)).buildAndConnectView(mView); verify(mSmartspaceController, times(2)).buildAndConnectView(mView);
} }
@Test
public void onLocaleListChanged_rebuildsSmartspaceViews_whenDecouplingEnabled() {
when(mSmartspaceController.isEnabled()).thenReturn(true);
when(mSmartspaceController.isDateWeatherDecoupled()).thenReturn(true);
mController.init();
mController.onLocaleListChanged();
// Should be called once on initial setup, then once again for locale change
verify(mSmartspaceController, times(2)).buildAndConnectDateView(mView);
verify(mSmartspaceController, times(2)).buildAndConnectWeatherView(mView);
verify(mSmartspaceController, times(2)).buildAndConnectView(mView);
}
@Test @Test
public void testSmartspaceDisabledShowsKeyguardStatusArea() { public void testSmartspaceDisabledShowsKeyguardStatusArea() {
when(mSmartspaceController.isEnabled()).thenReturn(false); when(mSmartspaceController.isEnabled()).thenReturn(false);

View File

@@ -112,6 +112,9 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
@Mock @Mock
private lateinit var handler: Handler private lateinit var handler: Handler
@Mock
private lateinit var datePlugin: BcSmartspaceDataPlugin
@Mock @Mock
private lateinit var weatherPlugin: BcSmartspaceDataPlugin private lateinit var weatherPlugin: BcSmartspaceDataPlugin
@@ -155,6 +158,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
KeyguardBypassController.OnBypassStateChangedListener KeyguardBypassController.OnBypassStateChangedListener
private lateinit var deviceProvisionedListener: DeviceProvisionedListener private lateinit var deviceProvisionedListener: DeviceProvisionedListener
private lateinit var dateSmartspaceView: SmartspaceView
private lateinit var weatherSmartspaceView: SmartspaceView private lateinit var weatherSmartspaceView: SmartspaceView
private lateinit var smartspaceView: SmartspaceView private lateinit var smartspaceView: SmartspaceView
@@ -190,6 +194,8 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
`when`(secureSettings.getUriFor(NOTIF_ON_LOCKSCREEN_SETTING)) `when`(secureSettings.getUriFor(NOTIF_ON_LOCKSCREEN_SETTING))
.thenReturn(fakeNotifOnLockscreenSettingUri) .thenReturn(fakeNotifOnLockscreenSettingUri)
`when`(smartspaceManager.createSmartspaceSession(any())).thenReturn(smartspaceSession) `when`(smartspaceManager.createSmartspaceSession(any())).thenReturn(smartspaceSession)
`when`(datePlugin.getView(any())).thenReturn(
createDateSmartspaceView(), createDateSmartspaceView())
`when`(weatherPlugin.getView(any())).thenReturn( `when`(weatherPlugin.getView(any())).thenReturn(
createWeatherSmartspaceView(), createWeatherSmartspaceView()) createWeatherSmartspaceView(), createWeatherSmartspaceView())
`when`(plugin.getView(any())).thenReturn(createSmartspaceView(), createSmartspaceView()) `when`(plugin.getView(any())).thenReturn(createSmartspaceView(), createSmartspaceView())
@@ -221,6 +227,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
executor, executor,
bgExecutor, bgExecutor,
handler, handler,
Optional.of(datePlugin),
Optional.of(weatherPlugin), Optional.of(weatherPlugin),
Optional.of(plugin), Optional.of(plugin),
Optional.of(configPlugin), Optional.of(configPlugin),
@@ -275,7 +282,8 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
// THEN the listener is registered to the underlying plugin // THEN the listener is registered to the underlying plugin
verify(plugin).registerListener(controllerListener) verify(plugin).registerListener(controllerListener)
// The listener is registered only for the plugin, not the weather plugin. // The listener is registered only for the plugin, not the date, or weather plugin.
verify(datePlugin, never()).registerListener(any())
verify(weatherPlugin, never()).registerListener(any()) verify(weatherPlugin, never()).registerListener(any())
} }
@@ -289,7 +297,8 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
// THEN the listener is subsequently registered // THEN the listener is subsequently registered
verify(plugin).registerListener(controllerListener) verify(plugin).registerListener(controllerListener)
// The listener is registered only for the plugin, not the weather plugin. // The listener is registered only for the plugin, not the date, or the weather plugin.
verify(datePlugin, never()).registerListener(any())
verify(weatherPlugin, never()).registerListener(any()) verify(weatherPlugin, never()).registerListener(any())
} }
@@ -308,6 +317,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
verify(plugin).registerSmartspaceEventNotifier(null) verify(plugin).registerSmartspaceEventNotifier(null)
verify(weatherPlugin).onTargetsAvailable(emptyList()) verify(weatherPlugin).onTargetsAvailable(emptyList())
verify(weatherPlugin).registerSmartspaceEventNotifier(null) verify(weatherPlugin).registerSmartspaceEventNotifier(null)
verify(datePlugin).registerSmartspaceEventNotifier(null)
} }
@Test @Test
@@ -357,6 +367,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
configChangeListener.onThemeChanged() configChangeListener.onThemeChanged()
// We update the new text color to match the wallpaper color // We update the new text color to match the wallpaper color
verify(dateSmartspaceView).setPrimaryTextColor(anyInt())
verify(weatherSmartspaceView).setPrimaryTextColor(anyInt()) verify(weatherSmartspaceView).setPrimaryTextColor(anyInt())
verify(smartspaceView).setPrimaryTextColor(anyInt()) verify(smartspaceView).setPrimaryTextColor(anyInt())
} }
@@ -384,6 +395,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
statusBarStateListener.onDozeAmountChanged(0.1f, 0.7f) statusBarStateListener.onDozeAmountChanged(0.1f, 0.7f)
// We pass that along to the view // We pass that along to the view
verify(dateSmartspaceView).setDozeAmount(0.7f)
verify(weatherSmartspaceView).setDozeAmount(0.7f) verify(weatherSmartspaceView).setDozeAmount(0.7f)
verify(smartspaceView).setDozeAmount(0.7f) verify(smartspaceView).setDozeAmount(0.7f)
} }
@@ -502,6 +514,8 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
verify(plugin).onTargetsAvailable(eq(listOf(targets[0], targets[1], targets[2]))) verify(plugin).onTargetsAvailable(eq(listOf(targets[0], targets[1], targets[2])))
// No filtering is applied for the weather plugin // No filtering is applied for the weather plugin
verify(weatherPlugin).onTargetsAvailable(eq(targets)) verify(weatherPlugin).onTargetsAvailable(eq(targets))
// No targets needed for the date plugin
verify(datePlugin, never()).onTargetsAvailable(any())
} }
@Test @Test
@@ -633,6 +647,18 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
private fun connectSession() { private fun connectSession() {
if (controller.isDateWeatherDecoupled()) { if (controller.isDateWeatherDecoupled()) {
val dateView = controller.buildAndConnectDateView(fakeParent)
dateSmartspaceView = dateView as SmartspaceView
fakeParent.addView(dateView)
controller.stateChangeListener.onViewAttachedToWindow(dateView)
verify(dateSmartspaceView).setUiSurface(
BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD)
verify(dateSmartspaceView).registerDataProvider(datePlugin)
verify(dateSmartspaceView).setPrimaryTextColor(anyInt())
verify(dateSmartspaceView).setDozeAmount(0.5f)
val weatherView = controller.buildAndConnectWeatherView(fakeParent) val weatherView = controller.buildAndConnectWeatherView(fakeParent)
weatherSmartspaceView = weatherView as SmartspaceView weatherSmartspaceView = weatherView as SmartspaceView
fakeParent.addView(weatherView) fakeParent.addView(weatherView)
@@ -686,6 +712,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
verify(smartspaceView).setDozeAmount(0.5f) verify(smartspaceView).setDozeAmount(0.5f)
if (controller.isDateWeatherDecoupled()) { if (controller.isDateWeatherDecoupled()) {
clearInvocations(dateSmartspaceView)
clearInvocations(weatherSmartspaceView) clearInvocations(weatherSmartspaceView)
} }
clearInvocations(smartspaceView) clearInvocations(smartspaceView)
@@ -734,7 +761,38 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
).thenReturn(if (value) 1 else 0) ).thenReturn(if (value) 1 else 0)
} }
// Separate function for the weather view, which doesn't implement all functions in interface. // Separate function for the date view, which implements a specific subset of all functions.
private fun createDateSmartspaceView(): SmartspaceView {
return spy(object : View(context), SmartspaceView {
override fun registerDataProvider(plugin: BcSmartspaceDataPlugin?) {
}
override fun setPrimaryTextColor(color: Int) {
}
override fun setIsDreaming(isDreaming: Boolean) {
}
override fun setUiSurface(uiSurface: String) {
}
override fun setDozeAmount(amount: Float) {
}
override fun setIntentStarter(intentStarter: BcSmartspaceDataPlugin.IntentStarter?) {
}
override fun setFalsingManager(falsingManager: FalsingManager?) {
}
override fun setDnd(image: Drawable?, description: String?) {
}
override fun setNextAlarm(image: Drawable?, description: String?) {
}
})
}
// Separate function for the weather view, which implements a specific subset of all functions.
private fun createWeatherSmartspaceView(): SmartspaceView { private fun createWeatherSmartspaceView(): SmartspaceView {
return spy(object : View(context), SmartspaceView { return spy(object : View(context), SmartspaceView {
override fun registerDataProvider(plugin: BcSmartspaceDataPlugin?) { override fun registerDataProvider(plugin: BcSmartspaceDataPlugin?) {