diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index 0685794dae521..dd42c8ee76d40 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -91,6 +91,7 @@ public class KeyguardClockSwitchController extends ViewController= 0) { + mStatusArea.removeView(mDateWeatherView); + addDateWeatherView(index); + } + } int index = mStatusArea.indexOfChild(mSmartspaceView); if (index >= 0) { mStatusArea.removeView(mSmartspaceView); @@ -247,16 +256,28 @@ public class KeyguardClockSwitchController extends ViewController, @Named(WEATHER_SMARTSPACE_DATA_PLUGIN) optionalWeatherPlugin: Optional, optionalPlugin: Optional, @@ -94,6 +97,7 @@ constructor( } private var session: SmartspaceSession? = null + private val datePlugin: BcSmartspaceDataPlugin? = optionalDatePlugin.orElse(null) private val weatherPlugin: BcSmartspaceDataPlugin? = optionalWeatherPlugin.orElse(null) private val plugin: BcSmartspaceDataPlugin? = optionalPlugin.orElse(null) private val configPlugin: BcSmartspaceConfigPlugin? = optionalConfigPlugin.orElse(null) @@ -223,7 +227,7 @@ constructor( execution.assertIsMainThread() return featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED) && - weatherPlugin != null + datePlugin != null && weatherPlugin != null } private fun updateBypassEnabled() { @@ -231,6 +235,25 @@ constructor( 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. */ @@ -309,7 +332,7 @@ constructor( } private fun connectSession() { - if (weatherPlugin == null && plugin == null) return + if (datePlugin == null && weatherPlugin == null && plugin == null) return if (session != null || smartspaceViews.isEmpty()) { return } @@ -347,6 +370,7 @@ constructor( statusBarStateController.addCallback(statusBarStateListener) bypassController.registerOnBypassStateChangedListener(bypassStateChangedListener) + datePlugin?.registerSmartspaceEventNotifier { e -> session?.notifySmartspaceEvent(e) } weatherPlugin?.registerSmartspaceEventNotifier { e -> session?.notifySmartspaceEvent(e) } plugin?.registerSmartspaceEventNotifier { e -> session?.notifySmartspaceEvent(e) } @@ -384,6 +408,8 @@ constructor( bypassController.unregisterOnBypassStateChangedListener(bypassStateChangedListener) session = null + datePlugin?.registerSmartspaceEventNotifier(null) + weatherPlugin?.registerSmartspaceEventNotifier(null) weatherPlugin?.onTargetsAvailable(emptyList()) diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java index a4180fd2e0f13..512c3515b587b 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java @@ -33,6 +33,7 @@ import android.os.UserHandle; import android.provider.Settings; import android.testing.AndroidTestingRunner; import android.view.View; +import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.RelativeLayout; @@ -118,6 +119,11 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { @Mock 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 KeyguardClockSwitchController mController; @@ -145,6 +151,8 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { when(mLargeClockView.getContext()).thenReturn(getContext()); when(mView.isAttachedToWindow()).thenReturn(true); + when(mSmartspaceController.buildAndConnectDateView(any())).thenReturn(mFakeDateView); + when(mSmartspaceController.buildAndConnectWeatherView(any())).thenReturn(mFakeWeatherView); when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mFakeSmartspaceView); mExecutor = new FakeExecutor(new FakeSystemClock()); mController = new KeyguardClockSwitchController( @@ -251,6 +259,19 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { 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 public void testSmartspaceDisabledShowsKeyguardStatusArea() { when(mSmartspaceController.isEnabled()).thenReturn(false); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt index 2423f13e6494a..0a576de4c3a9c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt @@ -112,6 +112,9 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { @Mock private lateinit var handler: Handler + @Mock + private lateinit var datePlugin: BcSmartspaceDataPlugin + @Mock private lateinit var weatherPlugin: BcSmartspaceDataPlugin @@ -155,6 +158,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { KeyguardBypassController.OnBypassStateChangedListener private lateinit var deviceProvisionedListener: DeviceProvisionedListener + private lateinit var dateSmartspaceView: SmartspaceView private lateinit var weatherSmartspaceView: SmartspaceView private lateinit var smartspaceView: SmartspaceView @@ -190,6 +194,8 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { `when`(secureSettings.getUriFor(NOTIF_ON_LOCKSCREEN_SETTING)) .thenReturn(fakeNotifOnLockscreenSettingUri) `when`(smartspaceManager.createSmartspaceSession(any())).thenReturn(smartspaceSession) + `when`(datePlugin.getView(any())).thenReturn( + createDateSmartspaceView(), createDateSmartspaceView()) `when`(weatherPlugin.getView(any())).thenReturn( createWeatherSmartspaceView(), createWeatherSmartspaceView()) `when`(plugin.getView(any())).thenReturn(createSmartspaceView(), createSmartspaceView()) @@ -221,6 +227,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { executor, bgExecutor, handler, + Optional.of(datePlugin), Optional.of(weatherPlugin), Optional.of(plugin), Optional.of(configPlugin), @@ -275,7 +282,8 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { // THEN the listener is registered to the underlying plugin 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()) } @@ -289,7 +297,8 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { // THEN the listener is subsequently registered 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()) } @@ -308,6 +317,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { verify(plugin).registerSmartspaceEventNotifier(null) verify(weatherPlugin).onTargetsAvailable(emptyList()) verify(weatherPlugin).registerSmartspaceEventNotifier(null) + verify(datePlugin).registerSmartspaceEventNotifier(null) } @Test @@ -357,6 +367,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { configChangeListener.onThemeChanged() // We update the new text color to match the wallpaper color + verify(dateSmartspaceView).setPrimaryTextColor(anyInt()) verify(weatherSmartspaceView).setPrimaryTextColor(anyInt()) verify(smartspaceView).setPrimaryTextColor(anyInt()) } @@ -384,6 +395,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { statusBarStateListener.onDozeAmountChanged(0.1f, 0.7f) // We pass that along to the view + verify(dateSmartspaceView).setDozeAmount(0.7f) verify(weatherSmartspaceView).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]))) // No filtering is applied for the weather plugin verify(weatherPlugin).onTargetsAvailable(eq(targets)) + // No targets needed for the date plugin + verify(datePlugin, never()).onTargetsAvailable(any()) } @Test @@ -633,6 +647,18 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { private fun connectSession() { 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) weatherSmartspaceView = weatherView as SmartspaceView fakeParent.addView(weatherView) @@ -686,6 +712,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { verify(smartspaceView).setDozeAmount(0.5f) if (controller.isDateWeatherDecoupled()) { + clearInvocations(dateSmartspaceView) clearInvocations(weatherSmartspaceView) } clearInvocations(smartspaceView) @@ -734,7 +761,38 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { ).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 { return spy(object : View(context), SmartspaceView { override fun registerDataProvider(plugin: BcSmartspaceDataPlugin?) {