Loading packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelTest.kt +70 −24 Original line number Diff line number Diff line Loading @@ -24,26 +24,25 @@ import android.media.session.MediaSession import android.media.session.PlaybackState import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.logging.InstanceId import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testScope import com.android.systemui.media.controls.domain.pipeline.MediaDataFilterImpl import com.android.systemui.media.controls.domain.pipeline.mediaDataFilter import com.android.systemui.media.controls.shared.model.MediaData import com.android.systemui.media.controls.shared.model.MediaDeviceData import com.android.systemui.media.controls.util.mediaInstanceId import com.android.systemui.statusbar.notificationLockscreenUserManager import com.android.systemui.testKosmos import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers import org.mockito.ArgumentMatchers.any import org.mockito.Mockito import org.mockito.kotlin.eq import org.mockito.kotlin.whenever @SmallTest @RunWith(AndroidJUnit4::class) Loading @@ -52,17 +51,15 @@ class MediaControlViewModelTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope private val mediaDataFilter: MediaDataFilterImpl = kosmos.mediaDataFilter private val mediaDataFilter = kosmos.mediaDataFilter private val notificationLockscreenUserManager = kosmos.notificationLockscreenUserManager private val packageManager = kosmos.packageManager private val drawable = context.getDrawable(R.drawable.ic_media_play) private val instanceId: InstanceId = kosmos.mediaInstanceId private val instanceId = kosmos.mediaInstanceId private val underTest: MediaControlViewModel = kosmos.mediaControlViewModel @Test fun addMediaControl_mediaControlViewModelIsLoaded() = testScope.runTest { @Before fun setUp() { whenever(packageManager.getApplicationIcon(Mockito.anyString())).thenReturn(drawable) whenever(packageManager.getApplicationIcon(any(ApplicationInfo::class.java))) .thenReturn(drawable) Loading @@ -71,11 +68,14 @@ class MediaControlViewModelTest : SysuiTestCase() { whenever(packageManager.getApplicationLabel(any())).thenReturn(PACKAGE_NAME) whenever(notificationLockscreenUserManager.isCurrentProfile(USER_ID)).thenReturn(true) whenever(notificationLockscreenUserManager.isProfileAvailable(USER_ID)).thenReturn(true) val playerModel by collectLastValue(underTest.player) context.setMockPackageManager(packageManager) } val mediaData = initMediaData() @Test fun addMediaControl_mediaControlViewModelIsLoaded() = testScope.runTest { val playerModel by collectLastValue(underTest.player) val mediaData = initMediaData(ARTIST, TITLE) mediaDataFilter.onMediaDataLoaded(KEY, KEY, mediaData) Loading @@ -88,7 +88,51 @@ class MediaControlViewModelTest : SysuiTestCase() { assertThat(playerModel?.playTurbulenceNoise).isFalse() } private fun initMediaData(): MediaData { @Test fun emitDuplicateMediaControls_mediaControlIsNotBound() = testScope.runTest { val playerModel by collectLastValue(underTest.player) val mediaData = initMediaData(ARTIST, TITLE) mediaDataFilter.onMediaDataLoaded(KEY, KEY, mediaData) assertThat(playerModel).isNotNull() assertThat(playerModel?.titleName).isEqualTo(TITLE) assertThat(playerModel?.artistName).isEqualTo(ARTIST) assertThat(underTest.isNewPlayer(playerModel!!)).isTrue() mediaDataFilter.onMediaDataLoaded(KEY, KEY, mediaData) assertThat(playerModel).isNotNull() assertThat(playerModel?.titleName).isEqualTo(TITLE) assertThat(playerModel?.artistName).isEqualTo(ARTIST) assertThat(underTest.isNewPlayer(playerModel!!)).isFalse() } @Test fun emitDifferentMediaControls_mediaControlIsBound() = testScope.runTest { val playerModel by collectLastValue(underTest.player) var mediaData = initMediaData(ARTIST, TITLE) mediaDataFilter.onMediaDataLoaded(KEY, KEY, mediaData) assertThat(playerModel).isNotNull() assertThat(playerModel?.titleName).isEqualTo(TITLE) assertThat(playerModel?.artistName).isEqualTo(ARTIST) assertThat(underTest.isNewPlayer(playerModel!!)).isTrue() mediaData = initMediaData(ARTIST_2, TITLE_2) mediaDataFilter.onMediaDataLoaded(KEY, KEY, mediaData) assertThat(playerModel).isNotNull() assertThat(playerModel?.titleName).isEqualTo(TITLE_2) assertThat(playerModel?.artistName).isEqualTo(ARTIST_2) assertThat(underTest.isNewPlayer(playerModel!!)).isTrue() } private fun initMediaData(artist: String, title: String): MediaData { val device = MediaDeviceData(true, null, DEVICE_NAME, null, showBroadcastButton = true) // Create media session Loading @@ -111,12 +155,12 @@ class MediaControlViewModelTest : SysuiTestCase() { return MediaData( userId = USER_ID, artist = ARTIST, song = TITLE, artist = artist, song = title, packageName = PACKAGE, token = session.sessionToken, device = device, instanceId = instanceId instanceId = instanceId, ) } Loading @@ -127,6 +171,8 @@ class MediaControlViewModelTest : SysuiTestCase() { private const val PACKAGE = "PKG" private const val ARTIST = "ARTIST" private const val TITLE = "TITLE" private const val ARTIST_2 = "ARTIST_2" private const val TITLE_2 = "TITLE_2" private const val DEVICE_NAME = "DEVICE_NAME" private const val SESSION_KEY = "SESSION_KEY" private const val SESSION_ARTIST = "SESSION_ARTIST" Loading packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt +14 −8 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ import com.android.systemui.bluetooth.BroadcastDialogController import com.android.systemui.media.controls.data.repository.MediaFilterRepository import com.android.systemui.media.controls.domain.pipeline.MediaDataProcessor import com.android.systemui.media.controls.domain.pipeline.getNotificationActions import com.android.systemui.media.controls.shared.MediaLogger import com.android.systemui.media.controls.shared.model.MediaControlModel import com.android.systemui.media.controls.shared.model.MediaData import com.android.systemui.media.controls.util.MediaSmartspaceLogger Loading @@ -59,6 +60,7 @@ constructor( private val lockscreenUserManager: NotificationLockscreenUserManager, private val mediaOutputDialogManager: MediaOutputDialogManager, private val broadcastDialogController: BroadcastDialogController, private val mediaLogger: MediaLogger, ) { val mediaControl: Flow<MediaControlModel?> = Loading @@ -73,7 +75,7 @@ constructor( instanceId: InstanceId, delayMs: Long, eventId: Int, location: Int location: Int, ): Boolean { logSmartspaceUserEvent(eventId, location) val dismissed = Loading @@ -81,7 +83,7 @@ constructor( if (!dismissed) { Log.w( TAG, "Manager failed to dismiss media of instanceId=$instanceId, Token uid=${token?.uid}" "Manager failed to dismiss media of instanceId=$instanceId, Token uid=${token?.uid}", ) } return dismissed Loading Loading @@ -120,13 +122,13 @@ constructor( expandable: Expandable, clickIntent: PendingIntent, eventId: Int, location: Int location: Int, ) { logSmartspaceUserEvent(eventId, location) if (!launchOverLockscreen(clickIntent)) { activityStarter.postStartActivityDismissingKeyguard( clickIntent, expandable.activityTransitionController(Cuj.CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER) expandable.activityTransitionController(Cuj.CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER), ) } } Loading @@ -146,7 +148,7 @@ constructor( keyguardStateController.isShowing && activityIntentHelper.wouldPendingShowOverLockscreen( pendingIntent, lockscreenUserManager.currentUserId lockscreenUserManager.currentUserId, ) if (showOverLockscreen) { try { Loading @@ -166,7 +168,7 @@ constructor( fun startMediaOutputDialog( expandable: Expandable, packageName: String, token: MediaSession.Token? = null token: MediaSession.Token? = null, ) { mediaOutputDialogManager.createAndShowWithController( packageName, Loading @@ -180,7 +182,7 @@ constructor( broadcastDialogController.createBroadcastDialogWithController( broadcastApp, packageName, expandable.dialogTransitionController() expandable.dialogTransitionController(), ) } Loading @@ -188,10 +190,14 @@ constructor( repository.logSmartspaceCardUserEvent( eventId, MediaSmartspaceLogger.getSurface(location), instanceId = instanceId instanceId = instanceId, ) } fun logMediaControlIsBound(artistName: CharSequence, songName: CharSequence) { mediaLogger.logMediaControlIsBound(instanceId, artistName, songName) } private fun Expandable.dialogController(): DialogTransitionAnimator.Controller? { return dialogTransitionController( cuj = Loading packages/SystemUI/src/com/android/systemui/media/controls/shared/MediaLogger.kt +26 −9 Original line number Diff line number Diff line Loading @@ -36,7 +36,7 @@ class MediaLogger @Inject constructor(@MediaLog private val buffer: LogBuffer) { bool1 = active str2 = reason }, { "add media $str1, active: $bool1, reason: $str2" } { "add media $str1, active: $bool1, reason: $str2" }, ) } Loading @@ -48,7 +48,7 @@ class MediaLogger @Inject constructor(@MediaLog private val buffer: LogBuffer) { str1 = instanceId.toString() str2 = reason }, { "removing media $str1, reason: $str2" } { "removing media $str1, reason: $str2" }, ) } Loading @@ -61,7 +61,7 @@ class MediaLogger @Inject constructor(@MediaLog private val buffer: LogBuffer) { bool1 = isActive str2 = reason }, { "add recommendation $str1, active $bool1, reason: $str2" } { "add recommendation $str1, active $bool1, reason: $str2" }, ) } Loading @@ -74,7 +74,7 @@ class MediaLogger @Inject constructor(@MediaLog private val buffer: LogBuffer) { bool1 = immediately str2 = reason }, { "removing recommendation $str1, immediate=$bool1, reason: $str2" } { "removing recommendation $str1, immediate=$bool1, reason: $str2" }, ) } Loading @@ -83,7 +83,7 @@ class MediaLogger @Inject constructor(@MediaLog private val buffer: LogBuffer) { TAG, LogLevel.DEBUG, { str1 = instanceId.toString() }, { "adding media card $str1 to carousel" } { "adding media card $str1 to carousel" }, ) } Loading @@ -92,7 +92,7 @@ class MediaLogger @Inject constructor(@MediaLog private val buffer: LogBuffer) { TAG, LogLevel.DEBUG, { str1 = instanceId.toString() }, { "removing media card $str1 from carousel" } { "removing media card $str1 from carousel" }, ) } Loading @@ -101,7 +101,7 @@ class MediaLogger @Inject constructor(@MediaLog private val buffer: LogBuffer) { TAG, LogLevel.DEBUG, { str1 = key }, { "adding recommendation card $str1 to carousel" } { "adding recommendation card $str1 to carousel" }, ) } Loading @@ -110,7 +110,7 @@ class MediaLogger @Inject constructor(@MediaLog private val buffer: LogBuffer) { TAG, LogLevel.DEBUG, { str1 = key }, { "removing recommendation card $str1 from carousel" } { "removing recommendation card $str1 from carousel" }, ) } Loading @@ -119,7 +119,24 @@ class MediaLogger @Inject constructor(@MediaLog private val buffer: LogBuffer) { TAG, LogLevel.DEBUG, { str1 = key }, { "duplicate media notification $str1 posted" } { "duplicate media notification $str1 posted" }, ) } fun logMediaControlIsBound( instanceId: InstanceId, artistName: CharSequence, title: CharSequence, ) { buffer.log( TAG, LogLevel.DEBUG, { str1 = instanceId.toString() str2 = artistName.toString() str3 = title.toString() }, { "binding media control, instance id= $str1, artist= $str2, title= $str3" }, ) } Loading packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt +30 −27 Original line number Diff line number Diff line Loading @@ -80,8 +80,9 @@ object MediaControlViewBinder { mediaCard.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.STARTED) { launch { viewModel.player.collectLatest { playerViewModel -> playerViewModel?.let { viewModel.player.collectLatest { player -> player?.let { if (viewModel.isNewPlayer(it)) { bindMediaCard( viewHolder, viewController, Loading @@ -90,6 +91,8 @@ object MediaControlViewBinder { backgroundDispatcher, mainDispatcher, ) viewModel.onMediaControlIsBound(it.artistName, it.titleName) } } } } Loading Loading @@ -143,7 +146,7 @@ object MediaControlViewBinder { viewHolder, viewModel.outputSwitcher, viewController, falsingManager falsingManager, ) bindGutsViewModel(viewHolder, viewModel, viewController, falsingManager) bindActionButtons(viewHolder, viewModel, viewController, falsingManager) Loading @@ -157,7 +160,7 @@ object MediaControlViewBinder { viewController, backgroundDispatcher, mainDispatcher, isSongUpdated isSongUpdated, ) if (viewModel.playTurbulenceNoise) { Loading Loading @@ -259,12 +262,12 @@ object MediaControlViewBinder { if (buttonView.id == R.id.actionPrev) { viewController.setUpPrevButtonInfo( buttonModel.isEnabled, buttonModel.notVisibleValue buttonModel.notVisibleValue, ) } else if (buttonView.id == R.id.actionNext) { viewController.setUpNextButtonInfo( buttonModel.isEnabled, buttonModel.notVisibleValue buttonModel.notVisibleValue, ) } val animHandler = (buttonView.tag ?: AnimationBindHandler()) as AnimationBindHandler Loading Loading @@ -295,7 +298,7 @@ object MediaControlViewBinder { viewController.collapsedLayout, visible, buttonModel.notVisibleValue, buttonModel.showInCollapsed buttonModel.showInCollapsed, ) } } Loading Loading @@ -350,7 +353,7 @@ object MediaControlViewBinder { createTouchRippleAnimation( button, viewController.colorSchemeTransition, multiRippleView multiRippleView, ) ) Loading Loading @@ -382,12 +385,12 @@ object MediaControlViewBinder { setVisibleAndAlpha( expandedSet, R.id.media_explicit_indicator, viewModel.isExplicitVisible viewModel.isExplicitVisible, ) setVisibleAndAlpha( collapsedSet, R.id.media_explicit_indicator, viewModel.isExplicitVisible viewModel.isExplicitVisible, ) // refreshState is required here to resize the text views (and prevent ellipsis) Loading @@ -398,7 +401,7 @@ object MediaControlViewBinder { // something is incorrectly bound, but needs to be run if other elements were // updated while the enter animation was running viewController.refreshState() } }, ) } Loading Loading @@ -427,7 +430,7 @@ object MediaControlViewBinder { viewModel.backgroundCover!!, viewModel.colorScheme, width, height height, ) } else { ColorDrawable(Color.TRANSPARENT) Loading Loading @@ -493,7 +496,7 @@ object MediaControlViewBinder { transitionDrawable: TransitionDrawable, layer: Int, targetWidth: Int, targetHeight: Int targetHeight: Int, ) { val drawable = transitionDrawable.getDrawable(layer) ?: return val width = drawable.intrinsicWidth Loading @@ -509,7 +512,7 @@ object MediaControlViewBinder { artworkIcon: android.graphics.drawable.Icon, mutableColorScheme: ColorScheme, width: Int, height: Int height: Int, ): LayerDrawable { val albumArt = MediaArtworkHelper.getScaledBackground(context, artworkIcon, width, height) return MediaArtworkHelper.setUpGradientColorOnDrawable( Loading @@ -517,7 +520,7 @@ object MediaControlViewBinder { context.getDrawable(R.drawable.qs_media_scrim)?.mutate() as GradientDrawable, mutableColorScheme, MEDIA_PLAYER_SCRIM_START_ALPHA, MEDIA_PLAYER_SCRIM_END_ALPHA MEDIA_PLAYER_SCRIM_END_ALPHA, ) } Loading @@ -544,7 +547,7 @@ object MediaControlViewBinder { private fun createTouchRippleAnimation( button: ImageButton, colorSchemeTransition: ColorSchemeTransition, multiRippleView: MultiRippleView multiRippleView: MultiRippleView, ): RippleAnimation { val maxSize = (multiRippleView.width * 2).toFloat() return RippleAnimation( Loading @@ -562,7 +565,7 @@ object MediaControlViewBinder { baseRingFadeParams = null, sparkleRingFadeParams = null, centerFillFadeParams = null, shouldDistort = false shouldDistort = false, ) ) } Loading Loading @@ -596,7 +599,7 @@ object MediaControlViewBinder { set: ConstraintSet, resId: Int, visible: Boolean, notVisibleValue: Int notVisibleValue: Int, ) { set.setVisibility(resId, if (visible) ConstraintSet.VISIBLE else notVisibleValue) set.setAlpha(resId, if (visible) 1.0f else 0.0f) Loading @@ -618,7 +621,7 @@ object MediaControlViewBinder { collapsedSet: ConstraintSet, visible: Boolean, notVisibleValue: Int, showInCollapsed: Boolean showInCollapsed: Boolean, ) { if (notVisibleValue == ConstraintSet.INVISIBLE) { // Since time views should appear instead of buttons. Loading packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaActionViewModel.kt +13 −1 Original line number Diff line number Diff line Loading @@ -32,4 +32,16 @@ data class MediaActionViewModel( val buttonId: Int? = null, val isEnabled: Boolean, val onClicked: (Int) -> Unit, ) ) { fun contentEquals(other: MediaActionViewModel?): Boolean { return other?.let { contentDescription == other.contentDescription && isVisibleWhenScrubbing == other.isVisibleWhenScrubbing && notVisibleValue == other.notVisibleValue && showInCollapsed == other.showInCollapsed && rebindId == other.rebindId && buttonId == other.buttonId && isEnabled == other.isEnabled } ?: false } } Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelTest.kt +70 −24 Original line number Diff line number Diff line Loading @@ -24,26 +24,25 @@ import android.media.session.MediaSession import android.media.session.PlaybackState import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.logging.InstanceId import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testScope import com.android.systemui.media.controls.domain.pipeline.MediaDataFilterImpl import com.android.systemui.media.controls.domain.pipeline.mediaDataFilter import com.android.systemui.media.controls.shared.model.MediaData import com.android.systemui.media.controls.shared.model.MediaDeviceData import com.android.systemui.media.controls.util.mediaInstanceId import com.android.systemui.statusbar.notificationLockscreenUserManager import com.android.systemui.testKosmos import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers import org.mockito.ArgumentMatchers.any import org.mockito.Mockito import org.mockito.kotlin.eq import org.mockito.kotlin.whenever @SmallTest @RunWith(AndroidJUnit4::class) Loading @@ -52,17 +51,15 @@ class MediaControlViewModelTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope private val mediaDataFilter: MediaDataFilterImpl = kosmos.mediaDataFilter private val mediaDataFilter = kosmos.mediaDataFilter private val notificationLockscreenUserManager = kosmos.notificationLockscreenUserManager private val packageManager = kosmos.packageManager private val drawable = context.getDrawable(R.drawable.ic_media_play) private val instanceId: InstanceId = kosmos.mediaInstanceId private val instanceId = kosmos.mediaInstanceId private val underTest: MediaControlViewModel = kosmos.mediaControlViewModel @Test fun addMediaControl_mediaControlViewModelIsLoaded() = testScope.runTest { @Before fun setUp() { whenever(packageManager.getApplicationIcon(Mockito.anyString())).thenReturn(drawable) whenever(packageManager.getApplicationIcon(any(ApplicationInfo::class.java))) .thenReturn(drawable) Loading @@ -71,11 +68,14 @@ class MediaControlViewModelTest : SysuiTestCase() { whenever(packageManager.getApplicationLabel(any())).thenReturn(PACKAGE_NAME) whenever(notificationLockscreenUserManager.isCurrentProfile(USER_ID)).thenReturn(true) whenever(notificationLockscreenUserManager.isProfileAvailable(USER_ID)).thenReturn(true) val playerModel by collectLastValue(underTest.player) context.setMockPackageManager(packageManager) } val mediaData = initMediaData() @Test fun addMediaControl_mediaControlViewModelIsLoaded() = testScope.runTest { val playerModel by collectLastValue(underTest.player) val mediaData = initMediaData(ARTIST, TITLE) mediaDataFilter.onMediaDataLoaded(KEY, KEY, mediaData) Loading @@ -88,7 +88,51 @@ class MediaControlViewModelTest : SysuiTestCase() { assertThat(playerModel?.playTurbulenceNoise).isFalse() } private fun initMediaData(): MediaData { @Test fun emitDuplicateMediaControls_mediaControlIsNotBound() = testScope.runTest { val playerModel by collectLastValue(underTest.player) val mediaData = initMediaData(ARTIST, TITLE) mediaDataFilter.onMediaDataLoaded(KEY, KEY, mediaData) assertThat(playerModel).isNotNull() assertThat(playerModel?.titleName).isEqualTo(TITLE) assertThat(playerModel?.artistName).isEqualTo(ARTIST) assertThat(underTest.isNewPlayer(playerModel!!)).isTrue() mediaDataFilter.onMediaDataLoaded(KEY, KEY, mediaData) assertThat(playerModel).isNotNull() assertThat(playerModel?.titleName).isEqualTo(TITLE) assertThat(playerModel?.artistName).isEqualTo(ARTIST) assertThat(underTest.isNewPlayer(playerModel!!)).isFalse() } @Test fun emitDifferentMediaControls_mediaControlIsBound() = testScope.runTest { val playerModel by collectLastValue(underTest.player) var mediaData = initMediaData(ARTIST, TITLE) mediaDataFilter.onMediaDataLoaded(KEY, KEY, mediaData) assertThat(playerModel).isNotNull() assertThat(playerModel?.titleName).isEqualTo(TITLE) assertThat(playerModel?.artistName).isEqualTo(ARTIST) assertThat(underTest.isNewPlayer(playerModel!!)).isTrue() mediaData = initMediaData(ARTIST_2, TITLE_2) mediaDataFilter.onMediaDataLoaded(KEY, KEY, mediaData) assertThat(playerModel).isNotNull() assertThat(playerModel?.titleName).isEqualTo(TITLE_2) assertThat(playerModel?.artistName).isEqualTo(ARTIST_2) assertThat(underTest.isNewPlayer(playerModel!!)).isTrue() } private fun initMediaData(artist: String, title: String): MediaData { val device = MediaDeviceData(true, null, DEVICE_NAME, null, showBroadcastButton = true) // Create media session Loading @@ -111,12 +155,12 @@ class MediaControlViewModelTest : SysuiTestCase() { return MediaData( userId = USER_ID, artist = ARTIST, song = TITLE, artist = artist, song = title, packageName = PACKAGE, token = session.sessionToken, device = device, instanceId = instanceId instanceId = instanceId, ) } Loading @@ -127,6 +171,8 @@ class MediaControlViewModelTest : SysuiTestCase() { private const val PACKAGE = "PKG" private const val ARTIST = "ARTIST" private const val TITLE = "TITLE" private const val ARTIST_2 = "ARTIST_2" private const val TITLE_2 = "TITLE_2" private const val DEVICE_NAME = "DEVICE_NAME" private const val SESSION_KEY = "SESSION_KEY" private const val SESSION_ARTIST = "SESSION_ARTIST" Loading
packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt +14 −8 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ import com.android.systemui.bluetooth.BroadcastDialogController import com.android.systemui.media.controls.data.repository.MediaFilterRepository import com.android.systemui.media.controls.domain.pipeline.MediaDataProcessor import com.android.systemui.media.controls.domain.pipeline.getNotificationActions import com.android.systemui.media.controls.shared.MediaLogger import com.android.systemui.media.controls.shared.model.MediaControlModel import com.android.systemui.media.controls.shared.model.MediaData import com.android.systemui.media.controls.util.MediaSmartspaceLogger Loading @@ -59,6 +60,7 @@ constructor( private val lockscreenUserManager: NotificationLockscreenUserManager, private val mediaOutputDialogManager: MediaOutputDialogManager, private val broadcastDialogController: BroadcastDialogController, private val mediaLogger: MediaLogger, ) { val mediaControl: Flow<MediaControlModel?> = Loading @@ -73,7 +75,7 @@ constructor( instanceId: InstanceId, delayMs: Long, eventId: Int, location: Int location: Int, ): Boolean { logSmartspaceUserEvent(eventId, location) val dismissed = Loading @@ -81,7 +83,7 @@ constructor( if (!dismissed) { Log.w( TAG, "Manager failed to dismiss media of instanceId=$instanceId, Token uid=${token?.uid}" "Manager failed to dismiss media of instanceId=$instanceId, Token uid=${token?.uid}", ) } return dismissed Loading Loading @@ -120,13 +122,13 @@ constructor( expandable: Expandable, clickIntent: PendingIntent, eventId: Int, location: Int location: Int, ) { logSmartspaceUserEvent(eventId, location) if (!launchOverLockscreen(clickIntent)) { activityStarter.postStartActivityDismissingKeyguard( clickIntent, expandable.activityTransitionController(Cuj.CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER) expandable.activityTransitionController(Cuj.CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER), ) } } Loading @@ -146,7 +148,7 @@ constructor( keyguardStateController.isShowing && activityIntentHelper.wouldPendingShowOverLockscreen( pendingIntent, lockscreenUserManager.currentUserId lockscreenUserManager.currentUserId, ) if (showOverLockscreen) { try { Loading @@ -166,7 +168,7 @@ constructor( fun startMediaOutputDialog( expandable: Expandable, packageName: String, token: MediaSession.Token? = null token: MediaSession.Token? = null, ) { mediaOutputDialogManager.createAndShowWithController( packageName, Loading @@ -180,7 +182,7 @@ constructor( broadcastDialogController.createBroadcastDialogWithController( broadcastApp, packageName, expandable.dialogTransitionController() expandable.dialogTransitionController(), ) } Loading @@ -188,10 +190,14 @@ constructor( repository.logSmartspaceCardUserEvent( eventId, MediaSmartspaceLogger.getSurface(location), instanceId = instanceId instanceId = instanceId, ) } fun logMediaControlIsBound(artistName: CharSequence, songName: CharSequence) { mediaLogger.logMediaControlIsBound(instanceId, artistName, songName) } private fun Expandable.dialogController(): DialogTransitionAnimator.Controller? { return dialogTransitionController( cuj = Loading
packages/SystemUI/src/com/android/systemui/media/controls/shared/MediaLogger.kt +26 −9 Original line number Diff line number Diff line Loading @@ -36,7 +36,7 @@ class MediaLogger @Inject constructor(@MediaLog private val buffer: LogBuffer) { bool1 = active str2 = reason }, { "add media $str1, active: $bool1, reason: $str2" } { "add media $str1, active: $bool1, reason: $str2" }, ) } Loading @@ -48,7 +48,7 @@ class MediaLogger @Inject constructor(@MediaLog private val buffer: LogBuffer) { str1 = instanceId.toString() str2 = reason }, { "removing media $str1, reason: $str2" } { "removing media $str1, reason: $str2" }, ) } Loading @@ -61,7 +61,7 @@ class MediaLogger @Inject constructor(@MediaLog private val buffer: LogBuffer) { bool1 = isActive str2 = reason }, { "add recommendation $str1, active $bool1, reason: $str2" } { "add recommendation $str1, active $bool1, reason: $str2" }, ) } Loading @@ -74,7 +74,7 @@ class MediaLogger @Inject constructor(@MediaLog private val buffer: LogBuffer) { bool1 = immediately str2 = reason }, { "removing recommendation $str1, immediate=$bool1, reason: $str2" } { "removing recommendation $str1, immediate=$bool1, reason: $str2" }, ) } Loading @@ -83,7 +83,7 @@ class MediaLogger @Inject constructor(@MediaLog private val buffer: LogBuffer) { TAG, LogLevel.DEBUG, { str1 = instanceId.toString() }, { "adding media card $str1 to carousel" } { "adding media card $str1 to carousel" }, ) } Loading @@ -92,7 +92,7 @@ class MediaLogger @Inject constructor(@MediaLog private val buffer: LogBuffer) { TAG, LogLevel.DEBUG, { str1 = instanceId.toString() }, { "removing media card $str1 from carousel" } { "removing media card $str1 from carousel" }, ) } Loading @@ -101,7 +101,7 @@ class MediaLogger @Inject constructor(@MediaLog private val buffer: LogBuffer) { TAG, LogLevel.DEBUG, { str1 = key }, { "adding recommendation card $str1 to carousel" } { "adding recommendation card $str1 to carousel" }, ) } Loading @@ -110,7 +110,7 @@ class MediaLogger @Inject constructor(@MediaLog private val buffer: LogBuffer) { TAG, LogLevel.DEBUG, { str1 = key }, { "removing recommendation card $str1 from carousel" } { "removing recommendation card $str1 from carousel" }, ) } Loading @@ -119,7 +119,24 @@ class MediaLogger @Inject constructor(@MediaLog private val buffer: LogBuffer) { TAG, LogLevel.DEBUG, { str1 = key }, { "duplicate media notification $str1 posted" } { "duplicate media notification $str1 posted" }, ) } fun logMediaControlIsBound( instanceId: InstanceId, artistName: CharSequence, title: CharSequence, ) { buffer.log( TAG, LogLevel.DEBUG, { str1 = instanceId.toString() str2 = artistName.toString() str3 = title.toString() }, { "binding media control, instance id= $str1, artist= $str2, title= $str3" }, ) } Loading
packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt +30 −27 Original line number Diff line number Diff line Loading @@ -80,8 +80,9 @@ object MediaControlViewBinder { mediaCard.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.STARTED) { launch { viewModel.player.collectLatest { playerViewModel -> playerViewModel?.let { viewModel.player.collectLatest { player -> player?.let { if (viewModel.isNewPlayer(it)) { bindMediaCard( viewHolder, viewController, Loading @@ -90,6 +91,8 @@ object MediaControlViewBinder { backgroundDispatcher, mainDispatcher, ) viewModel.onMediaControlIsBound(it.artistName, it.titleName) } } } } Loading Loading @@ -143,7 +146,7 @@ object MediaControlViewBinder { viewHolder, viewModel.outputSwitcher, viewController, falsingManager falsingManager, ) bindGutsViewModel(viewHolder, viewModel, viewController, falsingManager) bindActionButtons(viewHolder, viewModel, viewController, falsingManager) Loading @@ -157,7 +160,7 @@ object MediaControlViewBinder { viewController, backgroundDispatcher, mainDispatcher, isSongUpdated isSongUpdated, ) if (viewModel.playTurbulenceNoise) { Loading Loading @@ -259,12 +262,12 @@ object MediaControlViewBinder { if (buttonView.id == R.id.actionPrev) { viewController.setUpPrevButtonInfo( buttonModel.isEnabled, buttonModel.notVisibleValue buttonModel.notVisibleValue, ) } else if (buttonView.id == R.id.actionNext) { viewController.setUpNextButtonInfo( buttonModel.isEnabled, buttonModel.notVisibleValue buttonModel.notVisibleValue, ) } val animHandler = (buttonView.tag ?: AnimationBindHandler()) as AnimationBindHandler Loading Loading @@ -295,7 +298,7 @@ object MediaControlViewBinder { viewController.collapsedLayout, visible, buttonModel.notVisibleValue, buttonModel.showInCollapsed buttonModel.showInCollapsed, ) } } Loading Loading @@ -350,7 +353,7 @@ object MediaControlViewBinder { createTouchRippleAnimation( button, viewController.colorSchemeTransition, multiRippleView multiRippleView, ) ) Loading Loading @@ -382,12 +385,12 @@ object MediaControlViewBinder { setVisibleAndAlpha( expandedSet, R.id.media_explicit_indicator, viewModel.isExplicitVisible viewModel.isExplicitVisible, ) setVisibleAndAlpha( collapsedSet, R.id.media_explicit_indicator, viewModel.isExplicitVisible viewModel.isExplicitVisible, ) // refreshState is required here to resize the text views (and prevent ellipsis) Loading @@ -398,7 +401,7 @@ object MediaControlViewBinder { // something is incorrectly bound, but needs to be run if other elements were // updated while the enter animation was running viewController.refreshState() } }, ) } Loading Loading @@ -427,7 +430,7 @@ object MediaControlViewBinder { viewModel.backgroundCover!!, viewModel.colorScheme, width, height height, ) } else { ColorDrawable(Color.TRANSPARENT) Loading Loading @@ -493,7 +496,7 @@ object MediaControlViewBinder { transitionDrawable: TransitionDrawable, layer: Int, targetWidth: Int, targetHeight: Int targetHeight: Int, ) { val drawable = transitionDrawable.getDrawable(layer) ?: return val width = drawable.intrinsicWidth Loading @@ -509,7 +512,7 @@ object MediaControlViewBinder { artworkIcon: android.graphics.drawable.Icon, mutableColorScheme: ColorScheme, width: Int, height: Int height: Int, ): LayerDrawable { val albumArt = MediaArtworkHelper.getScaledBackground(context, artworkIcon, width, height) return MediaArtworkHelper.setUpGradientColorOnDrawable( Loading @@ -517,7 +520,7 @@ object MediaControlViewBinder { context.getDrawable(R.drawable.qs_media_scrim)?.mutate() as GradientDrawable, mutableColorScheme, MEDIA_PLAYER_SCRIM_START_ALPHA, MEDIA_PLAYER_SCRIM_END_ALPHA MEDIA_PLAYER_SCRIM_END_ALPHA, ) } Loading @@ -544,7 +547,7 @@ object MediaControlViewBinder { private fun createTouchRippleAnimation( button: ImageButton, colorSchemeTransition: ColorSchemeTransition, multiRippleView: MultiRippleView multiRippleView: MultiRippleView, ): RippleAnimation { val maxSize = (multiRippleView.width * 2).toFloat() return RippleAnimation( Loading @@ -562,7 +565,7 @@ object MediaControlViewBinder { baseRingFadeParams = null, sparkleRingFadeParams = null, centerFillFadeParams = null, shouldDistort = false shouldDistort = false, ) ) } Loading Loading @@ -596,7 +599,7 @@ object MediaControlViewBinder { set: ConstraintSet, resId: Int, visible: Boolean, notVisibleValue: Int notVisibleValue: Int, ) { set.setVisibility(resId, if (visible) ConstraintSet.VISIBLE else notVisibleValue) set.setAlpha(resId, if (visible) 1.0f else 0.0f) Loading @@ -618,7 +621,7 @@ object MediaControlViewBinder { collapsedSet: ConstraintSet, visible: Boolean, notVisibleValue: Int, showInCollapsed: Boolean showInCollapsed: Boolean, ) { if (notVisibleValue == ConstraintSet.INVISIBLE) { // Since time views should appear instead of buttons. Loading
packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaActionViewModel.kt +13 −1 Original line number Diff line number Diff line Loading @@ -32,4 +32,16 @@ data class MediaActionViewModel( val buttonId: Int? = null, val isEnabled: Boolean, val onClicked: (Int) -> Unit, ) ) { fun contentEquals(other: MediaActionViewModel?): Boolean { return other?.let { contentDescription == other.contentDescription && isVisibleWhenScrubbing == other.isVisibleWhenScrubbing && notVisibleValue == other.notVisibleValue && showInCollapsed == other.showInCollapsed && rebindId == other.rebindId && buttonId == other.buttonId && isEnabled == other.isEnabled } ?: false } }