From 6ad767a09c6f34ce8e6f89a182e8a77acb8c1421 Mon Sep 17 00:00:00 2001 From: Matt Pietal Date: Thu, 12 Aug 2021 15:24:05 -0400 Subject: [PATCH] Support multiple smartspace views Previously, a singleton view was reused in multiple places. However, this does not work when multiple displays are in use, such as when casting the device display to another screen. Supports sets of views that are activated when they are attached to windows, and removed when they are detached. Fixes: 196175396 Test: atest LockscreenSmartspaceControllerTest Change-Id: Ied1ecff9103d8539f075785409d2951a1be21ab2 --- .../KeyguardClockSwitchController.java | 10 --- .../LockscreenSmartspaceController.kt | 47 +++++----- .../LockscreenSmartspaceControllerTest.kt | 87 +++++++++---------- 3 files changed, 63 insertions(+), 81 deletions(-) diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index 539fb656eaa2d..260b39378485c 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -239,16 +239,6 @@ public class KeyguardClockSwitchController extends ViewController() private var showSensitiveContentForCurrentUser = false private var showSensitiveContentForManagedUser = false private var managedUserHandle: UserHandle? = null + var stateChangeListener = object : View.OnAttachStateChangeListener { + override fun onViewAttachedToWindow(v: View) { + smartspaceViews.add(v as SmartspaceView) + + updateTextColorFromWallpaper() + statusBarStateListener.onDozeAmountChanged(0f, statusBarStateController.dozeAmount) + } + + override fun onViewDetachedFromWindow(v: View) { + smartspaceViews.remove(v as SmartspaceView) + } + } + fun isEnabled(): Boolean { execution.assertIsMainThread() @@ -90,17 +102,16 @@ class LockscreenSmartspaceController @Inject constructor( } /** - * Constructs the smartspace view and connects it to the smartspace service. Subsequent calls - * are idempotent until [disconnect] is called. + * Constructs the smartspace view and connects it to the smartspace service. */ - fun buildAndConnectView(parent: ViewGroup): View { + fun buildAndConnectView(parent: ViewGroup): View? { execution.assertIsMainThread() if (!isEnabled()) { throw RuntimeException("Cannot build view when not enabled") } - buildView(parent) + val view = buildView(parent) connectSession() return view @@ -110,14 +121,9 @@ class LockscreenSmartspaceController @Inject constructor( session?.requestSmartspaceUpdate() } - private fun buildView(parent: ViewGroup) { + private fun buildView(parent: ViewGroup): View? { if (plugin == null) { - return - } - if (this::view.isInitialized) { - // Due to some oddities with a singleton smartspace view, allow reparenting - (view.getParent() as ViewGroup?)?.removeView(view) - return + return null } val ssView = plugin.getView(parent) @@ -132,12 +138,7 @@ class LockscreenSmartspaceController @Inject constructor( } }) ssView.setFalsingManager(falsingManager) - - this.smartspaceView = ssView - this.view = ssView as View - - updateTextColorFromWallpaper() - statusBarStateListener.onDozeAmountChanged(0f, statusBarStateController.dozeAmount) + return (ssView as View).apply { addOnAttachStateChangeListener(stateChangeListener) } } private fun connectSession() { @@ -165,8 +166,6 @@ class LockscreenSmartspaceController @Inject constructor( /** * Disconnects the smartspace view from the smartspace service and cleans up any resources. - * Calling [buildAndConnectView] again will cause the same view to be reconnected to the - * service. */ fun disconnect() { execution.assertIsMainThread() @@ -231,7 +230,7 @@ class LockscreenSmartspaceController @Inject constructor( private val statusBarStateListener = object : StatusBarStateController.StateListener { override fun onDozeAmountChanged(linear: Float, eased: Float) { execution.assertIsMainThread() - smartspaceView.setDozeAmount(eased) + smartspaceViews.forEach { it.setDozeAmount(eased) } } } @@ -256,7 +255,7 @@ class LockscreenSmartspaceController @Inject constructor( private fun updateTextColorFromWallpaper() { val wallpaperTextColor = Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColor) - smartspaceView.setPrimaryTextColor(wallpaperTextColor) + smartspaceViews.forEach { it.setPrimaryTextColor(wallpaperTextColor) } } private fun reloadSmartspace() { 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 a2bb0aff48e36..575e01cf6e0f7 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 @@ -51,7 +51,6 @@ import com.android.systemui.util.mockito.capture import com.android.systemui.util.mockito.eq import com.android.systemui.util.settings.SecureSettings import com.android.systemui.util.time.FakeSystemClock -import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test import org.mockito.ArgumentCaptor @@ -114,6 +113,8 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { private lateinit var configChangeListener: ConfigurationListener private lateinit var statusBarStateListener: StateListener + private lateinit var smartspaceView: SmartspaceView + private val clock = FakeSystemClock() private val executor = FakeExecutor(clock) private val execution = FakeExecution() @@ -141,7 +142,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { `when`(secureSettings.getUriFor(PRIVATE_LOCKSCREEN_SETTING)) .thenReturn(fakePrivateLockscreenSettingUri) `when`(smartspaceManager.createSmartspaceSession(any())).thenReturn(smartspaceSession) - `when`(plugin.getView(any())).thenReturn(fakeSmartspaceView) + `when`(plugin.getView(any())).thenReturn(createSmartspaceView(), createSmartspaceView()) `when`(userTracker.userProfiles).thenReturn(userList) `when`(statusBarStateController.dozeAmount).thenReturn(0.5f) @@ -249,7 +250,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { configChangeListener.onThemeChanged() // We update the new text color to match the wallpaper color - verify(fakeSmartspaceView).setPrimaryTextColor(anyInt()) + verify(smartspaceView).setPrimaryTextColor(anyInt()) } @Test @@ -261,7 +262,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { statusBarStateListener.onDozeAmountChanged(0.1f, 0.7f) // We pass that along to the view - verify(fakeSmartspaceView).setDozeAmount(0.7f) + verify(smartspaceView).setDozeAmount(0.7f) } @Test @@ -393,40 +394,30 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { } @Test - fun testBuildViewIsIdempotent() { - // GIVEN a connected session - connectSession() - clearInvocations(plugin) - - // WHEN we disconnect and then reconnect - controller.disconnect() - controller.buildAndConnectView(fakeParent) - - // THEN the view is not rebuilt - verify(plugin, never()).getView(any()) - assertEquals(fakeSmartspaceView, controller.view) - } - - @Test - fun testDoubleConnectIsIgnored() { + fun testMultipleViewsUseSameSession() { // GIVEN a connected session connectSession() clearInvocations(smartspaceManager) clearInvocations(plugin) - // WHEN we're asked to connect a second time and add to a parent + // WHEN we're asked to connect a second time and add to a parent. If the same view + // was created the ViewGroup will throw an exception val view = controller.buildAndConnectView(fakeParent) fakeParent.addView(view) + val smartspaceView2 = view as SmartspaceView - // THEN the existing view and session are reused + // THEN the existing session is reused and views are registered verify(smartspaceManager, never()).createSmartspaceSession(any()) - verify(plugin, never()).getView(any()) - assertEquals(fakeSmartspaceView, controller.view) + verify(smartspaceView2).registerDataProvider(plugin) } private fun connectSession() { - controller.buildAndConnectView(fakeParent) + val view = controller.buildAndConnectView(fakeParent) + smartspaceView = view as SmartspaceView + controller.stateChangeListener.onViewAttachedToWindow(view) + + verify(smartspaceView).registerDataProvider(plugin) verify(smartspaceSession) .addOnTargetsAvailableListener(any(), capture(sessionListenerCaptor)) sessionListener = sessionListenerCaptor.value @@ -450,11 +441,11 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { verify(smartspaceSession).requestSmartspaceUpdate() clearInvocations(smartspaceSession) - verify(fakeSmartspaceView).setPrimaryTextColor(anyInt()) - verify(fakeSmartspaceView).setDozeAmount(0.5f) - clearInvocations(fakeSmartspaceView) + verify(smartspaceView).setPrimaryTextColor(anyInt()) + verify(smartspaceView).setDozeAmount(0.5f) + clearInvocations(view) - fakeParent.addView(fakeSmartspaceView) + fakeParent.addView(view) } private fun setActiveUser(userHandle: UserHandle) { @@ -490,31 +481,33 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { ).thenReturn(if (value) 1 else 0) } - private val fakeSmartspaceView = spy(object : View(context), SmartspaceView { - override fun registerDataProvider(plugin: BcSmartspaceDataPlugin?) { - } + private fun createSmartspaceView(): SmartspaceView { + return spy(object : View(context), SmartspaceView { + override fun registerDataProvider(plugin: BcSmartspaceDataPlugin?) { + } - override fun setPrimaryTextColor(color: Int) { - } + override fun setPrimaryTextColor(color: Int) { + } - override fun setDozeAmount(amount: Float) { - } + override fun setDozeAmount(amount: Float) { + } - override fun setIntentStarter(intentStarter: BcSmartspaceDataPlugin.IntentStarter?) { - } + override fun setIntentStarter(intentStarter: BcSmartspaceDataPlugin.IntentStarter?) { + } - override fun setFalsingManager(falsingManager: FalsingManager?) { - } + override fun setFalsingManager(falsingManager: FalsingManager?) { + } - override fun setDnd(image: Drawable?, description: String?) { - } + override fun setDnd(image: Drawable?, description: String?) { + } - override fun setNextAlarm(image: Drawable?, description: String?) { - } + override fun setNextAlarm(image: Drawable?, description: String?) { + } - override fun setMediaTarget(target: SmartspaceTarget?) { - } - }) + override fun setMediaTarget(target: SmartspaceTarget?) { + } + }) + } } private const val PRIVATE_LOCKSCREEN_SETTING =