Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ad7d1d18 authored by Michael Mikhail's avatar Michael Mikhail
Browse files

Fix reorder of UMO in carousel

Show the current media list instead of the sorted list. Media should not
be reordered unless a new UMO or recommendation is added to the carousel.

On Removal of media controls or recommendations, we should remove the
card without sorting.
When media carousel view started to show on the screen, we should show
the sorted media.

Flag: ACONFIG com.android.systemui.media_controls_refactor DISABLED
Bug: 328207006
Test: atest MediaDataFilterImplTest
Test: atest SystemUiRoboTests:MediaFilterRepositoryTest
Test: atest SystemUiRoboTests:MediaCarouselInteractorTest
Test: atest SystemUiRoboTests:MediaCarouselViewModelTest
Change-Id: I5600fb9610984082199a5839a5d825107648ddb0
parent 9a561a88
Loading
Loading
Loading
Loading
+74 −20
Original line number Original line Diff line number Diff line
@@ -150,7 +150,7 @@ class MediaFilterRepositoryTest : SysuiTestCase() {
    @Test
    @Test
    fun addMediaControlPlayingThenRemote() =
    fun addMediaControlPlayingThenRemote() =
        testScope.runTest {
        testScope.runTest {
            val sortedMedia by collectLastValue(underTest.sortedMedia)
            val currentMedia by collectLastValue(underTest.currentMedia)
            val playingInstanceId = InstanceId.fakeInstanceId(123)
            val playingInstanceId = InstanceId.fakeInstanceId(123)
            val remoteInstanceId = InstanceId.fakeInstanceId(321)
            val remoteInstanceId = InstanceId.fakeInstanceId(321)
            val playingData = createMediaData("app1", true, LOCAL, false, playingInstanceId)
            val playingData = createMediaData("app1", true, LOCAL, false, playingInstanceId)
@@ -161,8 +161,8 @@ class MediaFilterRepositoryTest : SysuiTestCase() {
            underTest.addSelectedUserMediaEntry(remoteData)
            underTest.addSelectedUserMediaEntry(remoteData)
            underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(remoteInstanceId))
            underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(remoteInstanceId))


            assertThat(sortedMedia?.size).isEqualTo(2)
            assertThat(currentMedia?.size).isEqualTo(2)
            assertThat(sortedMedia?.values)
            assertThat(currentMedia)
                .containsExactly(
                .containsExactly(
                    MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(playingInstanceId)),
                    MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(playingInstanceId)),
                    MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(remoteInstanceId))
                    MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(remoteInstanceId))
@@ -173,7 +173,7 @@ class MediaFilterRepositoryTest : SysuiTestCase() {
    @Test
    @Test
    fun switchMediaControlsPlaying() =
    fun switchMediaControlsPlaying() =
        testScope.runTest {
        testScope.runTest {
            val sortedMedia by collectLastValue(underTest.sortedMedia)
            val currentMedia by collectLastValue(underTest.currentMedia)
            val playingInstanceId1 = InstanceId.fakeInstanceId(123)
            val playingInstanceId1 = InstanceId.fakeInstanceId(123)
            val playingInstanceId2 = InstanceId.fakeInstanceId(321)
            val playingInstanceId2 = InstanceId.fakeInstanceId(321)
            var playingData1 = createMediaData("app1", true, LOCAL, false, playingInstanceId1)
            var playingData1 = createMediaData("app1", true, LOCAL, false, playingInstanceId1)
@@ -184,8 +184,8 @@ class MediaFilterRepositoryTest : SysuiTestCase() {
            underTest.addSelectedUserMediaEntry(playingData2)
            underTest.addSelectedUserMediaEntry(playingData2)
            underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(playingInstanceId2))
            underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(playingInstanceId2))


            assertThat(sortedMedia?.size).isEqualTo(2)
            assertThat(currentMedia?.size).isEqualTo(2)
            assertThat(sortedMedia?.values)
            assertThat(currentMedia)
                .containsExactly(
                .containsExactly(
                    MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(playingInstanceId1)),
                    MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(playingInstanceId1)),
                    MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(playingInstanceId2))
                    MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(playingInstanceId2))
