Loading packages/SystemUI/src/com/android/systemui/flags/Flags.kt +0 −3 Original line number Diff line number Diff line Loading @@ -197,9 +197,6 @@ object Flags { // TODO(b/254512673): Tracking Bug @JvmField val DREAM_MEDIA_TAP_TO_OPEN = unreleasedFlag("dream_media_tap_to_open") // TODO(b/266157412): Tracking Bug val MEDIA_RETAIN_SESSIONS = unreleasedFlag("media_retain_sessions") // 1000 - dock val SIMULATE_DOCK_THROUGH_CHARGING = releasedFlag("simulate_dock_through_charging") Loading packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt +4 −14 Original line number Diff line number Diff line Loading @@ -1171,8 +1171,6 @@ class LegacyMediaDataManagerImpl( logger.logMediaRemoved(removed.appUid, removed.packageName, removed.instanceId) } else if (isAbleToResume(removed)) { convertToResumePlayer(key, removed) } else if (mediaFlags.isRetainingPlayersEnabled()) { handlePossibleRemoval(key, removed, notificationRemoved = true) } else { notifyMediaDataRemoved(key) logger.logMediaRemoved(removed.appUid, removed.packageName, removed.instanceId) Loading @@ -1198,18 +1196,14 @@ class LegacyMediaDataManagerImpl( * if it was removed before becoming inactive. (Assumes that [removed] was removed from * [mediaEntries] before this function was called) */ private fun handlePossibleRemoval( key: String, removed: MediaData, notificationRemoved: Boolean = false, ) { private fun handlePossibleRemoval(key: String, removed: MediaData) { val hasSession = removed.token != null if (hasSession && removed.semanticActions != null) { // The app was using session actions, and the session is still valid: keep player if (DEBUG) Log.d(TAG, "Notification removed but using session actions $key") mediaEntries.put(key, removed) notifyMediaDataLoaded(key, key, removed) } else if (!notificationRemoved && removed.semanticActions == null) { } else if (removed.semanticActions == null) { // The app was using notification actions, and notif wasn't removed yet: keep player if (DEBUG) Log.d(TAG, "Session destroyed but using notification actions $key") mediaEntries.put(key, removed) Loading @@ -1220,14 +1214,10 @@ class LegacyMediaDataManagerImpl( if (DEBUG) Log.d(TAG, "Removing still-active player $key") notifyMediaDataRemoved(key) logger.logMediaRemoved(removed.appUid, removed.packageName, removed.instanceId) } else if (mediaFlags.isRetainingPlayersEnabled() || isAbleToResume(removed)) { } else if (isAbleToResume(removed)) { // Convert to resume if (DEBUG) { Log.d( TAG, "Notification ($notificationRemoved) and/or session " + "($hasSession) gone for inactive player $key", ) Log.d(TAG, "Session ($hasSession) gone for inactive player $key") } convertToResumePlayer(key, removed) } else { Loading packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt +4 −14 Original line number Diff line number Diff line Loading @@ -1126,8 +1126,6 @@ class MediaDataProcessor( logger.logMediaRemoved(removed.appUid, removed.packageName, removed.instanceId) } else if (isAbleToResume(removed)) { convertToResumePlayer(key, removed) } else if (mediaFlags.isRetainingPlayersEnabled()) { handlePossibleRemoval(key, removed, notificationRemoved = true) } else { notifyMediaDataRemoved(key) logger.logMediaRemoved(removed.appUid, removed.packageName, removed.instanceId) Loading @@ -1153,18 +1151,14 @@ class MediaDataProcessor( * if it was removed before becoming inactive. (Assumes that [removed] was removed from * [mediaDataRepository.mediaEntries] state before this function was called) */ private fun handlePossibleRemoval( key: String, removed: MediaData, notificationRemoved: Boolean = false, ) { private fun handlePossibleRemoval(key: String, removed: MediaData) { val hasSession = removed.token != null if (hasSession && removed.semanticActions != null) { // The app was using session actions, and the session is still valid: keep player if (DEBUG) Log.d(TAG, "Notification removed but using session actions $key") mediaDataRepository.addMediaEntry(key, removed) notifyMediaDataLoaded(key, key, removed) } else if (!notificationRemoved && removed.semanticActions == null) { } else if (removed.semanticActions == null) { // The app was using notification actions, and notif wasn't removed yet: keep player if (DEBUG) Log.d(TAG, "Session destroyed but using notification actions $key") mediaDataRepository.addMediaEntry(key, removed) Loading @@ -1175,14 +1169,10 @@ class MediaDataProcessor( if (DEBUG) Log.d(TAG, "Removing still-active player $key") notifyMediaDataRemoved(key) logger.logMediaRemoved(removed.appUid, removed.packageName, removed.instanceId) } else if (mediaFlags.isRetainingPlayersEnabled() || isAbleToResume(removed)) { } else if (isAbleToResume(removed)) { // Convert to resume if (DEBUG) { Log.d( TAG, "Notification ($notificationRemoved) and/or session " + "($hasSession) gone for inactive player $key", ) Log.d(TAG, "Session ($hasSession) gone for inactive player $key") } convertToResumePlayer(key, removed) } else { Loading packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt +0 −7 Original line number Diff line number Diff line Loading @@ -21,7 +21,6 @@ import android.os.UserHandle import com.android.systemui.Flags import com.android.systemui.dagger.SysUISingleton import com.android.systemui.flags.FeatureFlagsClassic import com.android.systemui.flags.Flags as FlagsClassic import javax.inject.Inject @SysUISingleton Loading @@ -39,10 +38,4 @@ class MediaFlags @Inject constructor(private val featureFlags: FeatureFlagsClass val featureFlag = Flags.mediaControlsButtonMedia3() return featureFlag && compatFlag } /** * If true, keep active media controls for the lifetime of the MediaSession, regardless of * whether the underlying notification was dismissed */ fun isRetainingPlayersEnabled() = featureFlags.isEnabled(FlagsClassic.MEDIA_RETAIN_SESSIONS) } packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt +0 −221 Original line number Diff line number Diff line Loading @@ -46,8 +46,6 @@ import com.android.systemui.InstanceIdSequenceFake import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.dump.DumpManager import com.android.systemui.flags.Flags.MEDIA_RETAIN_SESSIONS import com.android.systemui.flags.fakeFeatureFlagsClassic import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.media.controls.domain.resume.MediaResumeListener Loading Loading @@ -156,7 +154,6 @@ class LegacyMediaDataManagerImplTest(flags: FlagsParameterization) : SysuiTestCa private val kosmos = testKosmos().apply { mediaLogger = mockMediaLogger } private val testDispatcher = kosmos.testDispatcher private val testScope = kosmos.testScope private val fakeFeatureFlags = kosmos.fakeFeatureFlagsClassic private val mediaControllerFactory = kosmos.fakeMediaControllerFactory private val instanceIdSequence = InstanceIdSequenceFake(1 shl 20) Loading Loading @@ -246,7 +243,6 @@ class LegacyMediaDataManagerImplTest(flags: FlagsParameterization) : SysuiTestCa // treat mediaSessionBasedFilter as a listener for testing. listener = mediaSessionBasedFilter fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, false) whenever(logger.getNewInstanceId()).thenReturn(instanceIdSequence.newInstanceId()) whenever(keyguardUpdateMonitor.isUserInLockdown(any())).thenReturn(false) } Loading Loading @@ -1657,210 +1653,6 @@ class LegacyMediaDataManagerImplTest(flags: FlagsParameterization) : SysuiTestCa assertThat(mediaDataCaptor.value.isClearable).isFalse() } @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3) @Test fun testRetain_notifPlayer_notifRemoved_setToResume() { fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true) // When a media control based on notification is added, times out, and then removed addNotificationAndLoad() mediaDataManager.setInactive(KEY, timedOut = true) assertThat(mediaDataCaptor.value.active).isFalse() mediaDataManager.onNotificationRemoved(KEY) // It is converted to a resume player verify(listener) .onMediaDataLoaded( eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true), eq(0), eq(false), ) assertThat(mediaDataCaptor.value.resumption).isTrue() assertThat(mediaDataCaptor.value.active).isFalse() verify(logger) .logActiveConvertedToResume( anyInt(), eq(PACKAGE_NAME), eq(mediaDataCaptor.value.instanceId), ) } @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3) @Test fun testRetain_notifPlayer_sessionDestroyed_doesNotChange() { fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true) // When a media control based on notification is added and times out addNotificationAndLoad() mediaDataManager.setInactive(KEY, timedOut = true) assertThat(mediaDataCaptor.value.active).isFalse() // and then the session is destroyed sessionCallbackCaptor.value.invoke(KEY) // It remains as a regular player verify(listener, never()).onMediaDataRemoved(eq(KEY), anyBoolean()) verify(listener, never()) .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean()) } @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3) @Test fun testRetain_notifPlayer_removeWhileActive_fullyRemoved() { fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true) // When a media control based on notification is added and then removed, without timing out addNotificationAndLoad() val data = mediaDataCaptor.value assertThat(data.active).isTrue() mediaDataManager.onNotificationRemoved(KEY) // It is fully removed verify(listener).onMediaDataRemoved(eq(KEY), eq(false)) verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId)) verify(listener, never()) .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean()) } @Test fun testRetain_canResume_removeWhileActive_setToResume() { fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true) // When a media control that supports resumption is added addNotificationAndLoad() val dataResumable = mediaDataCaptor.value.copy(resumeAction = Runnable {}) mediaDataManager.onMediaDataLoaded(KEY, null, dataResumable) // And then removed while still active mediaDataManager.onNotificationRemoved(KEY) // It is converted to a resume player verify(listener) .onMediaDataLoaded( eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true), eq(0), eq(false), ) assertThat(mediaDataCaptor.value.resumption).isTrue() assertThat(mediaDataCaptor.value.active).isFalse() verify(logger) .logActiveConvertedToResume( anyInt(), eq(PACKAGE_NAME), eq(mediaDataCaptor.value.instanceId), ) } @Test fun testRetain_sessionPlayer_notifRemoved_doesNotChange() { fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true) addPlaybackStateAction() // When a media control with PlaybackState actions is added, times out, // and then the notification is removed addNotificationAndLoad() val data = mediaDataCaptor.value assertThat(data.active).isTrue() mediaDataManager.setInactive(KEY, timedOut = true) mediaDataManager.onNotificationRemoved(KEY) // It remains as a regular player verify(listener, never()).onMediaDataRemoved(eq(KEY), anyBoolean()) verify(listener, never()) .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean()) } @Test fun testRetain_sessionPlayer_sessionDestroyed_setToResume() { fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true) addPlaybackStateAction() // When a media control with PlaybackState actions is added, times out, // and then the session is destroyed addNotificationAndLoad() val data = mediaDataCaptor.value assertThat(data.active).isTrue() mediaDataManager.setInactive(KEY, timedOut = true) sessionCallbackCaptor.value.invoke(KEY) // It is converted to a resume player verify(listener) .onMediaDataLoaded( eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true), eq(0), eq(false), ) assertThat(mediaDataCaptor.value.resumption).isTrue() assertThat(mediaDataCaptor.value.active).isFalse() verify(logger) .logActiveConvertedToResume( anyInt(), eq(PACKAGE_NAME), eq(mediaDataCaptor.value.instanceId), ) } @Test fun testRetain_sessionPlayer_destroyedWhileActive_noResume_fullyRemoved() { fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true) addPlaybackStateAction() // When a media control using session actions is added, and then the session is destroyed // without timing out first addNotificationAndLoad() val data = mediaDataCaptor.value assertThat(data.active).isTrue() sessionCallbackCaptor.value.invoke(KEY) // It is fully removed verify(listener).onMediaDataRemoved(eq(KEY), eq(false)) verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId)) verify(listener, never()) .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean()) } @Test fun testRetain_sessionPlayer_canResume_destroyedWhileActive_setToResume() { fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true) addPlaybackStateAction() // When a media control using session actions and that does allow resumption is added, addNotificationAndLoad() val dataResumable = mediaDataCaptor.value.copy(resumeAction = Runnable {}) mediaDataManager.onMediaDataLoaded(KEY, null, dataResumable) // And then the session is destroyed without timing out first sessionCallbackCaptor.value.invoke(KEY) // It is converted to a resume player verify(listener) .onMediaDataLoaded( eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true), eq(0), eq(false), ) assertThat(mediaDataCaptor.value.resumption).isTrue() assertThat(mediaDataCaptor.value.active).isFalse() verify(logger) .logActiveConvertedToResume( anyInt(), eq(PACKAGE_NAME), eq(mediaDataCaptor.value.instanceId), ) } @Test fun testSessionPlayer_sessionDestroyed_noResume_fullyRemoved() { addPlaybackStateAction() Loading Loading @@ -1937,19 +1729,6 @@ class LegacyMediaDataManagerImplTest(flags: FlagsParameterization) : SysuiTestCa ) } @Test fun testSessionDestroyed_noNotificationKey_stillRemoved() { fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true) // When a notiifcation is added and then removed before it is fully processed mediaDataManager.onNotificationAdded(KEY, mediaNotification) backgroundExecutor.runAllReady() mediaDataManager.onNotificationRemoved(KEY) // We still make sure to remove it verify(listener).onMediaDataRemoved(eq(KEY), eq(false)) } @Test fun testResumeMediaLoaded_hasArtPermission_artLoaded() { // When resume media is loaded and user/app has permission to access the art URI, Loading Loading
packages/SystemUI/src/com/android/systemui/flags/Flags.kt +0 −3 Original line number Diff line number Diff line Loading @@ -197,9 +197,6 @@ object Flags { // TODO(b/254512673): Tracking Bug @JvmField val DREAM_MEDIA_TAP_TO_OPEN = unreleasedFlag("dream_media_tap_to_open") // TODO(b/266157412): Tracking Bug val MEDIA_RETAIN_SESSIONS = unreleasedFlag("media_retain_sessions") // 1000 - dock val SIMULATE_DOCK_THROUGH_CHARGING = releasedFlag("simulate_dock_through_charging") Loading
packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt +4 −14 Original line number Diff line number Diff line Loading @@ -1171,8 +1171,6 @@ class LegacyMediaDataManagerImpl( logger.logMediaRemoved(removed.appUid, removed.packageName, removed.instanceId) } else if (isAbleToResume(removed)) { convertToResumePlayer(key, removed) } else if (mediaFlags.isRetainingPlayersEnabled()) { handlePossibleRemoval(key, removed, notificationRemoved = true) } else { notifyMediaDataRemoved(key) logger.logMediaRemoved(removed.appUid, removed.packageName, removed.instanceId) Loading @@ -1198,18 +1196,14 @@ class LegacyMediaDataManagerImpl( * if it was removed before becoming inactive. (Assumes that [removed] was removed from * [mediaEntries] before this function was called) */ private fun handlePossibleRemoval( key: String, removed: MediaData, notificationRemoved: Boolean = false, ) { private fun handlePossibleRemoval(key: String, removed: MediaData) { val hasSession = removed.token != null if (hasSession && removed.semanticActions != null) { // The app was using session actions, and the session is still valid: keep player if (DEBUG) Log.d(TAG, "Notification removed but using session actions $key") mediaEntries.put(key, removed) notifyMediaDataLoaded(key, key, removed) } else if (!notificationRemoved && removed.semanticActions == null) { } else if (removed.semanticActions == null) { // The app was using notification actions, and notif wasn't removed yet: keep player if (DEBUG) Log.d(TAG, "Session destroyed but using notification actions $key") mediaEntries.put(key, removed) Loading @@ -1220,14 +1214,10 @@ class LegacyMediaDataManagerImpl( if (DEBUG) Log.d(TAG, "Removing still-active player $key") notifyMediaDataRemoved(key) logger.logMediaRemoved(removed.appUid, removed.packageName, removed.instanceId) } else if (mediaFlags.isRetainingPlayersEnabled() || isAbleToResume(removed)) { } else if (isAbleToResume(removed)) { // Convert to resume if (DEBUG) { Log.d( TAG, "Notification ($notificationRemoved) and/or session " + "($hasSession) gone for inactive player $key", ) Log.d(TAG, "Session ($hasSession) gone for inactive player $key") } convertToResumePlayer(key, removed) } else { Loading
packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt +4 −14 Original line number Diff line number Diff line Loading @@ -1126,8 +1126,6 @@ class MediaDataProcessor( logger.logMediaRemoved(removed.appUid, removed.packageName, removed.instanceId) } else if (isAbleToResume(removed)) { convertToResumePlayer(key, removed) } else if (mediaFlags.isRetainingPlayersEnabled()) { handlePossibleRemoval(key, removed, notificationRemoved = true) } else { notifyMediaDataRemoved(key) logger.logMediaRemoved(removed.appUid, removed.packageName, removed.instanceId) Loading @@ -1153,18 +1151,14 @@ class MediaDataProcessor( * if it was removed before becoming inactive. (Assumes that [removed] was removed from * [mediaDataRepository.mediaEntries] state before this function was called) */ private fun handlePossibleRemoval( key: String, removed: MediaData, notificationRemoved: Boolean = false, ) { private fun handlePossibleRemoval(key: String, removed: MediaData) { val hasSession = removed.token != null if (hasSession && removed.semanticActions != null) { // The app was using session actions, and the session is still valid: keep player if (DEBUG) Log.d(TAG, "Notification removed but using session actions $key") mediaDataRepository.addMediaEntry(key, removed) notifyMediaDataLoaded(key, key, removed) } else if (!notificationRemoved && removed.semanticActions == null) { } else if (removed.semanticActions == null) { // The app was using notification actions, and notif wasn't removed yet: keep player if (DEBUG) Log.d(TAG, "Session destroyed but using notification actions $key") mediaDataRepository.addMediaEntry(key, removed) Loading @@ -1175,14 +1169,10 @@ class MediaDataProcessor( if (DEBUG) Log.d(TAG, "Removing still-active player $key") notifyMediaDataRemoved(key) logger.logMediaRemoved(removed.appUid, removed.packageName, removed.instanceId) } else if (mediaFlags.isRetainingPlayersEnabled() || isAbleToResume(removed)) { } else if (isAbleToResume(removed)) { // Convert to resume if (DEBUG) { Log.d( TAG, "Notification ($notificationRemoved) and/or session " + "($hasSession) gone for inactive player $key", ) Log.d(TAG, "Session ($hasSession) gone for inactive player $key") } convertToResumePlayer(key, removed) } else { Loading
packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt +0 −7 Original line number Diff line number Diff line Loading @@ -21,7 +21,6 @@ import android.os.UserHandle import com.android.systemui.Flags import com.android.systemui.dagger.SysUISingleton import com.android.systemui.flags.FeatureFlagsClassic import com.android.systemui.flags.Flags as FlagsClassic import javax.inject.Inject @SysUISingleton Loading @@ -39,10 +38,4 @@ class MediaFlags @Inject constructor(private val featureFlags: FeatureFlagsClass val featureFlag = Flags.mediaControlsButtonMedia3() return featureFlag && compatFlag } /** * If true, keep active media controls for the lifetime of the MediaSession, regardless of * whether the underlying notification was dismissed */ fun isRetainingPlayersEnabled() = featureFlags.isEnabled(FlagsClassic.MEDIA_RETAIN_SESSIONS) }
packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt +0 −221 Original line number Diff line number Diff line Loading @@ -46,8 +46,6 @@ import com.android.systemui.InstanceIdSequenceFake import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.dump.DumpManager import com.android.systemui.flags.Flags.MEDIA_RETAIN_SESSIONS import com.android.systemui.flags.fakeFeatureFlagsClassic import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.media.controls.domain.resume.MediaResumeListener Loading Loading @@ -156,7 +154,6 @@ class LegacyMediaDataManagerImplTest(flags: FlagsParameterization) : SysuiTestCa private val kosmos = testKosmos().apply { mediaLogger = mockMediaLogger } private val testDispatcher = kosmos.testDispatcher private val testScope = kosmos.testScope private val fakeFeatureFlags = kosmos.fakeFeatureFlagsClassic private val mediaControllerFactory = kosmos.fakeMediaControllerFactory private val instanceIdSequence = InstanceIdSequenceFake(1 shl 20) Loading Loading @@ -246,7 +243,6 @@ class LegacyMediaDataManagerImplTest(flags: FlagsParameterization) : SysuiTestCa // treat mediaSessionBasedFilter as a listener for testing. listener = mediaSessionBasedFilter fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, false) whenever(logger.getNewInstanceId()).thenReturn(instanceIdSequence.newInstanceId()) whenever(keyguardUpdateMonitor.isUserInLockdown(any())).thenReturn(false) } Loading Loading @@ -1657,210 +1653,6 @@ class LegacyMediaDataManagerImplTest(flags: FlagsParameterization) : SysuiTestCa assertThat(mediaDataCaptor.value.isClearable).isFalse() } @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3) @Test fun testRetain_notifPlayer_notifRemoved_setToResume() { fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true) // When a media control based on notification is added, times out, and then removed addNotificationAndLoad() mediaDataManager.setInactive(KEY, timedOut = true) assertThat(mediaDataCaptor.value.active).isFalse() mediaDataManager.onNotificationRemoved(KEY) // It is converted to a resume player verify(listener) .onMediaDataLoaded( eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true), eq(0), eq(false), ) assertThat(mediaDataCaptor.value.resumption).isTrue() assertThat(mediaDataCaptor.value.active).isFalse() verify(logger) .logActiveConvertedToResume( anyInt(), eq(PACKAGE_NAME), eq(mediaDataCaptor.value.instanceId), ) } @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3) @Test fun testRetain_notifPlayer_sessionDestroyed_doesNotChange() { fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true) // When a media control based on notification is added and times out addNotificationAndLoad() mediaDataManager.setInactive(KEY, timedOut = true) assertThat(mediaDataCaptor.value.active).isFalse() // and then the session is destroyed sessionCallbackCaptor.value.invoke(KEY) // It remains as a regular player verify(listener, never()).onMediaDataRemoved(eq(KEY), anyBoolean()) verify(listener, never()) .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean()) } @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3) @Test fun testRetain_notifPlayer_removeWhileActive_fullyRemoved() { fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true) // When a media control based on notification is added and then removed, without timing out addNotificationAndLoad() val data = mediaDataCaptor.value assertThat(data.active).isTrue() mediaDataManager.onNotificationRemoved(KEY) // It is fully removed verify(listener).onMediaDataRemoved(eq(KEY), eq(false)) verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId)) verify(listener, never()) .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean()) } @Test fun testRetain_canResume_removeWhileActive_setToResume() { fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true) // When a media control that supports resumption is added addNotificationAndLoad() val dataResumable = mediaDataCaptor.value.copy(resumeAction = Runnable {}) mediaDataManager.onMediaDataLoaded(KEY, null, dataResumable) // And then removed while still active mediaDataManager.onNotificationRemoved(KEY) // It is converted to a resume player verify(listener) .onMediaDataLoaded( eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true), eq(0), eq(false), ) assertThat(mediaDataCaptor.value.resumption).isTrue() assertThat(mediaDataCaptor.value.active).isFalse() verify(logger) .logActiveConvertedToResume( anyInt(), eq(PACKAGE_NAME), eq(mediaDataCaptor.value.instanceId), ) } @Test fun testRetain_sessionPlayer_notifRemoved_doesNotChange() { fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true) addPlaybackStateAction() // When a media control with PlaybackState actions is added, times out, // and then the notification is removed addNotificationAndLoad() val data = mediaDataCaptor.value assertThat(data.active).isTrue() mediaDataManager.setInactive(KEY, timedOut = true) mediaDataManager.onNotificationRemoved(KEY) // It remains as a regular player verify(listener, never()).onMediaDataRemoved(eq(KEY), anyBoolean()) verify(listener, never()) .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean()) } @Test fun testRetain_sessionPlayer_sessionDestroyed_setToResume() { fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true) addPlaybackStateAction() // When a media control with PlaybackState actions is added, times out, // and then the session is destroyed addNotificationAndLoad() val data = mediaDataCaptor.value assertThat(data.active).isTrue() mediaDataManager.setInactive(KEY, timedOut = true) sessionCallbackCaptor.value.invoke(KEY) // It is converted to a resume player verify(listener) .onMediaDataLoaded( eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true), eq(0), eq(false), ) assertThat(mediaDataCaptor.value.resumption).isTrue() assertThat(mediaDataCaptor.value.active).isFalse() verify(logger) .logActiveConvertedToResume( anyInt(), eq(PACKAGE_NAME), eq(mediaDataCaptor.value.instanceId), ) } @Test fun testRetain_sessionPlayer_destroyedWhileActive_noResume_fullyRemoved() { fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true) addPlaybackStateAction() // When a media control using session actions is added, and then the session is destroyed // without timing out first addNotificationAndLoad() val data = mediaDataCaptor.value assertThat(data.active).isTrue() sessionCallbackCaptor.value.invoke(KEY) // It is fully removed verify(listener).onMediaDataRemoved(eq(KEY), eq(false)) verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId)) verify(listener, never()) .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean()) } @Test fun testRetain_sessionPlayer_canResume_destroyedWhileActive_setToResume() { fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true) addPlaybackStateAction() // When a media control using session actions and that does allow resumption is added, addNotificationAndLoad() val dataResumable = mediaDataCaptor.value.copy(resumeAction = Runnable {}) mediaDataManager.onMediaDataLoaded(KEY, null, dataResumable) // And then the session is destroyed without timing out first sessionCallbackCaptor.value.invoke(KEY) // It is converted to a resume player verify(listener) .onMediaDataLoaded( eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true), eq(0), eq(false), ) assertThat(mediaDataCaptor.value.resumption).isTrue() assertThat(mediaDataCaptor.value.active).isFalse() verify(logger) .logActiveConvertedToResume( anyInt(), eq(PACKAGE_NAME), eq(mediaDataCaptor.value.instanceId), ) } @Test fun testSessionPlayer_sessionDestroyed_noResume_fullyRemoved() { addPlaybackStateAction() Loading Loading @@ -1937,19 +1729,6 @@ class LegacyMediaDataManagerImplTest(flags: FlagsParameterization) : SysuiTestCa ) } @Test fun testSessionDestroyed_noNotificationKey_stillRemoved() { fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true) // When a notiifcation is added and then removed before it is fully processed mediaDataManager.onNotificationAdded(KEY, mediaNotification) backgroundExecutor.runAllReady() mediaDataManager.onNotificationRemoved(KEY) // We still make sure to remove it verify(listener).onMediaDataRemoved(eq(KEY), eq(false)) } @Test fun testResumeMediaLoaded_hasArtPermission_artLoaded() { // When resume media is loaded and user/app has permission to access the art URI, Loading