summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamSmartspaceController.kt80
-rw-r--r--packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceViewComponent.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt98
4 files changed, 168 insertions, 19 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java
index 9b954f5f0c4a..6808142e98d2 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java
@@ -51,6 +51,7 @@ public interface RegisteredComplicationsModule {
int DREAM_MEDIA_COMPLICATION_WEIGHT = 0;
int DREAM_HOME_CONTROLS_CHIP_COMPLICATION_WEIGHT = 4;
int DREAM_MEDIA_ENTRY_COMPLICATION_WEIGHT = 3;
+ int DREAM_WEATHER_COMPLICATION_WEIGHT = 0;
/**
* Provides layout parameters for the clock time complication.
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamSmartspaceController.kt
index 63f63a5093d2..78e132ff6397 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamSmartspaceController.kt
@@ -30,14 +30,15 @@ import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.BcSmartspaceDataPlugin
import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener
import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceView
+import com.android.systemui.plugins.BcSmartspaceDataPlugin.UI_SURFACE_DREAM
import com.android.systemui.smartspace.SmartspacePrecondition
import com.android.systemui.smartspace.SmartspaceTargetFilter
+import com.android.systemui.smartspace.dagger.SmartspaceModule
import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.DREAM_SMARTSPACE_DATA_PLUGIN
import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.DREAM_SMARTSPACE_PRECONDITION
import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.DREAM_SMARTSPACE_TARGET_FILTER
import com.android.systemui.smartspace.dagger.SmartspaceViewComponent
import com.android.systemui.util.concurrency.Execution
-import java.lang.RuntimeException
import java.util.Optional
import java.util.concurrent.Executor
import javax.inject.Inject
@@ -56,13 +57,16 @@ class DreamSmartspaceController @Inject constructor(
@Named(DREAM_SMARTSPACE_PRECONDITION) private val precondition: SmartspacePrecondition,
@Named(DREAM_SMARTSPACE_TARGET_FILTER)
private val optionalTargetFilter: Optional<SmartspaceTargetFilter>,
- @Named(DREAM_SMARTSPACE_DATA_PLUGIN) optionalPlugin: Optional<BcSmartspaceDataPlugin>
+ @Named(DREAM_SMARTSPACE_DATA_PLUGIN) optionalPlugin: Optional<BcSmartspaceDataPlugin>,
+ @Named(SmartspaceModule.WEATHER_SMARTSPACE_DATA_PLUGIN)
+ optionalWeatherPlugin: Optional<BcSmartspaceDataPlugin>,
) {
companion object {
private const val TAG = "DreamSmartspaceCtrlr"
}
private var session: SmartspaceSession? = null
+ private val weatherPlugin: BcSmartspaceDataPlugin? = optionalWeatherPlugin.orElse(null)
private val plugin: BcSmartspaceDataPlugin? = optionalPlugin.orElse(null)
private var targetFilter: SmartspaceTargetFilter? = optionalTargetFilter.orElse(null)
@@ -116,31 +120,54 @@ class DreamSmartspaceController @Inject constructor(
private val sessionListener = SmartspaceSession.OnTargetsAvailableListener { targets ->
execution.assertIsMainThread()
+ // The weather data plugin takes unfiltered targets and performs the filtering internally.
+ weatherPlugin?.onTargetsAvailable(targets)
+
onTargetsAvailableUnfiltered(targets)
val filteredTargets = targets.filter { targetFilter?.filterSmartspaceTarget(it) ?: true }
plugin?.onTargetsAvailable(filteredTargets)
}
/**
+ * Constructs the weather view with custom layout and connects it to the weather plugin.
+ */
+ fun buildAndConnectWeatherView(parent: ViewGroup, customView: View?): View? {
+ return buildAndConnectViewWithPlugin(parent, weatherPlugin, customView)
+ }
+
+ /**
* Constructs the smartspace view and connects it to the smartspace service.
*/
fun buildAndConnectView(parent: ViewGroup): View? {
+ return buildAndConnectViewWithPlugin(parent, plugin, null)
+ }
+
+ private fun buildAndConnectViewWithPlugin(
+ parent: ViewGroup,
+ smartspaceDataPlugin: BcSmartspaceDataPlugin?,
+ customView: View?
+ ): View? {
execution.assertIsMainThread()
if (!precondition.conditionsMet()) {
throw RuntimeException("Cannot build view when not enabled")
}
- val view = buildView(parent)
+ val view = buildView(parent, smartspaceDataPlugin, customView)
connectSession()
return view
}
- private fun buildView(parent: ViewGroup): View? {
- return if (plugin != null) {
- var view = smartspaceViewComponentFactory.create(parent, plugin, stateChangeListener)
+ private fun buildView(
+ parent: ViewGroup,
+ smartspaceDataPlugin: BcSmartspaceDataPlugin?,
+ customView: View?
+ ): View? {
+ return if (smartspaceDataPlugin != null) {
+ val view = smartspaceViewComponentFactory.create(parent, smartspaceDataPlugin,
+ stateChangeListener, customView)
.getView()
if (view !is View) {
return null
@@ -157,7 +184,10 @@ class DreamSmartspaceController @Inject constructor(
}
private fun connectSession() {
- if (plugin == null || session != null || !hasActiveSessionListeners()) {
+ if (plugin == null && weatherPlugin == null) {
+ return
+ }
+ if (session != null || !hasActiveSessionListeners()) {
return
}
@@ -166,13 +196,14 @@ class DreamSmartspaceController @Inject constructor(
}
val newSession = smartspaceManager.createSmartspaceSession(
- SmartspaceConfig.Builder(context, "dream").build()
+ SmartspaceConfig.Builder(context, UI_SURFACE_DREAM).build()
)
Log.d(TAG, "Starting smartspace session for dream")
newSession.addOnTargetsAvailableListener(uiExecutor, sessionListener)
this.session = newSession
- plugin.registerSmartspaceEventNotifier {
+ weatherPlugin?.registerSmartspaceEventNotifier { e -> session?.notifySmartspaceEvent(e) }
+ plugin?.registerSmartspaceEventNotifier {
e ->
session?.notifySmartspaceEvent(e)
}
@@ -199,22 +230,47 @@ class DreamSmartspaceController @Inject constructor(
session = null
+ weatherPlugin?.registerSmartspaceEventNotifier(null)
+ weatherPlugin?.onTargetsAvailable(emptyList())
+
plugin?.registerSmartspaceEventNotifier(null)
plugin?.onTargetsAvailable(emptyList())
Log.d(TAG, "Ending smartspace session for dream")
}
fun addListener(listener: SmartspaceTargetListener) {
+ addAndRegisterListener(listener, plugin)
+ }
+
+ fun removeListener(listener: SmartspaceTargetListener) {
+ removeAndUnregisterListener(listener, plugin)
+ }
+
+ fun addListenerForWeatherPlugin(listener: SmartspaceTargetListener) {
+ addAndRegisterListener(listener, weatherPlugin)
+ }
+
+ fun removeListenerForWeatherPlugin(listener: SmartspaceTargetListener) {
+ removeAndUnregisterListener(listener, weatherPlugin)
+ }
+
+ private fun addAndRegisterListener(
+ listener: SmartspaceTargetListener,
+ smartspaceDataPlugin: BcSmartspaceDataPlugin?
+ ) {
execution.assertIsMainThread()
- plugin?.registerListener(listener)
+ smartspaceDataPlugin?.registerListener(listener)
listeners.add(listener)
connectSession()
}
- fun removeListener(listener: SmartspaceTargetListener) {
+ private fun removeAndUnregisterListener(
+ listener: SmartspaceTargetListener,
+ smartspaceDataPlugin: BcSmartspaceDataPlugin?
+ ) {
execution.assertIsMainThread()
- plugin?.unregisterListener(listener)
+ smartspaceDataPlugin?.unregisterListener(listener)
listeners.remove(listener)
disconnect()
}
diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceViewComponent.kt b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceViewComponent.kt
index 5736a5cdc05a..26149321946d 100644
--- a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceViewComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceViewComponent.kt
@@ -37,7 +37,8 @@ interface SmartspaceViewComponent {
fun create(
@BindsInstance parent: ViewGroup,
@BindsInstance @Named(PLUGIN) plugin: BcSmartspaceDataPlugin,
- @BindsInstance onAttachListener: View.OnAttachStateChangeListener
+ @BindsInstance onAttachListener: View.OnAttachStateChangeListener,
+ @BindsInstance viewWithCustomLayout: View? = null
): SmartspaceViewComponent
}
@@ -53,10 +54,13 @@ interface SmartspaceViewComponent {
falsingManager: FalsingManager,
parent: ViewGroup,
@Named(PLUGIN) plugin: BcSmartspaceDataPlugin,
+ viewWithCustomLayout: View?,
onAttachListener: View.OnAttachStateChangeListener
):
BcSmartspaceDataPlugin.SmartspaceView {
- val ssView = plugin.getView(parent)
+ val ssView = viewWithCustomLayout
+ as? BcSmartspaceDataPlugin.SmartspaceView
+ ?: plugin.getView(parent)
// Currently, this is only used to provide SmartspaceView on Dream surface.
ssView.setUiSurface(UI_SURFACE_DREAM)
ssView.registerDataProvider(plugin)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
index a280510009a7..58b44ae5bcbd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
@@ -24,6 +24,7 @@ import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
import android.view.ViewGroup
+import android.widget.FrameLayout
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dreams.smartspace.DreamSmartspaceController
@@ -46,6 +47,7 @@ import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
+import org.mockito.Mockito.anyInt
import org.mockito.MockitoAnnotations
import org.mockito.Spy
@@ -69,12 +71,21 @@ class DreamSmartspaceControllerTest : SysuiTestCase() {
private lateinit var viewComponent: SmartspaceViewComponent
@Mock
+ private lateinit var weatherViewComponent: SmartspaceViewComponent
+
+ @Spy
+ private var weatherSmartspaceView: SmartspaceView = TestView(context)
+
+ @Mock
private lateinit var targetFilter: SmartspaceTargetFilter
@Mock
private lateinit var plugin: BcSmartspaceDataPlugin
@Mock
+ private lateinit var weatherPlugin: BcSmartspaceDataPlugin
+
+ @Mock
private lateinit var precondition: SmartspacePrecondition
@Spy
@@ -88,6 +99,9 @@ class DreamSmartspaceControllerTest : SysuiTestCase() {
private lateinit var controller: DreamSmartspaceController
+ // TODO(b/272811280): Remove usage of real view
+ private val fakeParent = FrameLayout(context)
+
/**
* A class which implements SmartspaceView and extends View. This is mocked to provide the right
* object inheritance and interface implementation used in DreamSmartspaceController
@@ -121,13 +135,17 @@ class DreamSmartspaceControllerTest : SysuiTestCase() {
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- `when`(viewComponentFactory.create(any(), eq(plugin), any()))
+ `when`(viewComponentFactory.create(any(), eq(plugin), any(), eq(null)))
.thenReturn(viewComponent)
`when`(viewComponent.getView()).thenReturn(smartspaceView)
+ `when`(viewComponentFactory.create(any(), eq(weatherPlugin), any(), any()))
+ .thenReturn(weatherViewComponent)
+ `when`(weatherViewComponent.getView()).thenReturn(weatherSmartspaceView)
`when`(smartspaceManager.createSmartspaceSession(any())).thenReturn(session)
controller = DreamSmartspaceController(context, smartspaceManager, execution, uiExecutor,
- viewComponentFactory, precondition, Optional.of(targetFilter), Optional.of(plugin))
+ viewComponentFactory, precondition, Optional.of(targetFilter), Optional.of(plugin),
+ Optional.of(weatherPlugin))
}
/**
@@ -168,11 +186,11 @@ class DreamSmartspaceControllerTest : SysuiTestCase() {
`when`(precondition.conditionsMet()).thenReturn(true)
controller.buildAndConnectView(Mockito.mock(ViewGroup::class.java))
- var stateChangeListener = withArgCaptor<View.OnAttachStateChangeListener> {
- verify(viewComponentFactory).create(any(), eq(plugin), capture())
+ val stateChangeListener = withArgCaptor<View.OnAttachStateChangeListener> {
+ verify(viewComponentFactory).create(any(), eq(plugin), capture(), eq(null))
}
- var mockView = Mockito.mock(TestView::class.java)
+ val mockView = Mockito.mock(TestView::class.java)
`when`(precondition.conditionsMet()).thenReturn(true)
stateChangeListener.onViewAttachedToWindow(mockView)
@@ -183,4 +201,74 @@ class DreamSmartspaceControllerTest : SysuiTestCase() {
verify(session).close()
}
+
+ /**
+ * Ensures session is created when weather smartspace view is created and attached.
+ */
+ @Test
+ fun testConnectOnWeatherViewCreate() {
+ `when`(precondition.conditionsMet()).thenReturn(true)
+
+ val customView = Mockito.mock(TestView::class.java)
+ val weatherView = controller.buildAndConnectWeatherView(fakeParent, customView)
+ val weatherSmartspaceView = weatherView as SmartspaceView
+ fakeParent.addView(weatherView)
+
+ // Then weather view is created with custom view and the default weatherPlugin.getView
+ // should not be called
+ verify(viewComponentFactory).create(eq(fakeParent), eq(weatherPlugin), any(),
+ eq(customView))
+ verify(weatherPlugin, Mockito.never()).getView(fakeParent)
+
+ // And then session is created
+ controller.stateChangeListener.onViewAttachedToWindow(weatherView)
+ verify(smartspaceManager).createSmartspaceSession(any())
+ verify(weatherSmartspaceView).setPrimaryTextColor(anyInt())
+ verify(weatherSmartspaceView).setDozeAmount(0f)
+ }
+
+ /**
+ * Ensures weather plugin registers target listener when it is added from the controller.
+ */
+ @Test
+ fun testAddListenerInController_registersListenerForWeatherPlugin() {
+ val customView = Mockito.mock(TestView::class.java)
+ `when`(precondition.conditionsMet()).thenReturn(true)
+
+ // Given a session is created
+ val weatherView = controller.buildAndConnectWeatherView(fakeParent, customView)
+ controller.stateChangeListener.onViewAttachedToWindow(weatherView)
+ verify(smartspaceManager).createSmartspaceSession(any())
+
+ // When a listener is added
+ controller.addListenerForWeatherPlugin(listener)
+
+ // Then the listener is registered to the weather plugin only
+ verify(weatherPlugin).registerListener(listener)
+ verify(plugin, Mockito.never()).registerListener(any())
+ }
+
+ /**
+ * Ensures session is closed and weather plugin unregisters the notifier when weather smartspace
+ * view is detached.
+ */
+ @Test
+ fun testDisconnect_emitsEmptyListAndRemovesNotifier() {
+ `when`(precondition.conditionsMet()).thenReturn(true)
+
+ // Given a session is created
+ val customView = Mockito.mock(TestView::class.java)
+ val weatherView = controller.buildAndConnectWeatherView(fakeParent, customView)
+ controller.stateChangeListener.onViewAttachedToWindow(weatherView)
+ verify(smartspaceManager).createSmartspaceSession(any())
+
+ // When view is detached
+ controller.stateChangeListener.onViewDetachedFromWindow(weatherView)
+ // Then the session is closed
+ verify(session).close()
+
+ // And the listener receives an empty list of targets and unregisters the notifier
+ verify(weatherPlugin).onTargetsAvailable(emptyList())
+ verify(weatherPlugin).registerSmartspaceEventNotifier(null)
+ }
}