@@ -198,12 +198,28 @@ class MediaFilterRepositoryTest : SysuiTestCase() {
            underTest.addSelectedUserMediaEntry(playingData1)
            underTest.addSelectedUserMediaEntry(playingData1)
            underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(playingInstanceId1))
            underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(playingInstanceId1))
            underTest.addSelectedUserMediaEntry(playingData2)
            underTest.addSelectedUserMediaEntry(playingData2)
            underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(playingInstanceId2))
            underTest.addMediaDataLoadingState(
                MediaDataLoadingModel.Loaded(playingInstanceId2, false)
            )

            assertThat(currentMedia?.size).isEqualTo(2)
            assertThat(currentMedia)
                .containsExactly(
                    MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(playingInstanceId1)),
                    MediaCommonModel.MediaControl(
                        MediaDataLoadingModel.Loaded(playingInstanceId2, false)
                    )
                )
                .inOrder()


            assertThat(sortedMedia?.size).isEqualTo(2)
            underTest.setOrderedMedia()
            assertThat(sortedMedia?.values)

            assertThat(currentMedia?.size).isEqualTo(2)
            assertThat(currentMedia)
                .containsExactly(
                .containsExactly(
                    MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(playingInstanceId2)),
                    MediaCommonModel.MediaControl(
                        MediaDataLoadingModel.Loaded(playingInstanceId2, false)
                    ),
                    MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(playingInstanceId1))
                    MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(playingInstanceId1))
                )
                )
                .inOrder()
                .inOrder()
@@ -212,7 +228,7 @@ class MediaFilterRepositoryTest : SysuiTestCase() {
    @Test
    @Test
    fun fullOrderTest() =
    fun fullOrderTest() =
        testScope.runTest {
        testScope.runTest {
            val sortedMedia by collectLastValue(underTest.sortedMedia)
            val currentMedia by collectLastValue(underTest.currentMedia)
            val instanceId1 = InstanceId.fakeInstanceId(123)
            val instanceId1 = InstanceId.fakeInstanceId(123)
            val instanceId2 = InstanceId.fakeInstanceId(456)
            val instanceId2 = InstanceId.fakeInstanceId(456)
            val instanceId3 = InstanceId.fakeInstanceId(321)
            val instanceId3 = InstanceId.fakeInstanceId(321)
@@ -252,8 +268,8 @@ class MediaFilterRepositoryTest : SysuiTestCase() {
                SmartspaceMediaLoadingModel.Loaded(KEY_MEDIA_SMARTSPACE, true)
                SmartspaceMediaLoadingModel.Loaded(KEY_MEDIA_SMARTSPACE, true)
            )
            )


            assertThat(sortedMedia?.size).isEqualTo(6)
            assertThat(currentMedia?.size).isEqualTo(6)
            assertThat(sortedMedia?.values)
            assertThat(currentMedia)
                .containsExactly(
                .containsExactly(
                    MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(instanceId1)),
                    MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(instanceId1)),
                    MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(instanceId2)),
                    MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(instanceId2)),
