Merge "Support multiple smartspace views"
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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 =
|
||||
|
||||
Reference in New Issue
Block a user