diff options
3 files changed, 70 insertions, 3 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaData.kt b/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaData.kt index dc7a4f18adbc..0b57175defe7 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaData.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaData.kt @@ -88,6 +88,8 @@ data class SmartspaceMediaData( } } +/** Key to indicate whether this card should be used to re-show recent media */ +const val EXTRA_KEY_TRIGGER_RESUME = "SHOULD_TRIGGER_RESUME" /** Key for extras [SmartspaceMediaData.cardAction] indicating why the card was sent */ const val EXTRA_KEY_TRIGGER_SOURCE = "MEDIA_RECOMMENDATION_TRIGGER_SOURCE" /** Value for [EXTRA_KEY_TRIGGER_SOURCE] when the card is sent on headphone connection */ diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataFilter.kt index 27f7b9736807..97717a64ce26 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataFilter.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataFilter.kt @@ -23,6 +23,7 @@ import com.android.internal.annotations.VisibleForTesting import com.android.systemui.broadcast.BroadcastSender import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.media.controls.models.player.MediaData +import com.android.systemui.media.controls.models.recommendation.EXTRA_KEY_TRIGGER_RESUME import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData import com.android.systemui.media.controls.util.MediaFlags import com.android.systemui.media.controls.util.MediaUiEventLogger @@ -138,14 +139,23 @@ constructor( val sorted = userEntries.toSortedMap(compareBy { userEntries.get(it)?.lastActive ?: -1 }) val timeSinceActive = timeSinceActiveForMostRecentMedia(sorted) var smartspaceMaxAgeMillis = SMARTSPACE_MAX_AGE - data.cardAction?.let { - val smartspaceMaxAgeSeconds = it.extras.getLong(RESUMABLE_MEDIA_MAX_AGE_SECONDS_KEY, 0) + data.cardAction?.extras?.let { + val smartspaceMaxAgeSeconds = it.getLong(RESUMABLE_MEDIA_MAX_AGE_SECONDS_KEY, 0) if (smartspaceMaxAgeSeconds > 0) { smartspaceMaxAgeMillis = TimeUnit.SECONDS.toMillis(smartspaceMaxAgeSeconds) } } - val shouldReactivate = !hasActiveMedia() && hasAnyMedia() && data.isActive + // Check if smartspace has explicitly specified whether to re-activate resumable media. + // The default behavior is to trigger if the smartspace data is active. + val shouldTriggerResume = + if (data.cardAction?.extras?.containsKey(EXTRA_KEY_TRIGGER_RESUME) == true) { + data.cardAction.extras.getBoolean(EXTRA_KEY_TRIGGER_RESUME, true) + } else { + true + } + val shouldReactivate = + shouldTriggerResume && !hasActiveMedia() && hasAnyMedia() && data.isActive if (timeSinceActive < smartspaceMaxAgeMillis) { // It could happen there are existing active media resume cards, then we don't need to diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataFilterTest.kt index eb6235ca8a6a..8532ffed85fb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataFilterTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataFilterTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.media.controls.pipeline import android.app.smartspace.SmartspaceAction +import android.os.Bundle import android.testing.AndroidTestingRunner import android.testing.TestableLooper import androidx.test.filters.SmallTest @@ -25,6 +26,7 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.BroadcastSender import com.android.systemui.media.controls.MediaTestUtils import com.android.systemui.media.controls.models.player.MediaData +import com.android.systemui.media.controls.models.recommendation.EXTRA_KEY_TRIGGER_RESUME import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData import com.android.systemui.media.controls.ui.MediaPlayerData import com.android.systemui.media.controls.util.MediaFlags @@ -75,6 +77,7 @@ class MediaDataFilterTest : SysuiTestCase() { @Mock private lateinit var smartspaceMediaRecommendationItem: SmartspaceAction @Mock private lateinit var logger: MediaUiEventLogger @Mock private lateinit var mediaFlags: MediaFlags + @Mock private lateinit var cardAction: SmartspaceAction private lateinit var mediaDataFilter: MediaDataFilter private lateinit var dataMain: MediaData @@ -122,6 +125,7 @@ class MediaDataFilterTest : SysuiTestCase() { whenever(smartspaceData.headphoneConnectionTimeMillis) .thenReturn(clock.currentTimeMillis() - 100) whenever(smartspaceData.instanceId).thenReturn(SMARTSPACE_INSTANCE_ID) + whenever(smartspaceData.cardAction).thenReturn(cardAction) } private fun setUser(id: Int) { @@ -574,4 +578,55 @@ class MediaDataFilterTest : SysuiTestCase() { verify(mediaDataManager, never()) .dismissSmartspaceRecommendation(eq(SMARTSPACE_KEY), anyLong()) } + + @Test + fun testSmartspaceLoaded_shouldTriggerResume_doesTrigger() { + // WHEN we have media that was recently played, but not currently active + val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime()) + mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) + verify(listener) + .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false)) + + // AND we get a smartspace signal with extra to trigger resume + val extras = Bundle().apply { putBoolean(EXTRA_KEY_TRIGGER_RESUME, true) } + whenever(cardAction.extras).thenReturn(extras) + mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) + + // THEN we should tell listeners to treat the media as active instead + val dataCurrentAndActive = dataCurrent.copy(active = true) + verify(listener) + .onMediaDataLoaded( + eq(KEY), + eq(KEY), + eq(dataCurrentAndActive), + eq(true), + eq(100), + eq(true) + ) + assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isTrue() + // And send the smartspace data, but not prioritized + verify(listener) + .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false)) + } + + @Test + fun testSmartspaceLoaded_notShouldTriggerResume_doesNotTrigger() { + // WHEN we have media that was recently played, but not currently active + val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime()) + mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) + verify(listener) + .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false)) + + // AND we get a smartspace signal with extra to not trigger resume + val extras = Bundle().apply { putBoolean(EXTRA_KEY_TRIGGER_RESUME, false) } + whenever(cardAction.extras).thenReturn(extras) + mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) + + // THEN listeners are not updated to show media + verify(listener, never()) + .onMediaDataLoaded(eq(KEY), eq(KEY), any(), eq(true), eq(100), eq(true)) + // But the smartspace update is still propagated + verify(listener) + .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false)) + } } |