@@ -270,7 +286,7 @@ class MediaFilterRepositoryTest : SysuiTestCase() {
    @Test
    @Test
    fun loadMediaFromRec() =
    fun loadMediaFromRec() =
        testScope.runTest {
        testScope.runTest {
            val isMediaFromRec by collectLastValue(underTest.isMediaFromRec)
            val currentMedia by collectLastValue(underTest.currentMedia)
            val instanceId1 = InstanceId.fakeInstanceId(123)
            val instanceId1 = InstanceId.fakeInstanceId(123)
            val instanceId2 = InstanceId.fakeInstanceId(456)
            val instanceId2 = InstanceId.fakeInstanceId(456)
            val data =
            val data =
@@ -278,22 +294,59 @@ class MediaFilterRepositoryTest : SysuiTestCase() {
                    active = true,
                    active = true,
                    instanceId = instanceId1,
                    instanceId = instanceId1,
                    packageName = PACKAGE_NAME,
                    packageName = PACKAGE_NAME,
                    isPlaying = true
                    isPlaying = true,
                    notificationKey = KEY,
                )
            val newData =
                MediaData(
                    active = true,
                    instanceId = instanceId2,
                    isPlaying = true,
                    notificationKey = KEY_2
                )
            val icon = Icon.createWithResource(context, R.drawable.ic_media_play)
            val mediaRecommendations =
                SmartspaceMediaData(
                    targetId = KEY_MEDIA_SMARTSPACE,
                    isActive = true,
                    packageName = PACKAGE_NAME,
                    recommendations = MediaTestHelper.getValidRecommendationList(icon),
                )
                )
            val newData = MediaData(active = true, instanceId = instanceId2)

            assertThat(isMediaFromRec).isFalse()


            underTest.setMediaFromRecPackageName(PACKAGE_NAME)
            underTest.setMediaFromRecPackageName(PACKAGE_NAME)
            underTest.addSelectedUserMediaEntry(data)
            underTest.addSelectedUserMediaEntry(data)
            underTest.setRecommendation(mediaRecommendations)
            underTest.setRecommendationsLoadingState(
                SmartspaceMediaLoadingModel.Loaded(KEY_MEDIA_SMARTSPACE)
            )
            underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId1))
            underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId1))


            assertThat(isMediaFromRec).isTrue()
            assertThat(currentMedia)
                .containsExactly(
                    MediaCommonModel.MediaControl(
                        MediaDataLoadingModel.Loaded(instanceId1),
                        isMediaFromRec = true
                    ),
                    MediaCommonModel.MediaRecommendations(
                        SmartspaceMediaLoadingModel.Loaded(KEY_MEDIA_SMARTSPACE)
                    )
                )
                .inOrder()


            underTest.addSelectedUserMediaEntry(newData)
            underTest.addSelectedUserMediaEntry(newData)
            underTest.addSelectedUserMediaEntry(data.copy(isPlaying = false))
            underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId2))
            underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId2))
            underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId1))


            assertThat(isMediaFromRec).isFalse()
            assertThat(currentMedia)
                .containsExactly(
                    MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(instanceId2)),
                    MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(instanceId1)),
                    MediaCommonModel.MediaRecommendations(
                        SmartspaceMediaLoadingModel.Loaded(KEY_MEDIA_SMARTSPACE)
                    )
                )
                .inOrder()
        }
        }


    private fun createMediaData(
    private fun createMediaData(
@@ -316,6 +369,7 @@ class MediaFilterRepositoryTest : SysuiTestCase() {
        private const val LOCAL = MediaData.PLAYBACK_LOCAL
        private const val LOCAL = MediaData.PLAYBACK_LOCAL
        private const val REMOTE = MediaData.PLAYBACK_CAST_LOCAL
        private const val REMOTE = MediaData.PLAYBACK_CAST_LOCAL
        private const val KEY = "KEY"
        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_MEDIA_SMARTSPACE = "MEDIA_SMARTSPACE_ID"
        private const val PACKAGE_NAME = "com.android.example"
        private const val PACKAGE_NAME = "com.android.example"
    }
    }
+35 −34
Original line number Original line Diff line number Diff line
@@ -55,6 +55,14 @@ class MediaCarouselInteractorTest : SysuiTestCase() {
    private val mediaFilterRepository: MediaFilterRepository = kosmos.mediaFilterRepository
    private val mediaFilterRepository: MediaFilterRepository = kosmos.mediaFilterRepository
    private val mediaRecommendationsInteractor: MediaRecommendationsInteractor =
    private val mediaRecommendationsInteractor: MediaRecommendationsInteractor =
        kosmos.mediaRecommendationsInteractor
        kosmos.mediaRecommendationsInteractor
    val icon = Icon.createWithResource(context, R.drawable.ic_media_play)
    private val mediaRecommendation =
        SmartspaceMediaData(
            targetId = KEY_MEDIA_SMARTSPACE,
            isActive = true,
            packageName = PACKAGE_NAME,
            recommendations = MediaTestHelper.getValidRecommendationList(icon),
        )


    private val underTest: MediaCarouselInteractor = kosmos.mediaCarouselInteractor
    private val underTest: MediaCarouselInteractor = kosmos.mediaCarouselInteractor


@@ -122,26 +130,19 @@ class MediaCarouselInteractorTest : SysuiTestCase() {
                collectLastValue(underTest.hasActiveMediaOrRecommendation)
                collectLastValue(underTest.hasActiveMediaOrRecommendation)
            val hasAnyMediaOrRecommendation by
            val hasAnyMediaOrRecommendation by
                collectLastValue(underTest.hasAnyMediaOrRecommendation)
                collectLastValue(underTest.hasAnyMediaOrRecommendation)
            val sortedMedia by collectLastValue(underTest.sortedMedia)
            val currentMedia by collectLastValue(underTest.currentMedia)
            kosmos.fakeFeatureFlagsClassic.set(Flags.MEDIA_RETAIN_RECOMMENDATIONS, false)
            kosmos.fakeFeatureFlagsClassic.set(Flags.MEDIA_RETAIN_RECOMMENDATIONS, false)


            val icon = Icon.createWithResource(context, R.drawable.ic_media_play)
            val userMediaRecommendation =
                SmartspaceMediaData(
                    targetId = KEY_MEDIA_SMARTSPACE,
                    isActive = true,
                    recommendations = MediaTestHelper.getValidRecommendationList(icon),
                )
            val userMedia = MediaData(active = false)
            val userMedia = MediaData(active = false)
            val recsLoadingModel = SmartspaceMediaLoadingModel.Loaded(KEY_MEDIA_SMARTSPACE, true)
            val recsLoadingModel = SmartspaceMediaLoadingModel.Loaded(KEY_MEDIA_SMARTSPACE, true)
            val mediaLoadingModel = MediaDataLoadingModel.Loaded(userMedia.instanceId)
            val mediaLoadingModel = MediaDataLoadingModel.Loaded(userMedia.instanceId)


            mediaFilterRepository.setRecommendation(userMediaRecommendation)
            mediaFilterRepository.setRecommendation(mediaRecommendation)
            mediaFilterRepository.setRecommendationsLoadingState(recsLoadingModel)
            mediaFilterRepository.setRecommendationsLoadingState(recsLoadingModel)


            assertThat(hasActiveMediaOrRecommendation).isTrue()
            assertThat(hasActiveMediaOrRecommendation).isTrue()
            assertThat(hasAnyMediaOrRecommendation).isTrue()
            assertThat(hasAnyMediaOrRecommendation).isTrue()
            assertThat(sortedMedia)
            assertThat(currentMedia)
                .containsExactly(MediaCommonModel.MediaRecommendations(recsLoadingModel))
                .containsExactly(MediaCommonModel.MediaRecommendations(recsLoadingModel))


            mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
            mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
@@ -149,7 +150,7 @@ class MediaCarouselInteractorTest : SysuiTestCase() {


            assertThat(hasActiveMediaOrRecommendation).isTrue()
            assertThat(hasActiveMediaOrRecommendation).isTrue()
            assertThat(hasAnyMediaOrRecommendation).isTrue()
            assertThat(hasAnyMediaOrRecommendation).isTrue()
            assertThat(sortedMedia)
            assertThat(currentMedia)
                .containsExactly(
                .containsExactly(
                    MediaCommonModel.MediaRecommendations(recsLoadingModel),
                    MediaCommonModel.MediaRecommendations(recsLoadingModel),
                    MediaCommonModel.MediaControl(mediaLoadingModel, true)
                    MediaCommonModel.MediaControl(mediaLoadingModel, true)
@@ -166,14 +167,6 @@ class MediaCarouselInteractorTest : SysuiTestCase() {
                collectLastValue(underTest.hasAnyMediaOrRecommendation)
                collectLastValue(underTest.hasAnyMediaOrRecommendation)
            kosmos.fakeFeatureFlagsClassic.set(Flags.MEDIA_RETAIN_RECOMMENDATIONS, false)
            kosmos.fakeFeatureFlagsClassic.set(Flags.MEDIA_RETAIN_RECOMMENDATIONS, false)


            val icon = Icon.createWithResource(context, R.drawable.ic_media_play)
            val mediaRecommendation =
                SmartspaceMediaData(
                    targetId = KEY_MEDIA_SMARTSPACE,
                    isActive = true,
                    recommendations = MediaTestHelper.getValidRecommendationList(icon),
                )

            mediaFilterRepository.setRecommendation(mediaRecommendation)
            mediaFilterRepository.setRecommendation(mediaRecommendation)


            assertThat(hasActiveMediaOrRecommendation).isTrue()
            assertThat(hasActiveMediaOrRecommendation).isTrue()
@@ -194,14 +187,6 @@ class MediaCarouselInteractorTest : SysuiTestCase() {
                collectLastValue(underTest.hasAnyMediaOrRecommendation)
                collectLastValue(underTest.hasAnyMediaOrRecommendation)
            kosmos.fakeFeatureFlagsClassic.set(Flags.MEDIA_RETAIN_RECOMMENDATIONS, false)
            kosmos.fakeFeatureFlagsClassic.set(Flags.MEDIA_RETAIN_RECOMMENDATIONS, false)


            val icon = Icon.createWithResource(context, R.drawable.ic_media_play)
            val mediaRecommendation =
                SmartspaceMediaData(
                    targetId = KEY_MEDIA_SMARTSPACE,
                    isActive = true,
                    recommendations = MediaTestHelper.getValidRecommendationList(icon),
                )

            mediaFilterRepository.setRecommendation(mediaRecommendation)
            mediaFilterRepository.setRecommendation(mediaRecommendation)


            assertThat(hasActiveMediaOrRecommendation).isTrue()
            assertThat(hasActiveMediaOrRecommendation).isTrue()
@@ -234,26 +219,42 @@ class MediaCarouselInteractorTest : SysuiTestCase() {
    @Test
    @Test
    fun loadMediaFromRec() =
    fun loadMediaFromRec() =
        testScope.runTest {
        testScope.runTest {
            val isMediaFromRec by collectLastValue(underTest.isMediaFromRec)
            val currentMedia by collectLastValue(underTest.currentMedia)
            val instanceId = InstanceId.fakeInstanceId(123)
            val instanceId = InstanceId.fakeInstanceId(123)
            val data = MediaData(active = true, instanceId = instanceId, packageName = PACKAGE_NAME)
            val data =

                MediaData(
            assertThat(isMediaFromRec).isFalse()
                    active = true,
                    instanceId = instanceId,
                    packageName = PACKAGE_NAME,
                    notificationKey = KEY
                )
            val smartspaceLoadingModel = SmartspaceMediaLoadingModel.Loaded(KEY_MEDIA_SMARTSPACE)
            val mediaLoadingModel = MediaDataLoadingModel.Loaded(instanceId)


            mediaFilterRepository.setRecommendation(mediaRecommendation)
            mediaFilterRepository.setRecommendationsLoadingState(smartspaceLoadingModel)
            mediaRecommendationsInteractor.switchToMediaControl(PACKAGE_NAME)
            mediaRecommendationsInteractor.switchToMediaControl(PACKAGE_NAME)
            mediaFilterRepository.addSelectedUserMediaEntry(data)
            mediaFilterRepository.addSelectedUserMediaEntry(data)
            mediaFilterRepository.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId))
            mediaFilterRepository.addMediaDataLoadingState(mediaLoadingModel)


            assertThat(isMediaFromRec).isFalse()
            assertThat(currentMedia)
                .containsExactly(MediaCommonModel.MediaRecommendations(smartspaceLoadingModel))
                .inOrder()


            mediaFilterRepository.addSelectedUserMediaEntry(data.copy(isPlaying = true))
            mediaFilterRepository.addSelectedUserMediaEntry(data.copy(isPlaying = true))
            mediaFilterRepository.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId))
            mediaFilterRepository.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId))


            assertThat(isMediaFromRec).isTrue()
            assertThat(currentMedia)
                .containsExactly(
                    MediaCommonModel.MediaControl(mediaLoadingModel, isMediaFromRec = true),
                    MediaCommonModel.MediaRecommendations(smartspaceLoadingModel)
                )
                .inOrder()
        }
        }


    companion object {
    companion object {
        private const val KEY_MEDIA_SMARTSPACE = "MEDIA_SMARTSPACE_ID"
        private const val KEY_MEDIA_SMARTSPACE = "MEDIA_SMARTSPACE_ID"
        private const val PACKAGE_NAME = "com.android.example"
        private const val PACKAGE_NAME = "com.android.example"
        private const val KEY = "key"
    }
    }
}
}
+23 −6
Original line number Original line Diff line number Diff line
@@ -92,13 +92,29 @@ class MediaCarouselViewModelTest : SysuiTestCase() {
            val instanceId1 = InstanceId.fakeInstanceId(123)
            val instanceId1 = InstanceId.fakeInstanceId(123)
            val instanceId2 = InstanceId.fakeInstanceId(456)
            val instanceId2 = InstanceId.fakeInstanceId(456)


            loadMediaControl(KEY, instanceId1)
            loadMediaControl(KEY, instanceId1, isPlaying = true)
            loadMediaControl(KEY_2, instanceId2)
            loadMediaControl(KEY_2, instanceId2, isPlaying = true)
            loadMediaControl(KEY, instanceId1, isPlaying = false)


            val firstMediaControl = sortedMedia?.get(0) as MediaCommonViewModel.MediaControl
            var mediaControl2 = sortedMedia?.get(0) as MediaCommonViewModel.MediaControl
            val secondMediaControl = sortedMedia?.get(1) as MediaCommonViewModel.MediaControl
            var mediaControl1 = sortedMedia?.get(1) as MediaCommonViewModel.MediaControl
            assertThat(firstMediaControl.instanceId).isEqualTo(instanceId2)
            assertThat(mediaControl2.instanceId).isEqualTo(instanceId2)
            assertThat(secondMediaControl.instanceId).isEqualTo(instanceId1)
            assertThat(mediaControl1.instanceId).isEqualTo(instanceId1)

            loadMediaControl(KEY, instanceId1, isPlaying = true)
            loadMediaControl(KEY_2, instanceId2, isPlaying = false)

            mediaControl2 = sortedMedia?.get(0) as MediaCommonViewModel.MediaControl
            mediaControl1 = sortedMedia?.get(1) as MediaCommonViewModel.MediaControl
            assertThat(mediaControl2.instanceId).isEqualTo(instanceId2)
            assertThat(mediaControl1.instanceId).isEqualTo(instanceId1)

            underTest.onAttached()

            mediaControl1 = sortedMedia?.get(0) as MediaCommonViewModel.MediaControl
            mediaControl2 = sortedMedia?.get(1) as MediaCommonViewModel.MediaControl
            assertThat(mediaControl1.instanceId).isEqualTo(instanceId1)
            assertThat(mediaControl2.instanceId).isEqualTo(instanceId2)
        }
        }


    @Test
    @Test
