summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/Android.bp1
-rw-r--r--packages/SystemUI/AndroidManifest.xml3
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt120
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaHost.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaDataProvider.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt30
11 files changed, 271 insertions, 5 deletions
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 657435326d15..a2359350243a 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -70,6 +70,7 @@ android_library {
"res",
],
static_libs: [
+ "bcsmartspace",
"WindowManager-Shell",
"SystemUIPluginLib",
"SystemUISharedLib",
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 42d7e587b668..0b56f63d4ddb 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -275,6 +275,9 @@
<!-- Permission to make accessibility service access Bubbles -->
<uses-permission android:name="android.permission.ADD_TRUSTED_DISPLAY" />
+ <!-- Permission for Smartspace. -->
+ <uses-permission android:name="android.permission.MANAGE_SMARTSPACE"/>
+
<protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" />
<protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" />
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index a3ff3753cbf9..33681c8d03e5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -1,6 +1,7 @@
package com.android.systemui.media
import android.animation.ArgbEvaluator
+import android.app.smartspace.SmartspaceTarget
import android.content.Context
import android.content.Intent
import android.content.res.ColorStateList
@@ -201,9 +202,18 @@ class MediaCarouselController @Inject constructor(
}
}
+ override fun onSmartspaceMediaDataLoaded(key: String, data: SmartspaceTarget) {
+ Log.d(TAG, "My Smartspace media update is here")
+ addOrUpdateSmartspaceMediaRecommendations(key, data)
+ }
+
override fun onMediaDataRemoved(key: String) {
removePlayer(key)
}
+
+ override fun onSmartspaceMediaDataRemoved(key: String) {
+ Log.d(TAG, "My Smartspace media removal request is received")
+ }
})
mediaFrame.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
// The pageIndicator is not laid out yet when we get the current state update,
@@ -291,6 +301,10 @@ class MediaCarouselController @Inject constructor(
}
}
+ private fun addOrUpdateSmartspaceMediaRecommendations(key: String, data: SmartspaceTarget) {
+ // TODO(b/182813345): Add Smartspace media recommendation view.
+ }
+
private fun removePlayer(key: String, dismissMediaData: Boolean = true) {
val removed = MediaPlayerData.removeMediaPlayer(key)
removed?.apply {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt
index aa3699e9a22b..2c094b885032 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.media
+import android.app.smartspace.SmartspaceTarget
import javax.inject.Inject
/**
@@ -37,10 +38,18 @@ class MediaDataCombineLatest @Inject constructor() : MediaDataManager.Listener,
}
}
+ override fun onSmartspaceMediaDataLoaded(key: String, data: SmartspaceTarget) {
+ listeners.toSet().forEach { it.onSmartspaceMediaDataLoaded(key, data) }
+ }
+
override fun onMediaDataRemoved(key: String) {
remove(key)
}
+ override fun onSmartspaceMediaDataRemoved(key: String) {
+ listeners.toSet().forEach { it.onSmartspaceMediaDataRemoved(key) }
+ }
+
override fun onMediaDeviceChanged(
key: String,
oldKey: String?,
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
index 1f580a953d09..aab27473d3e8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
@@ -16,6 +16,7 @@
package com.android.systemui.media
+import android.app.smartspace.SmartspaceTarget
import android.util.Log
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.broadcast.BroadcastDispatcher
@@ -82,6 +83,10 @@ class MediaDataFilter @Inject constructor(
}
}
+ override fun onSmartspaceMediaDataLoaded(key: String, data: SmartspaceTarget) {
+ listeners.forEach { it.onSmartspaceMediaDataLoaded(key, data) }
+ }
+
override fun onMediaDataRemoved(key: String) {
allEntries.remove(key)
userEntries.remove(key)?.let {
@@ -92,6 +97,10 @@ class MediaDataFilter @Inject constructor(
}
}
+ override fun onSmartspaceMediaDataRemoved(key: String) {
+ listeners.forEach { it.onSmartspaceMediaDataRemoved(key) }
+ }
+
@VisibleForTesting
internal fun handleUserSwitched(id: Int) {
// If the user changes, remove all current MediaData objects and inform listeners
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index 41c9daedf2d4..dfd588d0406e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -18,6 +18,10 @@ package com.android.systemui.media
import android.app.Notification
import android.app.PendingIntent
+import android.app.smartspace.SmartspaceConfig
+import android.app.smartspace.SmartspaceManager
+import android.app.smartspace.SmartspaceSession
+import android.app.smartspace.SmartspaceTarget
import android.content.BroadcastReceiver
import android.content.ContentResolver
import android.content.Context
@@ -33,6 +37,7 @@ import android.media.MediaMetadata
import android.media.session.MediaController
import android.media.session.MediaSession
import android.net.Uri
+import android.os.Parcelable
import android.os.UserHandle
import android.service.notification.StatusBarNotification
import android.text.TextUtils
@@ -45,6 +50,7 @@ import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.BcSmartspaceDataPlugin
import com.android.systemui.statusbar.NotificationMediaManager.isPlayingState
import com.android.systemui.statusbar.notification.row.HybridGroupManager
import com.android.systemui.util.Assert
@@ -54,6 +60,7 @@ import java.io.FileDescriptor
import java.io.IOException
import java.io.PrintWriter
import java.util.concurrent.Executor
+import java.util.concurrent.Executors
import javax.inject.Inject
// URI fields to try loading album art from
@@ -99,9 +106,16 @@ class MediaDataManager(
mediaDataCombineLatest: MediaDataCombineLatest,
private val mediaDataFilter: MediaDataFilter,
private val activityStarter: ActivityStarter,
+ private val smartspaceMediaDataProvider: SmartspaceMediaDataProvider,
private var useMediaResumption: Boolean,
private val useQsMediaPlayer: Boolean
-) : Dumpable {
+) : Dumpable, BcSmartspaceDataPlugin.SmartspaceTargetListener {
+
+ companion object {
+ // UI surface label for subscribing Smartspace updates.
+ @JvmField
+ val SMARTSPACE_UI_SURFACE_LABEL = "media_data_manager"
+ }
private val themeText = com.android.settingslib.Utils.getColorAttr(context,
com.android.internal.R.attr.textColorPrimary).defaultColor
@@ -117,6 +131,8 @@ class MediaDataManager(
// TODO(b/159539991#comment5): Move internal listeners to separate package.
private val internalListeners: MutableSet<Listener> = mutableSetOf()
private val mediaEntries: LinkedHashMap<String, MediaData> = LinkedHashMap()
+ // There should ONLY be at most one Smartspace media recommendation.
+ private var smartspaceMediaTarget: SmartspaceTarget? = null
internal var appsBlockedFromResume: MutableSet<String> = Utils.getBlockedMediaApps(context)
set(value) {
// Update list
@@ -128,6 +144,7 @@ class MediaDataManager(
removeAllForPackage(it)
}
}
+ private var smartspaceSession: SmartspaceSession? = null
@Inject
constructor(
@@ -143,11 +160,13 @@ class MediaDataManager(
mediaDeviceManager: MediaDeviceManager,
mediaDataCombineLatest: MediaDataCombineLatest,
mediaDataFilter: MediaDataFilter,
- activityStarter: ActivityStarter
+ activityStarter: ActivityStarter,
+ smartspaceMediaDataProvider: SmartspaceMediaDataProvider
) : this(context, backgroundExecutor, foregroundExecutor, mediaControllerFactory,
broadcastDispatcher, dumpManager, mediaTimeoutListener, mediaResumeListener,
mediaSessionBasedFilter, mediaDeviceManager, mediaDataCombineLatest, mediaDataFilter,
- activityStarter, Utils.useMediaResumption(context), Utils.useQsMediaPlayer(context))
+ activityStarter, smartspaceMediaDataProvider, Utils.useMediaResumption(context),
+ Utils.useQsMediaPlayer(context))
private val appChangeReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
@@ -200,9 +219,31 @@ class MediaDataManager(
}
// BroadcastDispatcher does not allow filters with data schemes
context.registerReceiver(appChangeReceiver, uninstallFilter)
+
+ // Register for Smartspace data updates.
+ smartspaceMediaDataProvider.registerListener(this)
+ val smartspaceManager: SmartspaceManager =
+ context.getSystemService(SmartspaceManager::class.java)
+ smartspaceSession = smartspaceManager.createSmartspaceSession(
+ SmartspaceConfig.Builder(context, SMARTSPACE_UI_SURFACE_LABEL).build())
+ smartspaceSession?.let {
+ it.registerSmartspaceUpdates(
+ // Use a new thread listening to Smartspace updates instead of using the existing
+ // backgroundExecutor. SmartspaceSession has scheduled routine updates which can be
+ // unpredictable on test simulators, using the backgroundExecutor makes it's hard to
+ // test the threads numbers.
+ // Switch to use backgroundExecutor when SmartspaceSession has a good way to be
+ // mocked.
+ Executors.newCachedThreadPool(),
+ SmartspaceSession.Callback { targets ->
+ smartspaceMediaDataProvider.onTargetsAvailable(targets)
+ })
+ }
+ smartspaceSession?.let { it.requestSmartspaceUpdate() }
}
fun destroy() {
+ smartspaceMediaDataProvider.unregisterListener(this)
context.unregisterReceiver(appChangeReceiver)
}
@@ -309,7 +350,7 @@ class MediaDataManager(
private fun addInternalListener(listener: Listener) = internalListeners.add(listener)
/**
- * Notify internal listeners of loaded event.
+ * Notify internal listeners of media loaded event.
*
* External listeners registered with [addListener] will be notified after the event propagates
* through the internal listener pipeline.
@@ -319,7 +360,17 @@ class MediaDataManager(
}
/**
- * Notify internal listeners of removed event.
+ * Notify internal listeners of Smartspace media loaded event.
+ *
+ * External listeners registered with [addListener] will be notified after the event propagates
+ * through the internal listener pipeline.
+ */
+ private fun notifySmartspaceMediaDataLoaded(key: String, info: SmartspaceTarget) {
+ internalListeners.forEach { it.onSmartspaceMediaDataLoaded(key, info) }
+ }
+
+ /**
+ * Notify internal listeners of media removed event.
*
* External listeners registered with [addListener] will be notified after the event propagates
* through the internal listener pipeline.
@@ -329,6 +380,16 @@ class MediaDataManager(
}
/**
+ * Notify internal listeners of Smartspace media removed event.
+ *
+ * External listeners registered with [addListener] will be notified after the event propagates
+ * through the internal listener pipeline.
+ */
+ private fun notifySmartspaceMediaDataRemoved(key: String) {
+ internalListeners.forEach { it.onSmartspaceMediaDataRemoved(key) }
+ }
+
+ /**
* Called whenever the player has been paused or stopped for a while, or swiped from QQS.
* This will make the player not active anymore, hiding it from QQS and Keyguard.
* @see MediaData.active
@@ -601,6 +662,49 @@ class MediaDataManager(
}
}
+ override fun onSmartspaceTargetsUpdated(targets: List<Parcelable>) {
+ Log.d(TAG, "My Smartspace media updates are here")
+ val mediaTargets = targets.filterIsInstance<SmartspaceTarget>()
+ when (mediaTargets.size) {
+ 0 -> {
+ Log.d(TAG, "Empty Smartspace media target")
+ smartspaceMediaTarget?.let {
+ notifySmartspaceMediaDataRemoved(it.smartspaceTargetId)
+ }
+ smartspaceMediaTarget = null
+ }
+ 1 -> {
+ // TODO(b/182811956): Reactivate the resumable media sessions whose last active
+ // time is within 3 hours.
+ // TODO(b/182813365): Wire this up with MediaTimeoutListener so the session can be
+ // expired after 30 seconds.
+ val newMediaTarget = mediaTargets.get(0)
+ if (smartspaceMediaTarget != null &&
+ smartspaceMediaTarget!!.smartspaceTargetId ==
+ newMediaTarget.smartspaceTargetId) {
+ // The same Smartspace updates can be received. Only send the first one.
+ Log.d(TAG, "Same Smartspace media update exists. Skip loading data.")
+ } else {
+ smartspaceMediaTarget?.let {
+ notifySmartspaceMediaDataRemoved(it.smartspaceTargetId)
+ }
+ notifySmartspaceMediaDataLoaded(
+ newMediaTarget.smartspaceTargetId, newMediaTarget)
+ smartspaceMediaTarget = newMediaTarget
+ }
+ }
+ else -> {
+ // There should NOT be more than 1 Smartspace media update. When it happens, it
+ // indicates a bad state or an error. Reset the status accordingly.
+ Log.wtf(TAG, "More than 1 Smartspace Media Update. Resetting the status...")
+ smartspaceMediaTarget?.let {
+ notifySmartspaceMediaDataRemoved(it.smartspaceTargetId)
+ }
+ smartspaceMediaTarget = null
+ }
+ }
+ }
+
fun onNotificationRemoved(key: String) {
Assert.isMainThread()
val removed = mediaEntries.remove(key)
@@ -685,10 +789,16 @@ class MediaDataManager(
*/
fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) {}
+ /** Called whenever there's new Smartspace media data loaded. */
+ fun onSmartspaceMediaDataLoaded(key: String, data: SmartspaceTarget) {}
+
/**
* Called whenever a previously existing Media notification was removed
*/
fun onMediaDataRemoved(key: String) {}
+
+ /** Called whenever a previously existing Smartspace media data was removed. */
+ fun onSmartspaceMediaDataRemoved(key: String) {}
}
override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
index b74ca283f16a..8c12a305992e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
@@ -1,5 +1,6 @@
package com.android.systemui.media
+import android.app.smartspace.SmartspaceTarget
import android.graphics.Rect
import android.util.ArraySet
import android.view.View
@@ -53,9 +54,17 @@ class MediaHost constructor(
updateViewVisibility()
}
+ override fun onSmartspaceMediaDataLoaded(key: String, data: SmartspaceTarget) {
+ updateViewVisibility()
+ }
+
override fun onMediaDataRemoved(key: String) {
updateViewVisibility()
}
+
+ override fun onSmartspaceMediaDataRemoved(key: String) {
+ updateViewVisibility()
+ }
}
fun addVisibilityChangeListener(listener: (Boolean) -> Unit) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt
index f695622b943a..d973478625de 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt
@@ -16,6 +16,7 @@
package com.android.systemui.media
+import android.app.smartspace.SmartspaceTarget
import android.content.ComponentName
import android.content.Context
import android.media.session.MediaController
@@ -134,6 +135,12 @@ class MediaSessionBasedFilter @Inject constructor(
}
}
+ override fun onSmartspaceMediaDataLoaded(key: String, data: SmartspaceTarget) {
+ backgroundExecutor.execute {
+ dispatchSmartspaceMediaDataLoaded(key, data)
+ }
+ }
+
override fun onMediaDataRemoved(key: String) {
// Queue on background thread to ensure ordering of loaded and removed events is maintained.
backgroundExecutor.execute {
@@ -142,6 +149,12 @@ class MediaSessionBasedFilter @Inject constructor(
}
}
+ override fun onSmartspaceMediaDataRemoved(key: String) {
+ backgroundExecutor.execute {
+ dispatchSmartspaceMediaDataRemoved(key)
+ }
+ }
+
private fun dispatchMediaDataLoaded(key: String, oldKey: String?, info: MediaData) {
foregroundExecutor.execute {
listeners.toSet().forEach { it.onMediaDataLoaded(key, oldKey, info) }
@@ -154,6 +167,18 @@ class MediaSessionBasedFilter @Inject constructor(
}
}
+ private fun dispatchSmartspaceMediaDataLoaded(key: String, info: SmartspaceTarget) {
+ foregroundExecutor.execute {
+ listeners.toSet().forEach { it.onSmartspaceMediaDataLoaded(key, info) }
+ }
+ }
+
+ private fun dispatchSmartspaceMediaDataRemoved(key: String) {
+ foregroundExecutor.execute {
+ listeners.toSet().forEach { it.onSmartspaceMediaDataRemoved(key) }
+ }
+ }
+
private fun handleControllersChanged(controllers: List<MediaController>) {
packageControllers.clear()
controllers.forEach {
diff --git a/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaDataProvider.kt b/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaDataProvider.kt
new file mode 100644
index 000000000000..0eab572f315f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaDataProvider.kt
@@ -0,0 +1,39 @@
+package com.android.systemui.media
+
+import android.app.smartspace.SmartspaceTarget
+import com.android.systemui.plugins.BcSmartspaceDataPlugin
+import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener
+import javax.inject.Inject
+
+/** Provides SmartspaceTargets of media types for SystemUI media control. */
+class SmartspaceMediaDataProvider @Inject constructor() : BcSmartspaceDataPlugin {
+
+ private val smartspaceMediaTargetListeners: MutableList<SmartspaceTargetListener> =
+ mutableListOf()
+ private var smartspaceMediaTargets: List<SmartspaceTarget> = listOf()
+
+ override fun registerListener(smartspaceTargetListener: SmartspaceTargetListener) {
+ smartspaceMediaTargetListeners.add(smartspaceTargetListener)
+ }
+
+ override fun unregisterListener(smartspaceTargetListener: SmartspaceTargetListener?) {
+ smartspaceMediaTargetListeners.remove(smartspaceTargetListener)
+ }
+
+ /** Updates Smartspace data and propagates it to any listeners. */
+ fun onTargetsAvailable(targets: List<SmartspaceTarget>) {
+ // Filter out non-media targets.
+ val mediaTargets = mutableListOf<SmartspaceTarget>()
+ for (target in targets) {
+ val smartspaceTarget = target
+ if (smartspaceTarget.featureType == SmartspaceTarget.FEATURE_MEDIA) {
+ mediaTargets.add(smartspaceTarget)
+ }
+ }
+
+ smartspaceMediaTargets = mediaTargets
+ smartspaceMediaTargetListeners.forEach {
+ it.onSmartspaceTargetsUpdated(smartspaceMediaTargets)
+ }
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index c565a271bdd7..5782d38a9281 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -24,6 +24,7 @@ import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Notification;
+import android.app.smartspace.SmartspaceTarget;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
@@ -248,6 +249,11 @@ public class NotificationMediaManager implements Dumpable {
}
@Override
+ public void onSmartspaceMediaDataLoaded(@NonNull String key,
+ @NonNull SmartspaceTarget data) {
+ }
+
+ @Override
public void onMediaDataRemoved(@NonNull String key) {
mNotifPipeline.getAllNotifs()
.stream()
@@ -260,6 +266,9 @@ public class NotificationMediaManager implements Dumpable {
getDismissedByUserStats(entry));
});
}
+
+ @Override
+ public void onSmartspaceMediaDataRemoved(@NonNull String key) {}
});
}
@@ -313,6 +322,11 @@ public class NotificationMediaManager implements Dumpable {
}
@Override
+ public void onSmartspaceMediaDataLoaded(@NonNull String key,
+ @NonNull SmartspaceTarget data) {
+ }
+
+ @Override
public void onMediaDataRemoved(@NonNull String key) {
NotificationEntry entry = mEntryManager.getPendingOrActiveNotif(key);
if (entry != null) {
@@ -323,6 +337,9 @@ public class NotificationMediaManager implements Dumpable {
NotificationListenerService.REASON_CANCEL);
}
}
+
+ @Override
+ public void onSmartspaceMediaDataRemoved(@NonNull String key) {}
});
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
index e88c72860ed8..96eb4b096931 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -2,6 +2,7 @@ package com.android.systemui.media
import android.app.Notification.MediaStyle
import android.app.PendingIntent
+import android.app.smartspace.SmartspaceTarget
import android.graphics.Bitmap
import android.media.MediaDescription
import android.media.MediaMetadata
@@ -39,6 +40,8 @@ import org.mockito.Mockito.`when` as whenever
private const val KEY = "KEY"
private const val KEY_2 = "KEY_2"
+private const val KEY_MEDIA_SMARTSPACE = "MEDIA_SMARTSPACE_ID"
+private const val KEY_NONE_MEDIA_SMARTSPACE = "NONE_MEDIA_SMARTSPACE_ID"
private const val PACKAGE_NAME = "com.android.systemui"
private const val APP_NAME = "SystemUI"
private const val SESSION_ARTIST = "artist"
@@ -72,6 +75,8 @@ class MediaDataManagerTest : SysuiTestCase() {
@Mock lateinit var listener: MediaDataManager.Listener
@Mock lateinit var pendingIntent: PendingIntent
@Mock lateinit var activityStarter: ActivityStarter
+ lateinit var smartspaceMediaDataProvider: SmartspaceMediaDataProvider
+ @Mock lateinit var mediaSmartspaceTarget: SmartspaceTarget
lateinit var mediaDataManager: MediaDataManager
lateinit var mediaNotification: StatusBarNotification
@Captor lateinit var mediaDataCaptor: ArgumentCaptor<MediaData>
@@ -80,6 +85,7 @@ class MediaDataManagerTest : SysuiTestCase() {
fun setup() {
foregroundExecutor = FakeExecutor(FakeSystemClock())
backgroundExecutor = FakeExecutor(FakeSystemClock())
+ smartspaceMediaDataProvider = SmartspaceMediaDataProvider()
mediaDataManager = MediaDataManager(
context = context,
backgroundExecutor = backgroundExecutor,
@@ -94,6 +100,7 @@ class MediaDataManagerTest : SysuiTestCase() {
mediaDataCombineLatest = mediaDataCombineLatest,
mediaDataFilter = mediaDataFilter,
activityStarter = activityStarter,
+ smartspaceMediaDataProvider = smartspaceMediaDataProvider,
useMediaResumption = true,
useQsMediaPlayer = true
)
@@ -117,6 +124,9 @@ class MediaDataManagerTest : SysuiTestCase() {
// mock, it doesn't pass those events along the chain to the external listeners. So, just
// treat mediaSessionBasedFilter as a listener for testing.
listener = mediaSessionBasedFilter
+
+ whenever(mediaSmartspaceTarget.smartspaceTargetId).thenReturn(KEY_MEDIA_SMARTSPACE)
+ whenever(mediaSmartspaceTarget.featureType).thenReturn(SmartspaceTarget.FEATURE_MEDIA)
}
@After
@@ -320,4 +330,24 @@ class MediaDataManagerTest : SysuiTestCase() {
assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor))
}
+
+ @Test
+ fun testOnSmartspaceMediaDataLoaded_hasNewMediaTarget_callsListener() {
+ smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget))
+ verify(listener).onSmartspaceMediaDataLoaded(
+ eq(KEY_MEDIA_SMARTSPACE), eq(mediaSmartspaceTarget))
+ }
+
+ @Test
+ fun testOnSmartspaceMediaDataLoaded_hasNoneMediaTarget_notCallsListener() {
+ smartspaceMediaDataProvider.onTargetsAvailable(listOf())
+ verify(listener, never()).onSmartspaceMediaDataLoaded(anyObject(), anyObject())
+ }
+
+ @Test
+ fun testOnSmartspaceMediaDataLoaded_hasNoneMediaTarget_callsRemoveListener() {
+ smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget))
+ smartspaceMediaDataProvider.onTargetsAvailable(listOf())
+ verify(listener).onSmartspaceMediaDataRemoved(KEY_MEDIA_SMARTSPACE)
+ }
}