Merge "Support multiple smartspace views"

This commit is contained in:
Matt Pietal
2021-09-29 12:53:04 +00:00
committed by Android (Google) Code Review
3 changed files with 63 additions and 81 deletions

View File

@@ -239,16 +239,6 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
mView.setClockPlugin(null, mStatusBarStateController.getState());
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;
}
}
/**

View File

@@ -74,15 +74,27 @@ class LockscreenSmartspaceController @Inject constructor(
) {
private var session: SmartspaceSession? = null
private val plugin: BcSmartspaceDataPlugin? = optionalPlugin.orElse(null)
private lateinit var smartspaceView: SmartspaceView
lateinit var view: View
private set
// Smartspace can be used on multiple displays, such as when the user casts their screen
private var smartspaceViews = mutableSetOf<SmartspaceView>()
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() {

View File

@@ -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 =