[DO NOT MERGE] 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
This commit is contained in:
@@ -234,16 +234,6 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
|
|||||||
mView.setClockPlugin(null, mStatusBarStateController.getState());
|
mView.setClockPlugin(null, mStatusBarStateController.getState());
|
||||||
|
|
||||||
mSmartspaceController.disconnect();
|
mSmartspaceController.disconnect();
|
||||||
|
|
||||||
// TODO: This is an unfortunate necessity since smartspace plugin retains a single instance
|
|
||||||
// of the smartspace view -- if we don't remove the view, it can't be reused by a later
|
|
||||||
// instance of this class. In order to fix this, we need to modify the plugin so that
|
|
||||||
// (a) we get a new view each time and (b) we can properly clean up an old view by making
|
|
||||||
// it unregister itself as a plugin listener.
|
|
||||||
if (mSmartspaceView != null) {
|
|
||||||
mView.removeView(mSmartspaceView);
|
|
||||||
mSmartspaceView = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -74,15 +74,27 @@ class LockscreenSmartspaceController @Inject constructor(
|
|||||||
) {
|
) {
|
||||||
private var session: SmartspaceSession? = null
|
private var session: SmartspaceSession? = null
|
||||||
private val plugin: BcSmartspaceDataPlugin? = optionalPlugin.orElse(null)
|
private val plugin: BcSmartspaceDataPlugin? = optionalPlugin.orElse(null)
|
||||||
private lateinit var smartspaceView: SmartspaceView
|
|
||||||
|
|
||||||
lateinit var view: View
|
// Smartspace can be used on multiple displays, such as when the user casts their screen
|
||||||
private set
|
private var smartspaceViews = mutableSetOf<SmartspaceView>()
|
||||||
|
|
||||||
private var showSensitiveContentForCurrentUser = false
|
private var showSensitiveContentForCurrentUser = false
|
||||||
private var showSensitiveContentForManagedUser = false
|
private var showSensitiveContentForManagedUser = false
|
||||||
private var managedUserHandle: UserHandle? = null
|
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 {
|
fun isEnabled(): Boolean {
|
||||||
execution.assertIsMainThread()
|
execution.assertIsMainThread()
|
||||||
|
|
||||||
@@ -90,17 +102,16 @@ class LockscreenSmartspaceController @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs the smartspace view and connects it to the smartspace service. Subsequent calls
|
* Constructs the smartspace view and connects it to the smartspace service.
|
||||||
* are idempotent until [disconnect] is called.
|
|
||||||
*/
|
*/
|
||||||
fun buildAndConnectView(parent: ViewGroup): View {
|
fun buildAndConnectView(parent: ViewGroup): View? {
|
||||||
execution.assertIsMainThread()
|
execution.assertIsMainThread()
|
||||||
|
|
||||||
if (!isEnabled()) {
|
if (!isEnabled()) {
|
||||||
throw RuntimeException("Cannot build view when not enabled")
|
throw RuntimeException("Cannot build view when not enabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
buildView(parent)
|
val view = buildView(parent)
|
||||||
connectSession()
|
connectSession()
|
||||||
|
|
||||||
return view
|
return view
|
||||||
@@ -110,14 +121,9 @@ class LockscreenSmartspaceController @Inject constructor(
|
|||||||
session?.requestSmartspaceUpdate()
|
session?.requestSmartspaceUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildView(parent: ViewGroup) {
|
private fun buildView(parent: ViewGroup): View? {
|
||||||
if (plugin == null) {
|
if (plugin == null) {
|
||||||
return
|
return null
|
||||||
}
|
|
||||||
if (this::view.isInitialized) {
|
|
||||||
// Due to some oddities with a singleton smartspace view, allow reparenting
|
|
||||||
(view.getParent() as ViewGroup?)?.removeView(view)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val ssView = plugin.getView(parent)
|
val ssView = plugin.getView(parent)
|
||||||
@@ -132,12 +138,7 @@ class LockscreenSmartspaceController @Inject constructor(
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
ssView.setFalsingManager(falsingManager)
|
ssView.setFalsingManager(falsingManager)
|
||||||
|
return (ssView as View).apply { addOnAttachStateChangeListener(stateChangeListener) }
|
||||||
this.smartspaceView = ssView
|
|
||||||
this.view = ssView as View
|
|
||||||
|
|
||||||
updateTextColorFromWallpaper()
|
|
||||||
statusBarStateListener.onDozeAmountChanged(0f, statusBarStateController.dozeAmount)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun connectSession() {
|
private fun connectSession() {
|
||||||
@@ -165,8 +166,6 @@ class LockscreenSmartspaceController @Inject constructor(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Disconnects the smartspace view from the smartspace service and cleans up any resources.
|
* 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() {
|
fun disconnect() {
|
||||||
execution.assertIsMainThread()
|
execution.assertIsMainThread()
|
||||||
@@ -231,7 +230,7 @@ class LockscreenSmartspaceController @Inject constructor(
|
|||||||
private val statusBarStateListener = object : StatusBarStateController.StateListener {
|
private val statusBarStateListener = object : StatusBarStateController.StateListener {
|
||||||
override fun onDozeAmountChanged(linear: Float, eased: Float) {
|
override fun onDozeAmountChanged(linear: Float, eased: Float) {
|
||||||
execution.assertIsMainThread()
|
execution.assertIsMainThread()
|
||||||
smartspaceView.setDozeAmount(eased)
|
smartspaceViews.forEach { it.setDozeAmount(eased) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,7 +255,7 @@ class LockscreenSmartspaceController @Inject constructor(
|
|||||||
|
|
||||||
private fun updateTextColorFromWallpaper() {
|
private fun updateTextColorFromWallpaper() {
|
||||||
val wallpaperTextColor = Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColor)
|
val wallpaperTextColor = Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColor)
|
||||||
smartspaceView.setPrimaryTextColor(wallpaperTextColor)
|
smartspaceViews.forEach { it.setPrimaryTextColor(wallpaperTextColor) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun reloadSmartspace() {
|
private fun reloadSmartspace() {
|
||||||
|
|||||||
@@ -51,7 +51,6 @@ import com.android.systemui.util.mockito.capture
|
|||||||
import com.android.systemui.util.mockito.eq
|
import com.android.systemui.util.mockito.eq
|
||||||
import com.android.systemui.util.settings.SecureSettings
|
import com.android.systemui.util.settings.SecureSettings
|
||||||
import com.android.systemui.util.time.FakeSystemClock
|
import com.android.systemui.util.time.FakeSystemClock
|
||||||
import org.junit.Assert.assertEquals
|
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.mockito.ArgumentCaptor
|
import org.mockito.ArgumentCaptor
|
||||||
@@ -114,6 +113,8 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
|
|||||||
private lateinit var configChangeListener: ConfigurationListener
|
private lateinit var configChangeListener: ConfigurationListener
|
||||||
private lateinit var statusBarStateListener: StateListener
|
private lateinit var statusBarStateListener: StateListener
|
||||||
|
|
||||||
|
private lateinit var smartspaceView: SmartspaceView
|
||||||
|
|
||||||
private val clock = FakeSystemClock()
|
private val clock = FakeSystemClock()
|
||||||
private val executor = FakeExecutor(clock)
|
private val executor = FakeExecutor(clock)
|
||||||
private val execution = FakeExecution()
|
private val execution = FakeExecution()
|
||||||
@@ -141,7 +142,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
|
|||||||
`when`(secureSettings.getUriFor(PRIVATE_LOCKSCREEN_SETTING))
|
`when`(secureSettings.getUriFor(PRIVATE_LOCKSCREEN_SETTING))
|
||||||
.thenReturn(fakePrivateLockscreenSettingUri)
|
.thenReturn(fakePrivateLockscreenSettingUri)
|
||||||
`when`(smartspaceManager.createSmartspaceSession(any())).thenReturn(smartspaceSession)
|
`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`(userTracker.userProfiles).thenReturn(userList)
|
||||||
`when`(statusBarStateController.dozeAmount).thenReturn(0.5f)
|
`when`(statusBarStateController.dozeAmount).thenReturn(0.5f)
|
||||||
|
|
||||||
@@ -249,7 +250,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(fakeSmartspaceView).setPrimaryTextColor(anyInt())
|
verify(smartspaceView).setPrimaryTextColor(anyInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -261,7 +262,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(fakeSmartspaceView).setDozeAmount(0.7f)
|
verify(smartspaceView).setDozeAmount(0.7f)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -393,40 +394,30 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testBuildViewIsIdempotent() {
|
fun testMultipleViewsUseSameSession() {
|
||||||
// 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() {
|
|
||||||
// GIVEN a connected session
|
// GIVEN a connected session
|
||||||
connectSession()
|
connectSession()
|
||||||
clearInvocations(smartspaceManager)
|
clearInvocations(smartspaceManager)
|
||||||
clearInvocations(plugin)
|
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)
|
val view = controller.buildAndConnectView(fakeParent)
|
||||||
fakeParent.addView(view)
|
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(smartspaceManager, never()).createSmartspaceSession(any())
|
||||||
verify(plugin, never()).getView(any())
|
verify(smartspaceView2).registerDataProvider(plugin)
|
||||||
assertEquals(fakeSmartspaceView, controller.view)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun connectSession() {
|
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)
|
verify(smartspaceSession)
|
||||||
.addOnTargetsAvailableListener(any(), capture(sessionListenerCaptor))
|
.addOnTargetsAvailableListener(any(), capture(sessionListenerCaptor))
|
||||||
sessionListener = sessionListenerCaptor.value
|
sessionListener = sessionListenerCaptor.value
|
||||||
@@ -450,11 +441,11 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
|
|||||||
verify(smartspaceSession).requestSmartspaceUpdate()
|
verify(smartspaceSession).requestSmartspaceUpdate()
|
||||||
clearInvocations(smartspaceSession)
|
clearInvocations(smartspaceSession)
|
||||||
|
|
||||||
verify(fakeSmartspaceView).setPrimaryTextColor(anyInt())
|
verify(smartspaceView).setPrimaryTextColor(anyInt())
|
||||||
verify(fakeSmartspaceView).setDozeAmount(0.5f)
|
verify(smartspaceView).setDozeAmount(0.5f)
|
||||||
clearInvocations(fakeSmartspaceView)
|
clearInvocations(view)
|
||||||
|
|
||||||
fakeParent.addView(fakeSmartspaceView)
|
fakeParent.addView(view)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setActiveUser(userHandle: UserHandle) {
|
private fun setActiveUser(userHandle: UserHandle) {
|
||||||
@@ -490,7 +481,8 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
|
|||||||
).thenReturn(if (value) 1 else 0)
|
).thenReturn(if (value) 1 else 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val fakeSmartspaceView = spy(object : View(context), SmartspaceView {
|
private fun createSmartspaceView(): SmartspaceView {
|
||||||
|
return spy(object : View(context), SmartspaceView {
|
||||||
override fun registerDataProvider(plugin: BcSmartspaceDataPlugin?) {
|
override fun registerDataProvider(plugin: BcSmartspaceDataPlugin?) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -516,6 +508,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private const val PRIVATE_LOCKSCREEN_SETTING =
|
private const val PRIVATE_LOCKSCREEN_SETTING =
|
||||||
Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS
|
Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS
|
||||||
|
|||||||
Reference in New Issue
Block a user