@@ -147,6 +163,7 @@ class MediaCarouselViewModelTest : SysuiTestCase() {
            val mediaControl = sortedMedia?.get(0) as MediaCommonViewModel.MediaControl
            val mediaControl = sortedMedia?.get(0) as MediaCommonViewModel.MediaControl
            assertThat(sortedMedia).hasSize(2)
            assertThat(sortedMedia).hasSize(2)
            assertThat(mediaControl.instanceId).isEqualTo(instanceId)
            assertThat(mediaControl.instanceId).isEqualTo(instanceId)
            assertThat(mediaControl.isMediaFromRec).isTrue()
        }
        }


    private fun loadMediaControl(key: String, instanceId: InstanceId, isPlaying: Boolean = true) {
    private fun loadMediaControl(key: String, instanceId: InstanceId, isPlaying: Boolean = true) {
+65 −20
Original line number Original line Diff line number Diff line
@@ -107,14 +107,11 @@ constructor(
            .thenByDescending { it.updateTime }
            .thenByDescending { it.updateTime }
            .thenByDescending { it.notificationKey }
            .thenByDescending { it.notificationKey }


    private val _sortedMedia: MutableStateFlow<TreeMap<MediaSortKeyModel, MediaCommonModel>> =
    private val _currentMedia: MutableStateFlow<List<MediaCommonModel>> =
        MutableStateFlow(TreeMap<MediaSortKeyModel, MediaCommonModel>(comparator))
        MutableStateFlow(mutableListOf())
    val sortedMedia: StateFlow<Map<MediaSortKeyModel, MediaCommonModel>> =
    val currentMedia = _currentMedia.asStateFlow()
        _sortedMedia.asStateFlow()

    private val _isMediaFromRec: MutableStateFlow<Boolean> = MutableStateFlow(false)
    val isMediaFromRec: StateFlow<Boolean> = _isMediaFromRec.asStateFlow()


    private var sortedMedia = TreeMap<MediaSortKeyModel, MediaCommonModel>(comparator)
    private var mediaFromRecPackageName: String? = null
    private var mediaFromRecPackageName: String? = null
    private var locale: Locale = applicationContext.resources.configuration.locales.get(0)
    private var locale: Locale = applicationContext.resources.configuration.locales.get(0)


@@ -186,7 +183,7 @@ constructor(
    fun addMediaDataLoadingState(mediaDataLoadingModel: MediaDataLoadingModel) {
    fun addMediaDataLoadingState(mediaDataLoadingModel: MediaDataLoadingModel) {
        val sortedMap = TreeMap<MediaSortKeyModel, MediaCommonModel>(comparator)
        val sortedMap = TreeMap<MediaSortKeyModel, MediaCommonModel>(comparator)
        sortedMap.putAll(
        sortedMap.putAll(
            _sortedMedia.value.filter { (_, commonModel) ->
            sortedMedia.filter { (_, commonModel) ->
                commonModel !is MediaCommonModel.MediaControl ||
                commonModel !is MediaCommonModel.MediaControl ||
                    commonModel.mediaLoadedModel.instanceId != mediaDataLoadingModel.instanceId
                    commonModel.mediaLoadedModel.instanceId != mediaDataLoadingModel.instanceId
            }
            }
@@ -207,18 +204,52 @@ constructor(
                )
                )


            if (mediaDataLoadingModel is MediaDataLoadingModel.Loaded) {
            if (mediaDataLoadingModel is MediaDataLoadingModel.Loaded) {
                val isMediaFromRec = isMediaFromRec(it)
                val newCommonModel =
                    MediaCommonModel.MediaControl(
                        mediaDataLoadingModel,
                        canBeRemoved(it),
                        isMediaFromRec(it)
                    )
                sortedMap[sortKey] = newCommonModel


                _isMediaFromRec.value = isMediaFromRec
                // On Addition or tapping on recommendations, we should show the new order of media.
                if (isMediaFromRec) {
                if (mediaFromRecPackageName == it.packageName) {
                    if (it.isPlaying == true) {
                        mediaFromRecPackageName = null
                        mediaFromRecPackageName = null
                        _currentMedia.value = sortedMap.values.toList()
                    }
                } else if (sortedMap.size > sortedMedia.size) {
                    _currentMedia.value = sortedMap.values.toList()
                } else if (sortedMap.size == sortedMedia.size) {
                    // When loading an update for an existing media control.
                    val currentList =
                        mutableListOf<MediaCommonModel>().apply { addAll(_currentMedia.value) }
                    currentList.forEachIndexed { index, mediaCommonModel ->
                        if (
                            mediaCommonModel is MediaCommonModel.MediaControl &&
                                mediaCommonModel.mediaLoadedModel.instanceId ==
                                    mediaDataLoadingModel.instanceId &&
                                mediaCommonModel != newCommonModel
                        ) {
                            // Update media model if changed.
                            currentList[index] = newCommonModel
                        }
                    }
                    _currentMedia.value = currentList
                }
                }
                sortedMap[sortKey] =
                    MediaCommonModel.MediaControl(mediaDataLoadingModel, canBeRemoved(it))
            }
            }
        }
        }


        _sortedMedia.value = sortedMap
        sortedMedia = sortedMap

        // On removal we want to keep the order being shown to user.
        if (mediaDataLoadingModel is MediaDataLoadingModel.Removed) {
            _currentMedia.value =
                _currentMedia.value.filter { commonModel ->
                    commonModel !is MediaCommonModel.MediaControl ||
                        mediaDataLoadingModel.instanceId != commonModel.mediaLoadedModel.instanceId
                }
        }
    }
    }


    fun setRecommendationsLoadingState(smartspaceMediaLoadingModel: SmartspaceMediaLoadingModel) {
    fun setRecommendationsLoadingState(smartspaceMediaLoadingModel: SmartspaceMediaLoadingModel) {
@@ -229,7 +260,7 @@ constructor(
            }
            }
        val sortedMap = TreeMap<MediaSortKeyModel, MediaCommonModel>(comparator)
        val sortedMap = TreeMap<MediaSortKeyModel, MediaCommonModel>(comparator)
        sortedMap.putAll(
        sortedMap.putAll(
            _sortedMedia.value.filter { (_, commonModel) ->
            sortedMedia.filter { (_, commonModel) ->
                commonModel !is MediaCommonModel.MediaRecommendations
                commonModel !is MediaCommonModel.MediaRecommendations
            }
            }
        )
        )
@@ -240,11 +271,25 @@ constructor(
                isPlaying = false,
                isPlaying = false,
                active = _smartspaceMediaData.value.isActive,
                active = _smartspaceMediaData.value.isActive,
            )
            )
        if (smartspaceMediaLoadingModel is SmartspaceMediaLoadingModel.Loaded) {
        when (smartspaceMediaLoadingModel) {
            sortedMap[sortKey] = MediaCommonModel.MediaRecommendations(smartspaceMediaLoadingModel)
            is SmartspaceMediaLoadingModel.Loaded ->
                sortedMap[sortKey] =
                    MediaCommonModel.MediaRecommendations(smartspaceMediaLoadingModel)
            is SmartspaceMediaLoadingModel.Removed ->
                _currentMedia.value =
                    _currentMedia.value.filter { commonModel ->
                        commonModel !is MediaCommonModel.MediaRecommendations
                    }
        }

        if (sortedMap.size > sortedMedia.size) {
            _currentMedia.value = sortedMap.values.toList()
        }
        sortedMedia = sortedMap
    }
    }


        _sortedMedia.value = sortedMap
    fun setOrderedMedia() {
        _currentMedia.value = sortedMedia.values.toList()
    }
    }


    fun setMediaFromRecPackageName(packageName: String) {
    fun setMediaFromRecPackageName(packageName: String) {
+7 −13
Original line number Original line Diff line number Diff line
@@ -63,7 +63,7 @@ constructor(
    private val mediaDeviceManager: MediaDeviceManager,
    private val mediaDeviceManager: MediaDeviceManager,
    private val mediaDataCombineLatest: MediaDataCombineLatest,
    private val mediaDataCombineLatest: MediaDataCombineLatest,
    private val mediaDataFilter: MediaDataFilterImpl,
    private val mediaDataFilter: MediaDataFilterImpl,
    mediaFilterRepository: MediaFilterRepository,
    private val mediaFilterRepository: MediaFilterRepository,
    private val mediaFlags: MediaFlags,
    private val mediaFlags: MediaFlags,
) : MediaDataManager, CoreStartable {
) : MediaDataManager, CoreStartable {


@@ -123,18 +123,8 @@ constructor(
                initialValue = false,
                initialValue = false,
            )
            )


    /** The most recent sorted set for user media instances */
    /** The current list for user media instances */
    val sortedMedia: StateFlow<List<MediaCommonModel>> =
    val currentMedia: StateFlow<List<MediaCommonModel>> = mediaFilterRepository.currentMedia
        mediaFilterRepository.sortedMedia
            .mapLatest { it.values.toList() }
            .stateIn(
                scope = applicationScope,
                started = SharingStarted.WhileSubscribed(),
                initialValue = emptyList(),
            )

    /** Whether the current change in media was done by clicking on a recommendation */
    val isMediaFromRec: StateFlow<Boolean> = mediaFilterRepository.isMediaFromRec


    override fun start() {
    override fun start() {
        if (!mediaFlags.isMediaControlsRefactorEnabled()) {
        if (!mediaFlags.isMediaControlsRefactorEnabled()) {
@@ -251,6 +241,10 @@ constructor(


    override fun isRecommendationActive() = mediaDataRepository.smartspaceMediaData.value.isActive
    override fun isRecommendationActive() = mediaDataRepository.smartspaceMediaData.value.isActive


    fun reorderMedia() {
        mediaFilterRepository.setOrderedMedia()
    }

    /** Add a listener for internal events. */
    /** Add a listener for internal events. */
    private fun addInternalListener(listener: MediaDataManager.Listener) =
    private fun addInternalListener(listener: MediaDataManager.Listener) =
        mediaDataProcessor.addInternalListener(listener)
        mediaDataProcessor.addInternalListener(listener)
Loading