diff options
author | 2025-03-03 11:57:33 -0800 | |
---|---|---|
committer | 2025-03-03 11:57:33 -0800 | |
commit | 3b44be4a07d2bb8128854c5cb426224cebd4a19c (patch) | |
tree | ec9db1e7233da963e8c64227e7e72f2bbcc7e8ba | |
parent | 7380bf0d40a5a7e8ebc1b524558d5edc9764d7b7 (diff) | |
parent | de330e9bdc8c1196d589f7619383ae7151ba72bd (diff) |
Merge "Clean up recommendations UI code" into main
34 files changed, 43 insertions, 4048 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt index 0197a1e61801..c72afc72fa16 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt @@ -16,26 +16,17 @@ package com.android.systemui.media.controls.domain.interactor -import android.R -import android.graphics.drawable.Icon 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.MediaTestHelper import com.android.systemui.media.controls.data.repository.MediaFilterRepository import com.android.systemui.media.controls.data.repository.mediaFilterRepository import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor -import com.android.systemui.media.controls.domain.pipeline.interactor.MediaRecommendationsInteractor import com.android.systemui.media.controls.domain.pipeline.interactor.mediaCarouselInteractor -import com.android.systemui.media.controls.domain.pipeline.interactor.mediaRecommendationsInteractor -import com.android.systemui.media.controls.shared.model.MediaCommonModel import com.android.systemui.media.controls.shared.model.MediaData import com.android.systemui.media.controls.shared.model.MediaDataLoadingModel -import com.android.systemui.media.controls.shared.model.SmartspaceMediaData -import com.android.systemui.media.controls.shared.model.SmartspaceMediaLoadingModel import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.runTest @@ -52,16 +43,6 @@ class MediaCarouselInteractorTest : SysuiTestCase() { private val mediaFilterRepository: MediaFilterRepository = with(kosmos) { mediaFilterRepository } - private val mediaRecommendationsInteractor: 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 @@ -119,81 +100,6 @@ class MediaCarouselInteractorTest : SysuiTestCase() { } @Test - fun addActiveRecommendation_inactiveMedia() = - testScope.runTest { - val hasActiveMediaOrRecommendation by - collectLastValue(underTest.hasActiveMediaOrRecommendation) - val hasAnyMediaOrRecommendation by - collectLastValue(underTest.hasAnyMediaOrRecommendation) - val currentMedia by collectLastValue(underTest.currentMedia) - - val userMedia = MediaData(active = false) - val recsLoadingModel = SmartspaceMediaLoadingModel.Loaded(KEY_MEDIA_SMARTSPACE, true) - val mediaLoadingModel = MediaDataLoadingModel.Loaded(userMedia.instanceId) - - mediaFilterRepository.setRecommendation(mediaRecommendation) - mediaFilterRepository.setRecommendationsLoadingState(recsLoadingModel) - - assertThat(hasActiveMediaOrRecommendation).isTrue() - assertThat(hasAnyMediaOrRecommendation).isTrue() - assertThat(currentMedia) - .containsExactly(MediaCommonModel.MediaRecommendations(recsLoadingModel)) - - mediaFilterRepository.addSelectedUserMediaEntry(userMedia) - mediaFilterRepository.addMediaDataLoadingState(mediaLoadingModel) - mediaFilterRepository.setOrderedMedia() - - assertThat(hasActiveMediaOrRecommendation).isTrue() - assertThat(hasAnyMediaOrRecommendation).isTrue() - assertThat(currentMedia) - .containsExactly( - MediaCommonModel.MediaRecommendations(recsLoadingModel), - MediaCommonModel.MediaControl(mediaLoadingModel, true), - ) - .inOrder() - } - - @Test - fun addActiveRecommendation_thenInactive() = - testScope.runTest { - val hasActiveMediaOrRecommendation by - collectLastValue(underTest.hasActiveMediaOrRecommendation) - val hasAnyMediaOrRecommendation by - collectLastValue(underTest.hasAnyMediaOrRecommendation) - - mediaFilterRepository.setRecommendation(mediaRecommendation) - - assertThat(hasActiveMediaOrRecommendation).isTrue() - assertThat(hasAnyMediaOrRecommendation).isTrue() - - mediaFilterRepository.setRecommendation(mediaRecommendation.copy(isActive = false)) - - assertThat(hasActiveMediaOrRecommendation).isFalse() - assertThat(hasAnyMediaOrRecommendation).isFalse() - } - - @Test - fun addActiveRecommendation_thenInvalid() = - testScope.runTest { - val hasActiveMediaOrRecommendation by - collectLastValue(underTest.hasActiveMediaOrRecommendation) - val hasAnyMediaOrRecommendation by - collectLastValue(underTest.hasAnyMediaOrRecommendation) - - mediaFilterRepository.setRecommendation(mediaRecommendation) - - assertThat(hasActiveMediaOrRecommendation).isTrue() - assertThat(hasAnyMediaOrRecommendation).isTrue() - - mediaFilterRepository.setRecommendation( - mediaRecommendation.copy(recommendations = listOf()) - ) - - assertThat(hasActiveMediaOrRecommendation).isFalse() - assertThat(hasAnyMediaOrRecommendation).isFalse() - } - - @Test fun hasAnyMedia_noMediaSet_returnsFalse() = testScope.runTest { assertThat(underTest.hasAnyMedia()).isFalse() } @@ -208,47 +114,4 @@ class MediaCarouselInteractorTest : SysuiTestCase() { @Test fun hasActiveMediaOrRecommendation_nothingSet_returnsFalse() = testScope.runTest { assertThat(underTest.hasActiveMediaOrRecommendation.value).isFalse() } - - @Test - fun loadMediaFromRec() = - testScope.runTest { - val currentMedia by collectLastValue(underTest.currentMedia) - val instanceId = InstanceId.fakeInstanceId(123) - val data = - MediaData( - 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) - mediaFilterRepository.addSelectedUserMediaEntry(data) - mediaFilterRepository.addMediaDataLoadingState(mediaLoadingModel) - - assertThat(currentMedia) - .containsExactly(MediaCommonModel.MediaRecommendations(smartspaceLoadingModel)) - .inOrder() - - mediaFilterRepository.addSelectedUserMediaEntry(data.copy(isPlaying = true)) - mediaFilterRepository.addMediaDataLoadingState(mediaLoadingModel) - - assertThat(currentMedia) - .containsExactly( - MediaCommonModel.MediaControl(mediaLoadingModel, isMediaFromRec = true), - MediaCommonModel.MediaRecommendations(smartspaceLoadingModel), - ) - .inOrder() - } - - companion object { - private const val KEY_MEDIA_SMARTSPACE = "MEDIA_SMARTSPACE_ID" - private const val PACKAGE_NAME = "com.android.example" - private const val KEY = "key" - private const val SURFACE = 4 - } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaRecommendationsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaRecommendationsInteractorTest.kt deleted file mode 100644 index 2265c0149cc3..000000000000 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaRecommendationsInteractorTest.kt +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.media.controls.domain.interactor - -import android.R -import android.content.ComponentName -import android.content.Intent -import android.content.applicationContext -import android.graphics.drawable.Icon -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SmallTest -import com.android.systemui.SysuiTestCase -import com.android.systemui.animation.Expandable -import com.android.systemui.broadcast.broadcastSender -import com.android.systemui.broadcast.mockBroadcastSender -import com.android.systemui.coroutines.collectLastValue -import com.android.systemui.kosmos.testScope -import com.android.systemui.media.controls.MediaTestHelper -import com.android.systemui.media.controls.domain.pipeline.MediaDataFilterImpl -import com.android.systemui.media.controls.domain.pipeline.interactor.MediaRecommendationsInteractor -import com.android.systemui.media.controls.domain.pipeline.interactor.MediaRecommendationsInteractor.Companion.EXPORTED_SMARTSPACE_TRAMPOLINE_ACTIVITY_NAME -import com.android.systemui.media.controls.domain.pipeline.interactor.mediaRecommendationsInteractor -import com.android.systemui.media.controls.domain.pipeline.mediaDataFilter -import com.android.systemui.media.controls.shared.model.MediaRecModel -import com.android.systemui.media.controls.shared.model.MediaRecommendationsModel -import com.android.systemui.media.controls.shared.model.SmartspaceMediaData -import com.android.systemui.plugins.activityStarter -import com.android.systemui.testKosmos -import com.android.systemui.util.mockito.any -import com.android.systemui.util.mockito.mock -import com.android.systemui.util.mockito.whenever -import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.test.runTest -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mockito.doNothing -import org.mockito.Mockito.spy -import org.mockito.Mockito.verify -import org.mockito.kotlin.eq - -@SmallTest -@RunWith(AndroidJUnit4::class) -class MediaRecommendationsInteractorTest : SysuiTestCase() { - - private val spyContext = spy(context) - private val kosmos = testKosmos().apply { applicationContext = spyContext } - private val testScope = kosmos.testScope - - private val mediaDataFilter: MediaDataFilterImpl = with(kosmos) { mediaDataFilter } - private val activityStarter = kosmos.activityStarter - private val icon: Icon = Icon.createWithResource(context, R.drawable.ic_media_play) - private val smartspaceMediaData: SmartspaceMediaData = - SmartspaceMediaData( - targetId = KEY_MEDIA_SMARTSPACE, - isActive = true, - packageName = PACKAGE_NAME, - recommendations = MediaTestHelper.getValidRecommendationList(icon), - ) - - private val underTest: MediaRecommendationsInteractor = - with(kosmos) { - broadcastSender = mockBroadcastSender - kosmos.mediaRecommendationsInteractor - } - - @Test - fun addRecommendation_smartspaceMediaDataUpdate() = - testScope.runTest { - val recommendations by collectLastValue(underTest.recommendations) - - val model = - MediaRecommendationsModel( - key = KEY_MEDIA_SMARTSPACE, - packageName = PACKAGE_NAME, - areRecommendationsValid = true, - mediaRecs = - listOf( - MediaRecModel(icon = icon), - MediaRecModel(icon = icon), - MediaRecModel(icon = icon), - ), - ) - - mediaDataFilter.onSmartspaceMediaDataLoaded(KEY_MEDIA_SMARTSPACE, smartspaceMediaData) - - assertThat(recommendations).isEqualTo(model) - } - - @Test - fun addInvalidRecommendation() = - testScope.runTest { - val recommendations by collectLastValue(underTest.recommendations) - val inValidData = smartspaceMediaData.copy(recommendations = listOf()) - - mediaDataFilter.onSmartspaceMediaDataLoaded(KEY_MEDIA_SMARTSPACE, smartspaceMediaData) - assertThat(recommendations?.areRecommendationsValid).isTrue() - - mediaDataFilter.onSmartspaceMediaDataLoaded(KEY_MEDIA_SMARTSPACE, inValidData) - assertThat(recommendations?.areRecommendationsValid).isFalse() - assertThat(recommendations?.mediaRecs?.isEmpty()).isTrue() - } - - @Test - fun removeRecommendation_noTrampolineActivity() { - val intent = Intent() - - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - - mediaDataFilter.onSmartspaceMediaDataLoaded(KEY_MEDIA_SMARTSPACE, smartspaceMediaData) - underTest.removeMediaRecommendations(KEY_MEDIA_SMARTSPACE, intent, 0) - - verify(kosmos.mockBroadcastSender).sendBroadcast(eq(intent)) - } - - @Test - fun removeRecommendation_usingTrampolineActivity() { - doNothing().whenever(spyContext).startActivity(any()) - val intent = Intent() - - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - intent.component = ComponentName(PACKAGE_NAME, EXPORTED_SMARTSPACE_TRAMPOLINE_ACTIVITY_NAME) - - underTest.removeMediaRecommendations(KEY_MEDIA_SMARTSPACE, intent, 0) - - verify(spyContext).startActivity(eq(intent)) - } - - @Test - fun startSettings() { - underTest.startSettings() - - verify(activityStarter).startActivity(any(), eq(true)) - } - - @Test - fun startClickIntent() { - doNothing().whenever(spyContext).startActivity(any()) - val intent = Intent() - val expandable = mock<Expandable>() - - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - - mediaDataFilter.onSmartspaceMediaDataLoaded(KEY_MEDIA_SMARTSPACE, smartspaceMediaData) - underTest.startClickIntent(expandable, intent) - - verify(spyContext).startActivity(eq(intent)) - } - - companion object { - private const val KEY_MEDIA_SMARTSPACE = "MEDIA_SMARTSPACE_ID" - private const val PACKAGE_NAME = "com.example.app" - private const val SURFACE = 4 - } -} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/util/MediaDiffUtilTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/util/MediaDiffUtilTest.kt index 005424ba599e..faa62c2febc1 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/util/MediaDiffUtilTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/util/MediaDiffUtilTest.kt @@ -23,7 +23,6 @@ import com.android.internal.logging.InstanceId import com.android.systemui.SysuiTestCase import com.android.systemui.media.controls.ui.viewmodel.MediaCommonViewModel import com.android.systemui.media.controls.ui.viewmodel.mediaControlViewModel -import com.android.systemui.media.controls.ui.viewmodel.mediaRecommendationsViewModel import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import org.junit.Assert.fail @@ -56,25 +55,6 @@ class MediaDiffUtilTest : SysuiTestCase() { } @Test - fun newMediaRecommendationsAdded() { - val mediaRecs = createMediaRecommendations(KEY_MEDIA_SMARTSPACE, true) - val oldList = listOf<MediaCommonViewModel>() - val newList = listOf(mediaRecs) - val mediaLoadedCallback = MediaViewModelCallback(oldList, newList) - val mediaLoadedListUpdateCallback = - MediaViewModelListUpdateCallback( - oldList, - newList, - { commonViewModel, _ -> assertThat(commonViewModel).isEqualTo(mediaRecs) }, - { commonViewModel, _ -> fail("Unexpected to update $commonViewModel") }, - { fail("Unexpected to remove $it") }, - { commonViewModel, _, _ -> fail("Unexpected to move $commonViewModel ") }, - ) - - DiffUtil.calculateDiff(mediaLoadedCallback).dispatchUpdatesTo(mediaLoadedListUpdateCallback) - } - - @Test fun updateMediaControl_contentChanged() { val mediaControl = createMediaControl(InstanceId.fakeInstanceId(123), true) val oldList = listOf(mediaControl) @@ -94,25 +74,6 @@ class MediaDiffUtilTest : SysuiTestCase() { } @Test - fun updateMediaRecommendations_contentChanged() { - val mediaRecs = createMediaRecommendations(KEY_MEDIA_SMARTSPACE, true) - val oldList = listOf(mediaRecs) - val newList = listOf(mediaRecs.copy(key = KEY_MEDIA_SMARTSPACE_2)) - val mediaLoadedCallback = MediaViewModelCallback(oldList, newList) - val mediaLoadedListUpdateCallback = - MediaViewModelListUpdateCallback( - oldList, - newList, - { commonViewModel, _ -> fail("Unexpected to add $commonViewModel") }, - { commonViewModel, _ -> assertThat(commonViewModel).isNotEqualTo(mediaRecs) }, - { fail("Unexpected to remove $it") }, - { commonViewModel, _, _ -> fail("Unexpected to move $commonViewModel ") }, - ) - - DiffUtil.calculateDiff(mediaLoadedCallback).dispatchUpdatesTo(mediaLoadedListUpdateCallback) - } - - @Test fun mediaControlMoved() { val mediaControl1 = createMediaControl(InstanceId.fakeInstanceId(123), true) val mediaControl2 = createMediaControl(InstanceId.fakeInstanceId(456), false) @@ -133,27 +94,6 @@ class MediaDiffUtilTest : SysuiTestCase() { } @Test - fun mediaRecommendationsMoved() { - val mediaControl1 = createMediaControl(InstanceId.fakeInstanceId(123), true) - val mediaControl2 = createMediaControl(InstanceId.fakeInstanceId(456), false) - val mediaRecs = createMediaRecommendations(KEY_MEDIA_SMARTSPACE, true) - val oldList = listOf(mediaRecs, mediaControl1, mediaControl2) - val newList = listOf(mediaControl1, mediaControl2, mediaRecs) - val mediaLoadedCallback = MediaViewModelCallback(oldList, newList) - val mediaLoadedListUpdateCallback = - MediaViewModelListUpdateCallback( - oldList, - newList, - { commonViewModel, _ -> fail("Unexpected to add $commonViewModel") }, - { commonViewModel, _ -> fail("Unexpected to update $commonViewModel") }, - { fail("Unexpected to remove $it") }, - { commonViewModel, _, _ -> assertThat(commonViewModel).isEqualTo(mediaRecs) }, - ) - - DiffUtil.calculateDiff(mediaLoadedCallback).dispatchUpdatesTo(mediaLoadedListUpdateCallback) - } - - @Test fun mediaControlRemoved() { val mediaControl = createMediaControl(InstanceId.fakeInstanceId(123), true) val oldList = listOf(mediaControl) @@ -172,25 +112,6 @@ class MediaDiffUtilTest : SysuiTestCase() { DiffUtil.calculateDiff(mediaLoadedCallback).dispatchUpdatesTo(mediaLoadedListUpdateCallback) } - @Test - fun mediaRecommendationsRemoved() { - val mediaRecs = createMediaRecommendations(KEY_MEDIA_SMARTSPACE_2, false) - val oldList = listOf(mediaRecs) - val newList = listOf<MediaCommonViewModel>() - val mediaLoadedCallback = MediaViewModelCallback(oldList, newList) - val mediaLoadedListUpdateCallback = - MediaViewModelListUpdateCallback( - oldList, - newList, - { commonViewModel, _ -> fail("Unexpected to add $commonViewModel") }, - { commonViewModel, _ -> fail("Unexpected to update $commonViewModel") }, - { commonViewModel -> assertThat(commonViewModel).isEqualTo(mediaRecs) }, - { commonViewModel, _, _ -> fail("Unexpected to move $commonViewModel ") }, - ) - - DiffUtil.calculateDiff(mediaLoadedCallback).dispatchUpdatesTo(mediaLoadedListUpdateCallback) - } - private fun createMediaControl( instanceId: InstanceId, immediatelyUpdateUi: Boolean, @@ -201,26 +122,7 @@ class MediaDiffUtilTest : SysuiTestCase() { controlViewModel = kosmos.mediaControlViewModel, onAdded = {}, onRemoved = {}, - onUpdated = {} - ) - } - - private fun createMediaRecommendations( - key: String, - loadingEnabled: Boolean, - ): MediaCommonViewModel.MediaRecommendations { - return MediaCommonViewModel.MediaRecommendations( - key = key, - loadingEnabled = loadingEnabled, - recsViewModel = kosmos.mediaRecommendationsViewModel, - onAdded = {}, - onRemoved = {}, - onUpdated = {} + onUpdated = {}, ) } - - companion object { - private const val KEY_MEDIA_SMARTSPACE = "MEDIA_SMARTSPACE_ID" - private const val KEY_MEDIA_SMARTSPACE_2 = "MEDIA_SMARTSPACE_ID_2" - } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelTest.kt index fb5bbf452cfa..e56b114dc847 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelTest.kt @@ -19,23 +19,18 @@ package com.android.systemui.media.controls.ui.viewmodel import android.R import android.content.packageManager import android.content.pm.ApplicationInfo -import android.graphics.drawable.Icon 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.MediaTestHelper import com.android.systemui.media.controls.domain.pipeline.MediaDataFilterImpl import com.android.systemui.media.controls.domain.pipeline.interactor.mediaCarouselInteractor -import com.android.systemui.media.controls.domain.pipeline.interactor.mediaRecommendationsInteractor import com.android.systemui.media.controls.domain.pipeline.mediaDataFilter import com.android.systemui.media.controls.shared.mediaLogger import com.android.systemui.media.controls.shared.mockMediaLogger import com.android.systemui.media.controls.shared.model.MediaData -import com.android.systemui.media.controls.shared.model.SmartspaceMediaData -import com.android.systemui.statusbar.notification.collection.provider.visualStabilityProvider import com.android.systemui.statusbar.notificationLockscreenUserManager import com.android.systemui.testKosmos import com.android.systemui.util.mockito.any @@ -62,15 +57,7 @@ class MediaCarouselViewModelTest : SysuiTestCase() { private val mediaDataFilter: MediaDataFilterImpl = kosmos.mediaDataFilter private val notificationLockscreenUserManager = kosmos.notificationLockscreenUserManager private val packageManager = kosmos.packageManager - private val icon = Icon.createWithResource(context, R.drawable.ic_media_play) private val drawable = context.getDrawable(R.drawable.ic_media_play) - private val smartspaceMediaData: SmartspaceMediaData = - SmartspaceMediaData( - targetId = KEY_MEDIA_SMARTSPACE, - isActive = true, - packageName = PACKAGE_NAME, - recommendations = MediaTestHelper.getValidRecommendationList(icon), - ) private val underTest: MediaCarouselViewModel = kosmos.mediaCarouselViewModel @@ -121,53 +108,6 @@ class MediaCarouselViewModelTest : SysuiTestCase() { } @Test - fun loadMediaControlsAndRecommendations_mediaItemsAreUpdated() = - testScope.runTest { - val sortedMedia by collectLastValue(underTest.mediaItems) - val instanceId1 = InstanceId.fakeInstanceId(123) - val instanceId2 = InstanceId.fakeInstanceId(456) - - loadMediaControl(KEY, instanceId1) - loadMediaControl(KEY_2, instanceId2) - loadMediaRecommendations() - - val firstMediaControl = sortedMedia?.get(0) as MediaCommonViewModel.MediaControl - val secondMediaControl = sortedMedia?.get(1) as MediaCommonViewModel.MediaControl - val recsCard = sortedMedia?.get(2) as MediaCommonViewModel.MediaRecommendations - assertThat(firstMediaControl.instanceId).isEqualTo(instanceId2) - assertThat(secondMediaControl.instanceId).isEqualTo(instanceId1) - assertThat(recsCard.key).isEqualTo(KEY_MEDIA_SMARTSPACE) - } - - @Test - fun recommendationClicked_switchToPlayer() = - testScope.runTest { - val sortedMedia by collectLastValue(underTest.mediaItems) - kosmos.visualStabilityProvider.isReorderingAllowed = false - val instanceId = InstanceId.fakeInstanceId(123) - - loadMediaRecommendations() - kosmos.mediaRecommendationsInteractor.switchToMediaControl(PACKAGE_NAME) - - var recsCard = sortedMedia?.get(0) as MediaCommonViewModel.MediaRecommendations - assertThat(sortedMedia).hasSize(1) - assertThat(recsCard.key).isEqualTo(KEY_MEDIA_SMARTSPACE) - - loadMediaControl(KEY, instanceId, false) - - recsCard = sortedMedia?.get(0) as MediaCommonViewModel.MediaRecommendations - assertThat(sortedMedia).hasSize(1) - assertThat(recsCard.key).isEqualTo(KEY_MEDIA_SMARTSPACE) - - loadMediaControl(KEY, instanceId, true) - - val mediaControl = sortedMedia?.get(0) as MediaCommonViewModel.MediaControl - assertThat(sortedMedia).hasSize(2) - assertThat(mediaControl.instanceId).isEqualTo(instanceId) - assertThat(mediaControl.isMediaFromRec).isTrue() - } - - @Test fun addMediaControlThenRemove_mediaEventsAreLogged() = testScope.runTest { val sortedMedia by collectLastValue(underTest.mediaItems) @@ -199,31 +139,6 @@ class MediaCarouselViewModelTest : SysuiTestCase() { verify(kosmos.mediaLogger).logMediaCardRemoved(eq(instanceId)) } - @Test - fun addMediaRecommendationThenRemove_mediaEventsAreLogged() = - testScope.runTest { - val sortedMedia by collectLastValue(underTest.mediaItems) - - loadMediaRecommendations() - - val mediaRecommendations = - sortedMedia?.get(0) as MediaCommonViewModel.MediaRecommendations - assertThat(mediaRecommendations.key).isEqualTo(KEY_MEDIA_SMARTSPACE) - - // when media recommendation is added to carousel - mediaRecommendations.onAdded(mediaRecommendations) - - verify(kosmos.mediaLogger).logMediaRecommendationCardAdded(eq(KEY_MEDIA_SMARTSPACE)) - - mediaDataFilter.onSmartspaceMediaDataRemoved(KEY, true) - assertThat(sortedMedia).isEmpty() - - // when media recommendation is removed from carousel - mediaRecommendations.onRemoved(true) - - verify(kosmos.mediaLogger).logMediaRecommendationCardRemoved(eq(KEY_MEDIA_SMARTSPACE)) - } - private fun loadMediaControl(key: String, instanceId: InstanceId, isPlaying: Boolean = true) { whenever(notificationLockscreenUserManager.isCurrentProfile(USER_ID)).thenReturn(true) whenever(notificationLockscreenUserManager.isProfileAvailable(USER_ID)).thenReturn(true) @@ -239,15 +154,10 @@ class MediaCarouselViewModelTest : SysuiTestCase() { mediaDataFilter.onMediaDataLoaded(key, key, mediaData) } - private fun loadMediaRecommendations(key: String = KEY_MEDIA_SMARTSPACE) { - mediaDataFilter.onSmartspaceMediaDataLoaded(key, smartspaceMediaData) - } - companion object { private const val USER_ID = 0 private const val KEY = "key" private const val KEY_2 = "key2" private const val PACKAGE_NAME = "com.example.app" - private const val KEY_MEDIA_SMARTSPACE = "MEDIA_SMARTSPACE_ID" } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModelTest.kt deleted file mode 100644 index 51b1911be5d5..000000000000 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModelTest.kt +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.media.controls.ui.viewmodel - -import android.R -import android.content.packageManager -import android.content.pm.ApplicationInfo -import android.graphics.drawable.Icon -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SmallTest -import com.android.systemui.SysuiTestCase -import com.android.systemui.coroutines.collectLastValue -import com.android.systemui.kosmos.testScope -import com.android.systemui.media.controls.MediaTestHelper -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.SmartspaceMediaData -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.Test -import org.junit.runner.RunWith -import org.mockito.ArgumentMatchers -import org.mockito.Mockito - -@SmallTest -@RunWith(AndroidJUnit4::class) -class MediaRecommendationsViewModelTest : SysuiTestCase() { - - private val kosmos = testKosmos() - private val testScope = kosmos.testScope - - private val mediaDataFilter: MediaDataFilterImpl = kosmos.mediaDataFilter - private val packageManager = kosmos.packageManager - private val icon: Icon = Icon.createWithResource(context, R.drawable.ic_media_play) - private val drawable = context.getDrawable(R.drawable.ic_media_play) - private val smartspaceMediaData: SmartspaceMediaData = - SmartspaceMediaData( - targetId = KEY_MEDIA_SMARTSPACE, - isActive = true, - packageName = PACKAGE_NAME, - recommendations = MediaTestHelper.getValidRecommendationList(icon), - ) - - private val underTest: MediaRecommendationsViewModel = kosmos.mediaRecommendationsViewModel - - @Test - fun loadRecommendations_recsCardViewModelIsLoaded() = - testScope.runTest { - whenever(packageManager.getApplicationIcon(Mockito.anyString())).thenReturn(drawable) - whenever(packageManager.getApplicationIcon(any(ApplicationInfo::class.java))) - .thenReturn(drawable) - whenever(packageManager.getApplicationInfo(eq(PACKAGE_NAME), ArgumentMatchers.anyInt())) - .thenReturn(ApplicationInfo()) - whenever(packageManager.getApplicationLabel(any())).thenReturn(PACKAGE_NAME) - val recsCardViewModel by collectLastValue(underTest.mediaRecsCard) - - context.setMockPackageManager(packageManager) - - mediaDataFilter.onSmartspaceMediaDataLoaded(KEY_MEDIA_SMARTSPACE, smartspaceMediaData) - - assertThat(recsCardViewModel).isNotNull() - assertThat(recsCardViewModel?.mediaRecs?.size) - .isEqualTo(smartspaceMediaData.recommendations.size) - } - - companion object { - private const val KEY_MEDIA_SMARTSPACE = "MEDIA_SMARTSPACE_ID" - private const val PACKAGE_NAME = "com.example.app" - } -} diff --git a/packages/SystemUI/res-keyguard/drawable/qs_media_recommendation_bg_gradient.xml b/packages/SystemUI/res-keyguard/drawable/qs_media_recommendation_bg_gradient.xml deleted file mode 100644 index 495fbb893eac..000000000000 --- a/packages/SystemUI/res-keyguard/drawable/qs_media_recommendation_bg_gradient.xml +++ /dev/null @@ -1,26 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2021 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<shape - xmlns:android="http://schemas.android.com/apk/res/android" - android:shape="rectangle"> - <corners android:radius="24dp"/> - <gradient - android:angle="0" - android:startColor="#00000000" - android:endColor="#ff000000" - android:type="linear" /> -</shape> diff --git a/packages/SystemUI/res/drawable/qs_media_rec_scrim.xml b/packages/SystemUI/res/drawable/qs_media_rec_scrim.xml deleted file mode 100644 index de0a6201cb09..000000000000 --- a/packages/SystemUI/res/drawable/qs_media_rec_scrim.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2023 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License - --> -<shape xmlns:android="http://schemas.android.com/apk/res/android" - android:shape="rectangle"> - <!-- gradient from 25% in the center to 100% at edges --> - <gradient - android:type="radial" - android:gradientRadius="40%p" - android:startColor="#AE000000" - android:endColor="#00000000" /> -</shape>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/media_recommendation_view.xml b/packages/SystemUI/res/layout/media_recommendation_view.xml deleted file mode 100644 index e63aa211f9f1..000000000000 --- a/packages/SystemUI/res/layout/media_recommendation_view.xml +++ /dev/null @@ -1,90 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2023 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License - --> -<!-- Layout for media recommendation item inside QSPanel carousel --> -<merge xmlns:android="http://schemas.android.com/apk/res/android"> - <!-- Album cover --> - <ImageView - android:id="@+id/media_cover" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:translationZ="0dp" - android:scaleType="matrix" - android:adjustViewBounds="true" - android:clipToOutline="true" - android:layerType="hardware" - android:background="@drawable/bg_smartspace_media_item"/> - - <!-- App icon --> - <com.android.internal.widget.CachingIconView - android:id="@+id/media_rec_app_icon" - android:layout_width="@dimen/qs_media_rec_album_icon_size" - android:layout_height="@dimen/qs_media_rec_album_icon_size" - android:minWidth="@dimen/qs_media_rec_album_icon_size" - android:minHeight="@dimen/qs_media_rec_album_icon_size" - android:layout_marginStart="@dimen/qs_media_info_spacing" - android:layout_marginTop="@dimen/qs_media_info_spacing"/> - - <!-- Artist name --> - <TextView - android:id="@+id/media_title" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginStart="@dimen/qs_media_info_spacing" - android:layout_marginEnd="@dimen/qs_media_info_spacing" - android:layout_marginBottom="@dimen/qs_media_rec_album_title_bottom_margin" - android:fontFamily="@*android:string/config_headlineFontFamilyMedium" - android:singleLine="true" - android:textSize="12sp" - android:gravity="top" - android:layout_gravity="bottom" - android:importantForAccessibility="no"/> - - <!-- Album name --> - <TextView - android:id="@+id/media_subtitle" - android:layout_width="match_parent" - android:layout_height="@dimen/qs_media_rec_album_subtitle_height" - android:layout_marginEnd="@dimen/qs_media_info_spacing" - android:layout_marginStart="@dimen/qs_media_info_spacing" - android:layout_marginBottom="@dimen/qs_media_info_spacing" - android:fontFamily="@*android:string/config_headlineFontFamily" - android:singleLine="true" - android:textSize="11sp" - android:gravity="center_vertical" - android:layout_gravity="bottom" - android:importantForAccessibility="no"/> - - <!-- Seek Bar --> - <SeekBar - android:id="@+id/media_progress_bar" - android:layout_width="match_parent" - android:layout_height="12dp" - android:layout_gravity="bottom" - android:maxHeight="@dimen/qs_media_enabled_seekbar_height" - android:thumb="@android:color/transparent" - android:splitTrack="false" - android:clickable="false" - android:progressTint="?android:attr/textColorPrimary" - android:progressBackgroundTint="?android:attr/textColorTertiary" - android:paddingTop="5dp" - android:paddingBottom="5dp" - android:paddingStart="0dp" - android:paddingEnd="0dp" - android:layout_marginEnd="@dimen/qs_media_info_spacing" - android:layout_marginStart="@dimen/qs_media_info_spacing" - android:layout_marginBottom="@dimen/qs_media_info_spacing"/> -</merge>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/media_recommendations.xml b/packages/SystemUI/res/layout/media_recommendations.xml deleted file mode 100644 index 65fc19c5b2a4..000000000000 --- a/packages/SystemUI/res/layout/media_recommendations.xml +++ /dev/null @@ -1,75 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2023 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License - --> - -<!-- Layout for media recommendations inside QSPanel carousel --> -<com.android.systemui.util.animation.TransitionLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/media_recommendations_updated" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:clipChildren="false" - android:clipToPadding="false" - android:forceHasOverlappingRendering="false" - android:background="@drawable/qs_media_background" - android:theme="@style/MediaPlayer"> - - <!-- This view just ensures the full media player is a certain height. --> - <View - android:id="@+id/sizing_view" - android:layout_width="match_parent" - android:layout_height="@dimen/qs_media_session_height_expanded" /> - - <TextView - android:id="@+id/media_rec_title" - style="@style/MediaPlayer.Recommendation.Header" - android:text="@string/controls_media_smartspace_rec_header"/> - - <FrameLayout - android:id="@+id/media_cover1_container" - style="@style/MediaPlayer.Recommendation.AlbumContainer.Updated" - > - - <include - layout="@layout/media_recommendation_view"/> - - </FrameLayout> - - - <FrameLayout - android:id="@+id/media_cover2_container" - style="@style/MediaPlayer.Recommendation.AlbumContainer.Updated" - > - - <include - layout="@layout/media_recommendation_view"/> - - </FrameLayout> - - <FrameLayout - android:id="@+id/media_cover3_container" - style="@style/MediaPlayer.Recommendation.AlbumContainer.Updated" - > - - <include - layout="@layout/media_recommendation_view"/> - - </FrameLayout> - - <include - layout="@layout/media_long_press_menu" /> - -</com.android.systemui.util.animation.TransitionLayout> diff --git a/packages/SystemUI/res/values-sw720dp-land/dimens.xml b/packages/SystemUI/res/values-sw720dp-land/dimens.xml index 41bb37efa623..f4f0424ade98 100644 --- a/packages/SystemUI/res/values-sw720dp-land/dimens.xml +++ b/packages/SystemUI/res/values-sw720dp-land/dimens.xml @@ -30,11 +30,6 @@ not appear immediately after user swipes to the side --> <dimen name="qs_tiles_page_horizontal_margin">20dp</dimen> - <!-- Size of Smartspace media recommendations cards in the QSPanel carousel --> - <dimen name="qs_media_rec_icon_top_margin">16dp</dimen> - <dimen name="qs_media_rec_album_size">112dp</dimen> - <dimen name="qs_media_rec_album_side_margin">16dp</dimen> - <dimen name="controls_panel_corner_radius">40dp</dimen> <dimen name="lockscreen_shade_max_over_scroll_amount">42dp</dimen> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 4995858f95a4..78e719f6289a 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -98,10 +98,6 @@ TODO (b/293252410) - change this comment/resource when flag is enabled --> <integer name="small_land_lockscreen_quick_settings_max_rows">2</integer> - <!-- If the dp width of the available space is <= this value, potentially adjust the number - of media recommendation items--> - <integer name="default_qs_media_rec_width_dp">380</integer> - <!-- The number of columns that the top level tiles span in the QuickSettings --> <!-- The default tiles to display in QuickSettings --> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 7c370d3bc064..c8e31079fca0 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1342,19 +1342,6 @@ <dimen name="qs_media_session_collapsed_legacy_guideline">144dp</dimen> <dimen name="qs_media_session_collapsed_guideline">168dp</dimen> - <!-- Size of Smartspace media recommendations cards in the QSPanel carousel --> - <dimen name="qs_media_rec_default_width">380dp</dimen> - <dimen name="qs_media_rec_icon_top_margin">16dp</dimen> - <dimen name="qs_media_rec_album_icon_size">16dp</dimen> - <dimen name="qs_media_rec_album_size">88dp</dimen> - <dimen name="qs_media_rec_album_width">110dp</dimen> - <dimen name="qs_media_rec_album_height_expanded">108dp</dimen> - <dimen name="qs_media_rec_album_height_collapsed">77dp</dimen> - <dimen name="qs_media_rec_album_side_margin">16dp</dimen> - <dimen name="qs_media_rec_album_bottom_margin">8dp</dimen> - <dimen name="qs_media_rec_album_title_bottom_margin">22dp</dimen> - <dimen name="qs_media_rec_album_subtitle_height">12dp</dimen> - <!-- Chipbar --> <!-- (Used for media tap-to-transfer chip for sender device and active unlock) --> <dimen name="chipbar_outer_padding">16dp</dimen> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 4431ddadc8de..7895ff7b90f6 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -895,57 +895,6 @@ <item name="android:textColor">@android:color/system_on_primary_dark</item> </style> - <style name="MediaPlayer.Recommendation"/> - - <style name="MediaPlayer.Recommendation.Header"> - <item name="android:layout_width">wrap_content</item> - <item name="android:layout_height">wrap_content</item> - <item name="android:layout_marginTop">@dimen/qs_media_padding</item> - <item name="android:layout_marginStart">@dimen/qs_media_padding</item> - <item name="android:fontFamily">=@*android:string/config_headlineFontFamilyMedium</item> - <item name="android:singleLine">true</item> - <item name="android:textSize">14sp</item> - <item name="android:textColor">?android:attr/textColorPrimary</item> - </style> - - <style name="MediaPlayer.Recommendation.AlbumContainer"> - <item name="android:layout_width">@dimen/qs_media_rec_album_size</item> - <item name="android:layout_height">@dimen/qs_media_rec_album_size</item> - <item name="android:background">@drawable/qs_media_light_source</item> - <item name="android:layout_marginTop">@dimen/qs_media_padding</item> - <item name="android:layout_marginBottom">@dimen/qs_media_rec_album_bottom_margin</item> - </style> - - <style name="MediaPlayer.Recommendation.AlbumContainer.Updated"> - <item name="android:layout_width">@dimen/qs_media_rec_album_width</item> - <item name="android:minWidth">@dimen/qs_media_rec_album_width</item> - <item name="android:minHeight">@dimen/qs_media_rec_album_height_collapsed</item> - <item name="android:background">@drawable/qs_media_light_source</item> - <item name="android:layout_marginTop">@dimen/qs_media_info_spacing</item> - </style> - - <style name="MediaPlayer.Recommendation.Album"> - <item name="android:backgroundTint">@color/media_player_album_bg</item> - </style> - - <style name="MediaPlayer.Recommendation.Text"> - <item name="android:layout_width">@dimen/qs_media_rec_album_size</item> - <item name="android:layout_height">wrap_content</item> - <item name="android:maxLines">1</item> - <item name="android:ellipsize">end</item> - <item name="android:textSize">14sp</item> - <item name="android:gravity">start</item> - </style> - - <style name="MediaPlayer.Recommendation.Text.Title"> - <item name="android:textColor">?android:attr/textColorPrimary</item> - </style> - - <style name="MediaPlayer.Recommendation.Text.Subtitle"> - <item name="android:textColor">?android:attr/textColorSecondary</item> - </style> - - <!-- Used to style charging animation AVD animation --> <style name="ChargingAnim" /> diff --git a/packages/SystemUI/res/xml/media_recommendations_collapsed.xml b/packages/SystemUI/res/xml/media_recommendations_collapsed.xml deleted file mode 100644 index d3be3c7de5ad..000000000000 --- a/packages/SystemUI/res/xml/media_recommendations_collapsed.xml +++ /dev/null @@ -1,64 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2023 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License - --> -<ConstraintSet - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" - > - - <Constraint - android:id="@+id/sizing_view" - android:layout_width="match_parent" - android:layout_height="@dimen/qs_media_session_height_collapsed" - /> - - <Constraint - android:id="@+id/media_rec_title" - style="@style/MediaPlayer.Recommendation.Header" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent"/> - - <Constraint - android:id="@+id/media_cover1_container" - style="@style/MediaPlayer.Recommendation.AlbumContainer.Updated" - android:layout_height="@dimen/qs_media_rec_album_height_collapsed" - android:layout_marginEnd="@dimen/qs_media_info_spacing" - android:layout_marginStart="@dimen/qs_media_padding" - app:layout_constraintTop_toBottomOf="@+id/media_rec_title" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintEnd_toStartOf="@id/media_cover2_container"/> - - - <Constraint - android:id="@+id/media_cover2_container" - style="@style/MediaPlayer.Recommendation.AlbumContainer.Updated" - android:layout_height="@dimen/qs_media_rec_album_height_collapsed" - android:layout_marginEnd="@dimen/qs_media_info_spacing" - app:layout_constraintTop_toBottomOf="@+id/media_rec_title" - app:layout_constraintStart_toEndOf="@id/media_cover1_container" - app:layout_constraintEnd_toStartOf="@id/media_cover3_container"/> - - <Constraint - android:id="@+id/media_cover3_container" - style="@style/MediaPlayer.Recommendation.AlbumContainer.Updated" - android:layout_height="@dimen/qs_media_rec_album_height_collapsed" - android:layout_marginEnd="@dimen/qs_media_padding" - app:layout_constraintTop_toBottomOf="@+id/media_rec_title" - app:layout_constraintStart_toEndOf="@id/media_cover2_container" - app:layout_constraintEnd_toEndOf="parent"/> - - -</ConstraintSet> diff --git a/packages/SystemUI/res/xml/media_recommendations_expanded.xml b/packages/SystemUI/res/xml/media_recommendations_expanded.xml deleted file mode 100644 index 88c70552e9e8..000000000000 --- a/packages/SystemUI/res/xml/media_recommendations_expanded.xml +++ /dev/null @@ -1,71 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2023 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License - --> -<ConstraintSet - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" - > - - <Constraint - android:id="@+id/sizing_view" - android:layout_width="match_parent" - android:layout_height="@dimen/qs_media_session_height_expanded" - /> - - <Constraint - android:id="@+id/media_rec_title" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="@dimen/qs_media_padding" - android:layout_marginStart="@dimen/qs_media_padding" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" - android:fontFamily="@*android:string/config_headlineFontFamilyMedium" - android:singleLine="true" - android:textSize="14sp" - android:textColor="@color/notification_primary_text_color"/> - - <Constraint - android:id="@+id/media_cover1_container" - style="@style/MediaPlayer.Recommendation.AlbumContainer.Updated" - android:layout_height="@dimen/qs_media_rec_album_height_expanded" - android:layout_marginEnd="@dimen/qs_media_info_spacing" - android:layout_marginStart="@dimen/qs_media_padding" - app:layout_constraintTop_toBottomOf="@+id/media_rec_title" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintEnd_toStartOf="@id/media_cover2_container"/> - - - <Constraint - android:id="@+id/media_cover2_container" - style="@style/MediaPlayer.Recommendation.AlbumContainer.Updated" - android:layout_height="@dimen/qs_media_rec_album_height_expanded" - android:layout_marginEnd="@dimen/qs_media_info_spacing" - app:layout_constraintTop_toBottomOf="@+id/media_rec_title" - app:layout_constraintStart_toEndOf="@id/media_cover1_container" - app:layout_constraintEnd_toStartOf="@id/media_cover3_container"/> - - <Constraint - android:id="@+id/media_cover3_container" - style="@style/MediaPlayer.Recommendation.AlbumContainer.Updated" - android:layout_height="@dimen/qs_media_rec_album_height_expanded" - android:layout_marginEnd="@dimen/qs_media_padding" - app:layout_constraintTop_toBottomOf="@+id/media_rec_title" - app:layout_constraintStart_toEndOf="@id/media_cover2_container" - app:layout_constraintEnd_toEndOf="parent"/> - - -</ConstraintSet> diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaRecommendationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaRecommendationsInteractor.kt deleted file mode 100644 index 0cb36edfd382..000000000000 --- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaRecommendationsInteractor.kt +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.media.controls.domain.pipeline.interactor - -import android.content.Context -import android.content.Intent -import android.provider.Settings -import android.util.Log -import androidx.annotation.VisibleForTesting -import com.android.internal.jank.InteractionJankMonitor -import com.android.systemui.animation.Expandable -import com.android.systemui.broadcast.BroadcastSender -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.media.controls.data.repository.MediaFilterRepository -import com.android.systemui.media.controls.domain.pipeline.MediaDataProcessor -import com.android.systemui.media.controls.shared.model.MediaRecModel -import com.android.systemui.media.controls.shared.model.MediaRecommendationsModel -import com.android.systemui.media.controls.shared.model.SmartspaceMediaData -import com.android.systemui.plugins.ActivityStarter -import java.net.URISyntaxException -import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.stateIn - -/** Encapsulates business logic for media recommendation */ -@SysUISingleton -class MediaRecommendationsInteractor -@Inject -constructor( - @Application applicationScope: CoroutineScope, - @Application private val applicationContext: Context, - private val repository: MediaFilterRepository, - private val mediaDataProcessor: MediaDataProcessor, - private val broadcastSender: BroadcastSender, - private val activityStarter: ActivityStarter, -) { - - val recommendations: Flow<MediaRecommendationsModel> = - repository.smartspaceMediaData.map { toRecommendationsModel(it) }.distinctUntilChanged() - - /** Indicates whether the recommendations card is active. */ - val isActive: StateFlow<Boolean> = - repository.smartspaceMediaData - .map { it.isActive } - .distinctUntilChanged() - .stateIn(applicationScope, SharingStarted.WhileSubscribed(), false) - - fun removeMediaRecommendations(key: String, dismissIntent: Intent?, delayMs: Long) { - mediaDataProcessor.dismissSmartspaceRecommendation(key, delayMs) - if (dismissIntent == null) { - Log.w(TAG, "Cannot create dismiss action click action: extras missing dismiss_intent.") - return - } - - val className = dismissIntent.component?.className - if (className == EXPORTED_SMARTSPACE_TRAMPOLINE_ACTIVITY_NAME) { - // Dismiss the card Smartspace data through Smartspace trampoline activity. - applicationContext.startActivity(dismissIntent) - } else { - broadcastSender.sendBroadcast(dismissIntent) - } - } - - fun startSettings() { - activityStarter.startActivity(SETTINGS_INTENT, /* dismissShade= */ true) - } - - fun startClickIntent(expandable: Expandable, intent: Intent) { - if (shouldActivityOpenInForeground(intent)) { - // Request to unlock the device if the activity needs to be opened in foreground. - activityStarter.postStartActivityDismissingKeyguard( - intent, - 0 /* delay */, - expandable.activityTransitionController( - InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER - ), - ) - } else { - // Otherwise, open the activity in background directly. - applicationContext.startActivity(intent) - } - } - - /** Returns if the action will open the activity in foreground. */ - private fun shouldActivityOpenInForeground(intent: Intent): Boolean { - val intentString = intent.extras?.getString(EXTRAS_SMARTSPACE_INTENT) ?: return false - try { - val wrapperIntent = Intent.parseUri(intentString, Intent.URI_INTENT_SCHEME) - return wrapperIntent.getBooleanExtra(KEY_SMARTSPACE_OPEN_IN_FOREGROUND, false) - } catch (e: URISyntaxException) { - Log.wtf(TAG, "Failed to create intent from URI: $intentString") - e.printStackTrace() - } - return false - } - - private fun toRecommendationsModel(data: SmartspaceMediaData): MediaRecommendationsModel { - val mediaRecs = ArrayList<MediaRecModel>() - data.recommendations.forEach { - with(it) { mediaRecs.add(MediaRecModel(intent, title, subtitle, icon, extras)) } - } - return with(data) { - MediaRecommendationsModel( - key = targetId, - uid = getUid(applicationContext), - packageName = packageName, - instanceId = instanceId, - appName = getAppName(applicationContext), - dismissIntent = dismissIntent, - areRecommendationsValid = isValid(), - mediaRecs = mediaRecs, - ) - } - } - - fun switchToMediaControl(packageName: String) { - repository.setMediaFromRecPackageName(packageName) - } - - companion object { - - private const val TAG = "MediaRecommendationsInteractor" - - // TODO (b/237284176) : move AGSA reference out. - private const val EXTRAS_SMARTSPACE_INTENT = - "com.google.android.apps.gsa.smartspace.extra.SMARTSPACE_INTENT" - @VisibleForTesting - const val EXPORTED_SMARTSPACE_TRAMPOLINE_ACTIVITY_NAME = - "com.google.android.apps.gsa.staticplugins.opa.smartspace." + - "ExportedSmartspaceTrampolineActivity" - - private const val KEY_SMARTSPACE_OPEN_IN_FOREGROUND = "KEY_OPEN_IN_FOREGROUND" - - private val SETTINGS_INTENT = Intent(Settings.ACTION_MEDIA_CONTROLS_SETTINGS) - } -} diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaRecommendationsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaRecommendationsViewBinder.kt deleted file mode 100644 index 4877d18de7ab..000000000000 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaRecommendationsViewBinder.kt +++ /dev/null @@ -1,469 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.media.controls.ui.binder - -import android.app.WallpaperColors -import android.content.Context -import android.content.res.ColorStateList -import android.content.res.Configuration -import android.graphics.Bitmap -import android.graphics.Color -import android.graphics.Matrix -import android.graphics.drawable.BitmapDrawable -import android.graphics.drawable.ColorDrawable -import android.graphics.drawable.Drawable -import android.graphics.drawable.GradientDrawable -import android.graphics.drawable.Icon -import android.graphics.drawable.LayerDrawable -import android.os.Trace -import android.util.TypedValue -import android.view.View -import android.view.ViewGroup -import androidx.appcompat.content.res.AppCompatResources -import androidx.constraintlayout.widget.ConstraintSet -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle -import com.android.systemui.animation.Expandable -import com.android.systemui.lifecycle.repeatWhenAttached -import com.android.systemui.media.controls.shared.model.NUM_REQUIRED_RECOMMENDATIONS -import com.android.systemui.media.controls.ui.animation.surfaceFromScheme -import com.android.systemui.media.controls.ui.animation.textPrimaryFromScheme -import com.android.systemui.media.controls.ui.animation.textSecondaryFromScheme -import com.android.systemui.media.controls.ui.controller.MediaViewController -import com.android.systemui.media.controls.ui.util.MediaArtworkHelper -import com.android.systemui.media.controls.ui.view.RecommendationViewHolder -import com.android.systemui.media.controls.ui.viewmodel.MediaRecViewModel -import com.android.systemui.media.controls.ui.viewmodel.MediaRecommendationsViewModel -import com.android.systemui.media.controls.ui.viewmodel.MediaRecsCardViewModel -import com.android.systemui.monet.ColorScheme -import com.android.systemui.monet.Style -import com.android.systemui.plugins.FalsingManager -import com.android.systemui.res.R -import com.android.systemui.util.animation.TransitionLayout -import kotlin.math.min -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.flow.collectLatest -import com.android.app.tracing.coroutines.launchTraced as launch -import kotlinx.coroutines.withContext - -private const val TAG = "MediaRecommendationsViewBinder" -private const val MEDIA_REC_SCRIM_START_ALPHA = 0.15f -private const val MEDIA_REC_SCRIM_END_ALPHA = 1.0f - -object MediaRecommendationsViewBinder { - - /** Binds recommendations view holder to the given view-model */ - fun bind( - viewHolder: RecommendationViewHolder, - viewModel: MediaRecommendationsViewModel, - mediaViewController: MediaViewController, - falsingManager: FalsingManager, - backgroundDispatcher: CoroutineDispatcher, - mainDispatcher: CoroutineDispatcher, - ) { - mediaViewController.recsConfigurationChangeListener = this::updateRecommendationsVisibility - val cardView = viewHolder.recommendations - cardView.repeatWhenAttached { - lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.STARTED) { - launch { - viewModel.mediaRecsCard.collectLatest { viewModel -> - viewModel?.let { - bindRecsCard( - viewHolder, - it, - mediaViewController, - falsingManager, - backgroundDispatcher, - mainDispatcher, - ) - } - } - } - } - } - } - } - - suspend fun bindRecsCard( - viewHolder: RecommendationViewHolder, - viewModel: MediaRecsCardViewModel, - viewController: MediaViewController, - falsingManager: FalsingManager, - backgroundDispatcher: CoroutineDispatcher, - mainDispatcher: CoroutineDispatcher, - ) { - // Set up media control location and its listener. - viewModel.onLocationChanged(viewController.currentEndLocation) - viewController.locationChangeListener = viewModel.onLocationChanged - - // Bind main card. - viewHolder.recommendations.contentDescription = - viewModel.contentDescription.invoke(viewController.isGutsVisible) - - viewHolder.recommendations.setOnClickListener { - if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return@setOnClickListener - viewModel.onClicked(Expandable.fromView(it)) - } - - viewHolder.recommendations.setOnLongClickListener { - if (falsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) - return@setOnLongClickListener true - if (!viewController.isGutsVisible) { - openGuts(viewHolder, viewModel, viewController) - } else { - closeGuts(viewHolder, viewModel, viewController) - } - return@setOnLongClickListener true - } - - // Bind colors - val appIcon = viewModel.mediaRecs.first().appIcon - fetchAndUpdateColors(viewHolder, appIcon, backgroundDispatcher, mainDispatcher) - // Bind all recommendations. - bindRecommendationsList( - viewHolder, - viewModel.mediaRecs, - falsingManager, - backgroundDispatcher, - mainDispatcher, - ) - updateRecommendationsVisibility(viewController, viewHolder.recommendations) - - // Set visibility of recommendations. - val expandedSet: ConstraintSet = viewController.expandedLayout - val collapsedSet: ConstraintSet = viewController.collapsedLayout - viewHolder.mediaTitles.forEach { - setVisibleAndAlpha(expandedSet, it.id, viewModel.areTitlesVisible) - setVisibleAndAlpha(collapsedSet, it.id, viewModel.areTitlesVisible) - } - viewHolder.mediaSubtitles.forEach { - setVisibleAndAlpha(expandedSet, it.id, viewModel.areSubtitlesVisible) - setVisibleAndAlpha(collapsedSet, it.id, viewModel.areSubtitlesVisible) - } - - bindRecommendationsGuts(viewHolder, viewModel, viewController, falsingManager) - - viewController.refreshState() - } - - private fun bindRecommendationsGuts( - viewHolder: RecommendationViewHolder, - viewModel: MediaRecsCardViewModel, - viewController: MediaViewController, - falsingManager: FalsingManager, - ) { - val gutsViewHolder = viewHolder.gutsViewHolder - val gutsViewModel = viewModel.gutsMenu - - gutsViewHolder.gutsText.text = gutsViewModel.gutsText - gutsViewHolder.dismissText.visibility = View.VISIBLE - gutsViewHolder.dismiss.isEnabled = true - gutsViewHolder.dismiss.setOnClickListener { - if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return@setOnClickListener - closeGuts(viewHolder, viewModel, viewController) - gutsViewModel.onDismissClicked() - } - - gutsViewHolder.cancelText.background = gutsViewModel.cancelTextBackground - gutsViewHolder.cancel.setOnClickListener { - if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { - closeGuts(viewHolder, viewModel, viewController) - } - } - - gutsViewHolder.settings.setOnClickListener { - if (!falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { - gutsViewModel.onSettingsClicked.invoke() - } - } - - gutsViewHolder.setDismissible(gutsViewModel.isDismissEnabled) - } - - private suspend fun bindRecommendationsList( - viewHolder: RecommendationViewHolder, - mediaRecs: List<MediaRecViewModel>, - falsingManager: FalsingManager, - backgroundDispatcher: CoroutineDispatcher, - mainDispatcher: CoroutineDispatcher, - ) { - mediaRecs.forEachIndexed { index, mediaRecViewModel -> - if (index >= NUM_REQUIRED_RECOMMENDATIONS) return@forEachIndexed - - val appIconView = viewHolder.mediaAppIcons[index] - appIconView.clearColorFilter() - appIconView.setImageDrawable(mediaRecViewModel.appIcon) - - val mediaCoverContainer = viewHolder.mediaCoverContainers[index] - mediaCoverContainer.setOnClickListener { - if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return@setOnClickListener - mediaRecViewModel.onClicked(Expandable.fromView(it), index) - } - mediaCoverContainer.setOnLongClickListener { - if (falsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) - return@setOnLongClickListener true - (it.parent as View).performLongClick() - return@setOnLongClickListener true - } - - val mediaCover = viewHolder.mediaCoverItems[index] - bindRecommendationArtwork( - mediaCover.context, - viewHolder, - mediaRecViewModel, - index, - backgroundDispatcher, - mainDispatcher, - ) - mediaCover.contentDescription = mediaRecViewModel.contentDescription - - val title = viewHolder.mediaTitles[index] - title.text = mediaRecViewModel.title - - val subtitle = viewHolder.mediaSubtitles[index] - subtitle.text = mediaRecViewModel.subtitle - - val progressBar = viewHolder.mediaProgressBars[index] - progressBar.progress = mediaRecViewModel.progress - if (mediaRecViewModel.progress == 0) { - progressBar.visibility = View.GONE - } - } - } - - private fun openGuts( - viewHolder: RecommendationViewHolder, - viewModel: MediaRecsCardViewModel, - mediaViewController: MediaViewController, - ) { - viewHolder.marquee(true, MediaViewController.GUTS_ANIMATION_DURATION) - mediaViewController.openGuts() - viewHolder.recommendations.contentDescription = viewModel.contentDescription.invoke(true) - viewModel.onLongClicked.invoke() - } - - private fun closeGuts( - viewHolder: RecommendationViewHolder, - mediaRecsCardViewModel: MediaRecsCardViewModel, - mediaViewController: MediaViewController, - ) { - viewHolder.marquee(false, MediaViewController.GUTS_ANIMATION_DURATION) - mediaViewController.closeGuts(false) - viewHolder.recommendations.contentDescription = - mediaRecsCardViewModel.contentDescription.invoke(false) - } - - private fun setVisibleAndAlpha(set: ConstraintSet, resId: Int, visible: Boolean) { - set.setVisibility(resId, if (visible) ConstraintSet.VISIBLE else ConstraintSet.GONE) - set.setAlpha(resId, if (visible) 1.0f else 0.0f) - } - - fun updateRecommendationsVisibility( - mediaViewController: MediaViewController, - cardView: TransitionLayout, - ) { - val fittedRecsNum = getNumberOfFittedRecommendations(cardView.context) - val expandedSet = mediaViewController.expandedLayout - val collapsedSet = mediaViewController.collapsedLayout - val mediaCoverContainers = getMediaCoverContainers(cardView) - // Hide media cover that cannot fit in the recommendation card. - mediaCoverContainers.forEachIndexed { index, container -> - setVisibleAndAlpha(expandedSet, container.id, index < fittedRecsNum) - setVisibleAndAlpha(collapsedSet, container.id, index < fittedRecsNum) - } - } - - private fun getMediaCoverContainers(cardView: TransitionLayout): List<ViewGroup> { - return listOf<ViewGroup>( - cardView.requireViewById(R.id.media_cover1_container), - cardView.requireViewById(R.id.media_cover2_container), - cardView.requireViewById(R.id.media_cover3_container), - ) - } - - private fun getNumberOfFittedRecommendations(context: Context): Int { - val res = context.resources - val config = res.configuration - val defaultDpWidth = res.getInteger(R.integer.default_qs_media_rec_width_dp) - val recCoverWidth = - (res.getDimensionPixelSize(R.dimen.qs_media_rec_album_width) + - res.getDimensionPixelSize(R.dimen.qs_media_info_spacing) * 2) - - // On landscape, media controls should take half of the screen width. - val displayAvailableDpWidth = - if (config.orientation == Configuration.ORIENTATION_LANDSCAPE) { - config.screenWidthDp / 2 - } else { - config.screenWidthDp - } - val fittedNum = - if (displayAvailableDpWidth > defaultDpWidth) { - val recCoverDefaultWidth = - res.getDimensionPixelSize(R.dimen.qs_media_rec_default_width) - recCoverDefaultWidth / recCoverWidth - } else { - val displayAvailableWidth = - TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_DIP, - displayAvailableDpWidth.toFloat(), - res.displayMetrics, - ) - .toInt() - displayAvailableWidth / recCoverWidth - } - return min(fittedNum.toDouble(), NUM_REQUIRED_RECOMMENDATIONS.toDouble()).toInt() - } - - private suspend fun bindRecommendationArtwork( - context: Context, - viewHolder: RecommendationViewHolder, - viewModel: MediaRecViewModel, - index: Int, - backgroundDispatcher: CoroutineDispatcher, - mainDispatcher: CoroutineDispatcher, - ) { - val traceCookie = viewHolder.hashCode() - val traceName = "MediaRecommendationsViewBinder#bindRecommendationArtwork" - Trace.beginAsyncSection(traceName, traceCookie) - - // Capture width & height from views in foreground for artwork scaling in background - val width = context.resources.getDimensionPixelSize(R.dimen.qs_media_rec_album_width) - val height = - context.resources.getDimensionPixelSize(R.dimen.qs_media_rec_album_height_expanded) - - withContext(backgroundDispatcher) { - val artwork = - getRecCoverBackground( - context, - viewModel.albumIcon, - width, - height, - backgroundDispatcher, - ) - withContext(mainDispatcher) { - val mediaCover = viewHolder.mediaCoverItems[index] - val coverMatrix = Matrix(mediaCover.imageMatrix) - coverMatrix.postScale(1.25f, 1.25f, 0.5f * width, 0.5f * height) - mediaCover.imageMatrix = coverMatrix - mediaCover.setImageDrawable(artwork) - } - } - } - - /** Returns the recommendation album cover of [width]x[height] size. */ - private suspend fun getRecCoverBackground( - context: Context, - icon: Icon?, - width: Int, - height: Int, - backgroundDispatcher: CoroutineDispatcher, - ): Drawable = - withContext(backgroundDispatcher) { - return@withContext MediaArtworkHelper.getWallpaperColor( - context, - backgroundDispatcher, - icon, - TAG, - ) - ?.let { wallpaperColors -> - addGradientToRecommendationAlbum( - context, - icon!!, - ColorScheme(wallpaperColors, true, Style.CONTENT), - width, - height, - ) - } ?: ColorDrawable(Color.TRANSPARENT) - } - - private fun addGradientToRecommendationAlbum( - context: Context, - artworkIcon: Icon, - mutableColorScheme: ColorScheme, - width: Int, - height: Int, - ): LayerDrawable { - // First try scaling rec card using bitmap drawable. - // If returns null, set drawable bounds. - val albumArt = - getScaledRecommendationCover(context, artworkIcon, width, height) - ?: MediaArtworkHelper.getScaledBackground(context, artworkIcon, width, height) - val gradient = - AppCompatResources.getDrawable(context, R.drawable.qs_media_rec_scrim)?.mutate() - as GradientDrawable - return MediaArtworkHelper.setUpGradientColorOnDrawable( - albumArt, - gradient, - mutableColorScheme, - MEDIA_REC_SCRIM_START_ALPHA, - MEDIA_REC_SCRIM_END_ALPHA, - ) - } - - /** Returns a [Drawable] of a given [artworkIcon] scaled to [width]x[height] size, . */ - private fun getScaledRecommendationCover( - context: Context, - artworkIcon: Icon, - width: Int, - height: Int, - ): Drawable? { - check(width > 0) { "Width must be a positive number but was $width" } - check(height > 0) { "Height must be a positive number but was $height" } - - return if ( - artworkIcon.type == Icon.TYPE_BITMAP || artworkIcon.type == Icon.TYPE_ADAPTIVE_BITMAP - ) { - artworkIcon.bitmap?.let { - val bitmap = Bitmap.createScaledBitmap(it, width, height, false) - BitmapDrawable(context.resources, bitmap) - } - } else { - null - } - } - - private suspend fun fetchAndUpdateColors( - viewHolder: RecommendationViewHolder, - appIcon: Drawable, - backgroundDispatcher: CoroutineDispatcher, - mainDispatcher: CoroutineDispatcher, - ) = - withContext(backgroundDispatcher) { - val colorScheme = - ColorScheme(WallpaperColors.fromDrawable(appIcon), /* darkTheme= */ true) - withContext(mainDispatcher) { - val backgroundColor = surfaceFromScheme(colorScheme) - val textPrimaryColor = textPrimaryFromScheme(colorScheme) - val textSecondaryColor = textSecondaryFromScheme(colorScheme) - - viewHolder.cardTitle.setTextColor(textPrimaryColor) - viewHolder.recommendations.setBackgroundTintList( - ColorStateList.valueOf(backgroundColor) - ) - - viewHolder.mediaTitles.forEach { it.setTextColor(textPrimaryColor) } - viewHolder.mediaSubtitles.forEach { it.setTextColor(textSecondaryColor) } - viewHolder.mediaProgressBars.forEach { - it.progressTintList = ColorStateList.valueOf(textPrimaryColor) - } - - viewHolder.gutsViewHolder.setColors(colorScheme) - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt index 7b1ae57ed421..ac6343c6bb64 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt @@ -61,14 +61,12 @@ import com.android.systemui.media.controls.domain.pipeline.MediaDataManager import com.android.systemui.media.controls.shared.model.MediaData import com.android.systemui.media.controls.shared.model.SmartspaceMediaData import com.android.systemui.media.controls.ui.binder.MediaControlViewBinder -import com.android.systemui.media.controls.ui.binder.MediaRecommendationsViewBinder import com.android.systemui.media.controls.ui.util.MediaViewModelCallback import com.android.systemui.media.controls.ui.util.MediaViewModelListUpdateCallback import com.android.systemui.media.controls.ui.view.MediaCarouselScrollHandler import com.android.systemui.media.controls.ui.view.MediaHostState import com.android.systemui.media.controls.ui.view.MediaScrollView import com.android.systemui.media.controls.ui.view.MediaViewHolder -import com.android.systemui.media.controls.ui.view.RecommendationViewHolder import com.android.systemui.media.controls.ui.viewmodel.MediaCarouselViewModel import com.android.systemui.media.controls.ui.viewmodel.MediaCommonViewModel import com.android.systemui.media.controls.util.MediaUiEventLogger @@ -478,41 +476,10 @@ constructor( MediaPlayerData.isSwipedAway = false } - override fun onSmartspaceMediaDataLoaded( - key: String, - data: SmartspaceMediaData, - shouldPrioritize: Boolean, - ) { - debugLogger.logRecommendationLoaded(key, data.isActive) - // Log the case where the hidden media carousel with the existed inactive resume - // media is shown by the Smartspace signal. - if (data.isActive) { - addSmartspaceMediaRecommendations(key, data, shouldPrioritize) - } else { - // Handle update to inactive as a removal - onSmartspaceMediaDataRemoved(data.targetId, immediately = true) - } - MediaPlayerData.isSwipedAway = false - } - override fun onMediaDataRemoved(key: String, userInitiated: Boolean) { debugLogger.logMediaRemoved(key, userInitiated) removePlayer(key, userInitiated = userInitiated) } - - override fun onSmartspaceMediaDataRemoved(key: String, immediately: Boolean) { - debugLogger.logRecommendationRemoved(key, immediately) - if (immediately || isReorderingAllowed) { - removePlayer(key) - if (!immediately) { - // Although it wasn't requested, we were able to process the removal - // immediately since reordering is allowed. So, notify hosts to update - updateHostVisibility() - } - } else { - keysNeedRemoval.add(key) - } - } } ) } @@ -655,22 +622,6 @@ constructor( mediaContent.addView(viewHolder.player, position) controllerById[commonViewModel.instanceId.toString()] = viewController } - is MediaCommonViewModel.MediaRecommendations -> { - val viewHolder = - RecommendationViewHolder.create(LayoutInflater.from(context), mediaContent) - viewController.attachRecommendations(viewHolder) - viewController.recommendationViewHolder?.recommendations?.layoutParams = lp - MediaRecommendationsViewBinder.bind( - viewHolder, - commonViewModel.recsViewModel, - viewController, - falsingManager, - backgroundDispatcher, - mainDispatcher, - ) - mediaContent.addView(viewHolder.recommendations, position) - controllerById[commonViewModel.key] = viewController - } } viewController.setListening(mediaCarouselScrollHandler.visibleToUser && currentlyExpanded) updateViewControllerToState(viewController, noAnimation = true) @@ -695,21 +646,10 @@ constructor( } private fun onRemoved(commonViewModel: MediaCommonViewModel) { - val id = - when (commonViewModel) { - is MediaCommonViewModel.MediaControl -> commonViewModel.instanceId.toString() - is MediaCommonViewModel.MediaRecommendations -> commonViewModel.key - } + val id = (commonViewModel as MediaCommonViewModel.MediaControl).instanceId.toString() controllerById.remove(id)?.let { - when (commonViewModel) { - is MediaCommonViewModel.MediaControl -> { - mediaCarouselScrollHandler.onPrePlayerRemoved(it.mediaViewHolder!!.player) - mediaContent.removeView(it.mediaViewHolder!!.player) - } - is MediaCommonViewModel.MediaRecommendations -> { - mediaContent.removeView(it.recommendationViewHolder!!.recommendations) - } - } + mediaCarouselScrollHandler.onPrePlayerRemoved(it.mediaViewHolder!!.player) + mediaContent.removeView(it.mediaViewHolder!!.player) it.onDestroy() mediaCarouselScrollHandler.onPlayersChanged() updatePageIndicator() @@ -718,21 +658,10 @@ constructor( } private fun onMoved(commonViewModel: MediaCommonViewModel, from: Int, to: Int) { - val id = - when (commonViewModel) { - is MediaCommonViewModel.MediaControl -> commonViewModel.instanceId.toString() - is MediaCommonViewModel.MediaRecommendations -> commonViewModel.key - } + val id = (commonViewModel as MediaCommonViewModel.MediaControl).instanceId.toString() controllerById[id]?.let { mediaContent.removeViewAt(from) - when (commonViewModel) { - is MediaCommonViewModel.MediaControl -> { - mediaContent.addView(it.mediaViewHolder!!.player, to) - } - is MediaCommonViewModel.MediaRecommendations -> { - mediaContent.addView(it.recommendationViewHolder!!.recommendations, to) - } - } + mediaContent.addView(it.mediaViewHolder!!.player, to) } updatePageIndicator() mediaCarouselScrollHandler.onPlayersChanged() @@ -746,11 +675,9 @@ constructor( val viewIds = viewModels .map { mediaCommonViewModel -> - when (mediaCommonViewModel) { - is MediaCommonViewModel.MediaControl -> - mediaCommonViewModel.instanceId.toString() - is MediaCommonViewModel.MediaRecommendations -> mediaCommonViewModel.key - } + (mediaCommonViewModel as MediaCommonViewModel.MediaControl) + .instanceId + .toString() } .toHashSet() controllerById @@ -758,7 +685,6 @@ constructor( .forEach { mediaCarouselScrollHandler.onPrePlayerRemoved(it.value.mediaViewHolder?.player) mediaContent.removeView(it.value.mediaViewHolder?.player) - mediaContent.removeView(it.value.recommendationViewHolder?.recommendations) it.value.onDestroy() mediaCarouselScrollHandler.onPlayersChanged() updatePageIndicator() @@ -808,9 +734,6 @@ constructor( mediaContent.removeAllViews() for (mediaPlayer in MediaPlayerData.players()) { mediaPlayer.mediaViewHolder?.let { mediaContent.addView(it.player) } - ?: mediaPlayer.recommendationViewHolder?.let { - mediaContent.addView(it.recommendations) - } } mediaCarouselScrollHandler.onPlayersChanged() mediaControlChipInteractor.updateMediaControlChipModelLegacy( @@ -980,67 +903,6 @@ constructor( return MediaViewHolder.create(LayoutInflater.from(context), mediaContent) } - private fun addSmartspaceMediaRecommendations( - key: String, - data: SmartspaceMediaData, - shouldPrioritize: Boolean, - ) = - traceSection("MediaCarouselController#addSmartspaceMediaRecommendations") { - if (DEBUG) Log.d(TAG, "Updating smartspace target in carousel") - MediaPlayerData.getMediaPlayer(key)?.let { - Log.w(TAG, "Skip adding smartspace target in carousel") - return - } - - val existingSmartspaceMediaKey = MediaPlayerData.smartspaceMediaKey() - existingSmartspaceMediaKey?.let { - val removedPlayer = - removePlayer(existingSmartspaceMediaKey, dismissMediaData = false) - removedPlayer?.run { - debugLogger.logPotentialMemoryLeak(existingSmartspaceMediaKey) - onDestroy() - } - } - - val newRecs = mediaControlPanelFactory.get() - newRecs.attachRecommendation( - RecommendationViewHolder.create(LayoutInflater.from(context), mediaContent) - ) - newRecs.mediaViewController.sizeChangedListener = this::updateCarouselDimensions - val lp = - LinearLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT, - ) - newRecs.recommendationViewHolder?.recommendations?.setLayoutParams(lp) - newRecs.bindRecommendation(data) - val curVisibleMediaKey = - MediaPlayerData.visiblePlayerKeys() - .elementAtOrNull(mediaCarouselScrollHandler.visibleMediaIndex) - MediaPlayerData.addMediaRecommendation( - key, - data, - newRecs, - shouldPrioritize, - systemClock, - debugLogger, - ) - updateViewControllerToState(newRecs.mediaViewController, noAnimation = true) - reorderAllPlayers(curVisibleMediaKey) - updatePageIndicator() - mediaFrame.requiresRemeasuring = true - // Check postcondition: mediaContent should have the same number of children as there - // are elements in mediaPlayers. - if (MediaPlayerData.players().size != mediaContent.childCount) { - Log.e( - TAG, - "Size of players list and number of views in carousel are out of sync. " + - "Players size is ${MediaPlayerData.players().size}. " + - "View count is ${mediaContent.childCount}.", - ) - } - } - fun removePlayer( key: String, dismissMediaData: Boolean = true, @@ -1057,7 +919,6 @@ constructor( return removed?.apply { mediaCarouselScrollHandler.onPrePlayerRemoved(removed.mediaViewHolder?.player) mediaContent.removeView(removed.mediaViewHolder?.player) - mediaContent.removeView(removed.recommendationViewHolder?.recommendations) removed.onDestroy() mediaCarouselScrollHandler.onPlayersChanged() mediaControlChipInteractor.updateMediaControlChipModelLegacy( @@ -1095,31 +956,18 @@ constructor( val mediaDataList = MediaPlayerData.mediaData() // Do not loop through the original list of media data because the re-addition of media data // is being executed in background thread. - mediaDataList.forEach { (key, data, isSsMediaRec) -> - if (isSsMediaRec) { - val smartspaceMediaData = MediaPlayerData.smartspaceMediaData + mediaDataList.forEach { (key, data, _) -> + val isSsReactivated = MediaPlayerData.isSsReactivated(key) + if (recreateMedia) { removePlayer(key, dismissMediaData = false, dismissRecommendation = false) - smartspaceMediaData?.let { - addSmartspaceMediaRecommendations( - it.targetId, - it, - MediaPlayerData.shouldPrioritizeSs, - ) - } - onUiExecutionEnd.run() - } else { - val isSsReactivated = MediaPlayerData.isSsReactivated(key) - if (recreateMedia) { - removePlayer(key, dismissMediaData = false, dismissRecommendation = false) - } - addOrUpdatePlayer( - key = key, - oldKey = null, - data = data, - isSsReactivated = isSsReactivated, - onUiExecutionEnd = onUiExecutionEnd, - ) } + addOrUpdatePlayer( + key = key, + oldKey = null, + data = data, + isSsReactivated = isSsReactivated, + onUiExecutionEnd = onUiExecutionEnd, + ) } } @@ -1129,12 +977,8 @@ constructor( if (recreateMedia) { mediaContent.removeAllViews() commonViewModels.forEachIndexed { index, viewModel -> - when (viewModel) { - is MediaCommonViewModel.MediaControl -> - controllerById[viewModel.instanceId.toString()]?.onDestroy() - is MediaCommonViewModel.MediaRecommendations -> - controllerById[viewModel.key]?.onDestroy() - } + val mediaControlViewModel = (viewModel as MediaCommonViewModel.MediaControl) + controllerById[mediaControlViewModel.instanceId.toString()]?.onDestroy() onAdded(viewModel, index, configChanged = true) } } diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLogger.kt index 5d62c022efba..365389107648 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLogger.kt @@ -64,28 +64,6 @@ constructor(@MediaCarouselControllerLog private val buffer: LogBuffer) { { "removing player $str1, by user $bool1" }, ) - fun logRecommendationLoaded(key: String, isActive: Boolean) = - buffer.log( - TAG, - LogLevel.DEBUG, - { - str1 = key - bool1 = isActive - }, - { "add recommendation $str1, active $bool1" }, - ) - - fun logRecommendationRemoved(key: String, immediately: Boolean) = - buffer.log( - TAG, - LogLevel.DEBUG, - { - str1 = key - bool1 = immediately - }, - { "removing recommendation $str1, immediate=$bool1" }, - ) - fun logCarouselHidden() = buffer.log(TAG, LogLevel.DEBUG, {}, { "hiding carousel" }) fun logCarouselVisible() = buffer.log(TAG, LogLevel.DEBUG, {}, { "showing carousel" }) diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java index a6bf5f43698b..006eb203a669 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java @@ -22,7 +22,6 @@ import static com.android.settingslib.flags.Flags.legacyLeAudioSharing; import static com.android.systemui.Flags.communalHub; import static com.android.systemui.Flags.mediaLockscreenLaunchAnimation; import static com.android.systemui.media.controls.domain.pipeline.MediaActionsKt.getNotificationActions; -import static com.android.systemui.media.controls.shared.model.SmartspaceMediaDataKt.NUM_REQUIRED_RECOMMENDATIONS; import static com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel.MEDIA_PLAYER_SCRIM_END_ALPHA; import static com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel.MEDIA_PLAYER_SCRIM_END_ALPHA_LEGACY; import static com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel.MEDIA_PLAYER_SCRIM_START_ALPHA; @@ -35,24 +34,17 @@ import android.app.ActivityOptions; import android.app.BroadcastOptions; import android.app.PendingIntent; import android.app.WallpaperColors; -import android.app.smartspace.SmartspaceAction; import android.content.Context; import android.content.Intent; -import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; -import android.content.res.ColorStateList; -import android.content.res.Configuration; -import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BlendMode; import android.graphics.Color; import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; -import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.drawable.Animatable; -import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; @@ -69,14 +61,12 @@ import android.provider.Settings; import android.text.TextUtils; import android.util.Log; import android.util.Pair; -import android.util.TypedValue; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.view.animation.Interpolator; import android.widget.ImageButton; import android.widget.ImageView; -import android.widget.SeekBar; import android.widget.TextView; import androidx.annotation.NonNull; @@ -105,7 +95,6 @@ import com.android.systemui.media.controls.shared.model.MediaAction; import com.android.systemui.media.controls.shared.model.MediaButton; import com.android.systemui.media.controls.shared.model.MediaData; import com.android.systemui.media.controls.shared.model.MediaDeviceData; -import com.android.systemui.media.controls.shared.model.SmartspaceMediaData; import com.android.systemui.media.controls.ui.animation.AnimationBindHandler; import com.android.systemui.media.controls.ui.animation.ColorSchemeTransition; import com.android.systemui.media.controls.ui.animation.MediaColorSchemesKt; @@ -113,7 +102,6 @@ import com.android.systemui.media.controls.ui.animation.MetadataAnimationHandler import com.android.systemui.media.controls.ui.binder.SeekBarObserver; import com.android.systemui.media.controls.ui.view.GutsViewHolder; import com.android.systemui.media.controls.ui.view.MediaViewHolder; -import com.android.systemui.media.controls.ui.view.RecommendationViewHolder; import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel; import com.android.systemui.media.controls.util.MediaDataUtils; import com.android.systemui.media.controls.util.MediaUiEventLogger; @@ -143,14 +131,12 @@ import com.android.systemui.util.ColorUtilKt; import com.android.systemui.util.animation.TransitionLayout; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.settings.GlobalSettings; -import com.android.systemui.util.time.SystemClock; import dagger.Lazy; import kotlin.Triple; import kotlin.Unit; -import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; import java.util.Random; @@ -165,17 +151,6 @@ public class MediaControlPanel { protected static final String TAG = "MediaControlPanel"; private static final float DISABLED_ALPHA = 0.38f; - private static final String EXPORTED_SMARTSPACE_TRAMPOLINE_ACTIVITY_NAME = "com.google" - + ".android.apps.gsa.staticplugins.opa.smartspace.ExportedSmartspaceTrampolineActivity"; - private static final String EXTRAS_SMARTSPACE_INTENT = - "com.google.android.apps.gsa.smartspace.extra.SMARTSPACE_INTENT"; - private static final String KEY_SMARTSPACE_ARTIST_NAME = "artist_name"; - private static final String KEY_SMARTSPACE_OPEN_IN_FOREGROUND = "KEY_OPEN_IN_FOREGROUND"; - - private static final float REC_MEDIA_COVER_SCALE_FACTOR = 1.25f; - private static final float MEDIA_REC_SCRIM_START_ALPHA = 0.15f; - private static final float MEDIA_REC_SCRIM_END_ALPHA = 1.0f; - private static final Intent SETTINGS_INTENT = new Intent(ACTION_MEDIA_CONTROLS_SETTINGS); // Buttons to show in small player when using semantic actions @@ -215,17 +190,14 @@ public class MediaControlPanel { private Context mContext; private MediaViewHolder mMediaViewHolder; - private RecommendationViewHolder mRecommendationViewHolder; private String mKey; private MediaData mMediaData; - private SmartspaceMediaData mRecommendationData; private MediaViewController mMediaViewController; private MediaSession.Token mToken; private MediaController mController; private Lazy<MediaDataManager> mMediaDataManagerLazy; // Uid for the media app. protected int mUid = Process.INVALID_UID; - private int mSmartspaceMediaItemsCount; private MediaCarouselController mMediaCarouselController; private final MediaOutputDialogManager mMediaOutputDialogManager; private final FalsingManager mFalsingManager; @@ -241,7 +213,6 @@ public class MediaControlPanel { private final NotificationLockscreenUserManager mLockscreenUserManager; // Used for logging. - private SystemClock mSystemClock; private MediaUiEventLogger mLogger; private InstanceId mInstanceId; private String mPackageName; @@ -310,7 +281,6 @@ public class MediaControlPanel { MediaOutputDialogManager mediaOutputDialogManager, MediaCarouselController mediaCarouselController, FalsingManager falsingManager, - SystemClock systemClock, MediaUiEventLogger logger, KeyguardStateController keyguardStateController, ActivityIntentHelper activityIntentHelper, @@ -330,7 +300,6 @@ public class MediaControlPanel { mMediaOutputDialogManager = mediaOutputDialogManager; mMediaCarouselController = mediaCarouselController; mFalsingManager = falsingManager; - mSystemClock = systemClock; mLogger = logger; mKeyguardStateController = keyguardStateController; mActivityIntentHelper = activityIntentHelper; @@ -373,16 +342,6 @@ public class MediaControlPanel { } /** - * Get the recommendation view holder used to display Smartspace media recs. - * - * @return the recommendation view holder - */ - @Nullable - public RecommendationViewHolder getRecommendationViewHolder() { - return mRecommendationViewHolder; - } - - /** * Get the view controller used to display media controls * * @return the media view controller @@ -465,7 +424,7 @@ public class MediaControlPanel { mSeekBarViewModel.attachTouchHandlers(vh.getSeekBar()); mSeekBarViewModel.setScrubbingChangeListener(mScrubbingChangeListener); mSeekBarViewModel.setEnabledChangeListener(mEnabledChangeListener); - mMediaViewController.attach(player, MediaViewController.TYPE.PLAYER); + mMediaViewController.attach(player); vh.getPlayer().setOnLongClickListener(v -> { if (mFalsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) return true; @@ -522,26 +481,6 @@ public class MediaControlPanel { return result; } - /** Attaches the recommendations to the recommendation view holder. */ - public void attachRecommendation(RecommendationViewHolder vh) { - mRecommendationViewHolder = vh; - TransitionLayout recommendations = vh.getRecommendations(); - - mMediaViewController.attach(recommendations, MediaViewController.TYPE.RECOMMENDATION); - mMediaViewController.configurationChangeListener = this::updateRecommendationsVisibility; - - mRecommendationViewHolder.getRecommendations().setOnLongClickListener(v -> { - if (mFalsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) return true; - if (!mMediaViewController.isGutsVisible()) { - openGuts(); - return true; - } else { - closeGuts(); - return true; - } - }); - } - /** Bind this player view based on the data given. */ public void bindPlayer(@NonNull MediaData data, String key) { SceneContainerFlag.assertInLegacyMode(); @@ -868,24 +807,6 @@ public class MediaControlPanel { mMediaViewHolder.getPlayer().setContentDescription(contentDescription); } - private void bindRecommendationContentDescription(SmartspaceMediaData data) { - if (mRecommendationViewHolder == null) { - return; - } - - CharSequence contentDescription; - if (mMediaViewController.isGutsVisible()) { - contentDescription = - mRecommendationViewHolder.getGutsViewHolder().getGutsText().getText(); - } else if (data != null) { - contentDescription = mContext.getString(R.string.controls_media_smartspace_rec_header); - } else { - contentDescription = null; - } - - mRecommendationViewHolder.getRecommendations().setContentDescription(contentDescription); - } - private void bindArtworkAndColors(MediaData data, String key, boolean updateBackground) { final int traceCookie = data.hashCode(); final String traceName = "MediaControlPanel#bindArtworkAndColors<" + key + ">"; @@ -993,62 +914,6 @@ public class MediaControlPanel { }); } - private void bindRecommendationArtwork( - SmartspaceAction recommendation, - String packageName, - int itemIndex - ) { - final int traceCookie = recommendation.hashCode(); - final String traceName = - "MediaControlPanel#bindRecommendationArtwork<" + packageName + ">"; - Trace.beginAsyncSection(traceName, traceCookie); - - // Capture width & height from views in foreground for artwork scaling in background - int width = mContext.getResources().getDimensionPixelSize(R.dimen.qs_media_rec_album_width); - int height = mContext.getResources().getDimensionPixelSize( - R.dimen.qs_media_rec_album_height_expanded); - - mBackgroundExecutor.execute(() -> { - // Album art - ColorScheme mutableColorScheme = null; - Drawable artwork; - Icon artworkIcon = recommendation.getIcon(); - WallpaperColors wallpaperColors = getWallpaperColor(artworkIcon); - if (wallpaperColors != null) { - mutableColorScheme = new ColorScheme(wallpaperColors, true, Style.CONTENT); - artwork = addGradientToRecommendationAlbum(artworkIcon, mutableColorScheme, width, - height); - } else { - artwork = new ColorDrawable(Color.TRANSPARENT); - } - - mMainExecutor.execute(() -> { - // Bind the artwork drawable to media cover. - ImageView mediaCover = - mRecommendationViewHolder.getMediaCoverItems().get(itemIndex); - // Rescale media cover - Matrix coverMatrix = new Matrix(mediaCover.getImageMatrix()); - coverMatrix.postScale(REC_MEDIA_COVER_SCALE_FACTOR, REC_MEDIA_COVER_SCALE_FACTOR, - 0.5f * width, 0.5f * height); - mediaCover.setImageMatrix(coverMatrix); - mediaCover.setImageDrawable(artwork); - - // Set up the app icon. - ImageView appIconView = mRecommendationViewHolder.getMediaAppIcons().get(itemIndex); - appIconView.clearColorFilter(); - try { - Drawable icon = mContext.getPackageManager() - .getApplicationIcon(packageName); - appIconView.setImageDrawable(icon); - } catch (PackageManager.NameNotFoundException e) { - Log.w(TAG, "Cannot find icon for package " + packageName, e); - appIconView.setImageResource(R.drawable.ic_music_note); - } - Trace.endAsyncSection(traceName, traceCookie); - }); - }); - } - // This method should be called from a background thread. WallpaperColors.fromBitmap takes a // good amount of time. We do that work on the background executor to avoid stalling animations // on the UI Thread. @@ -1088,21 +953,6 @@ public class MediaControlPanel { MEDIA_PLAYER_SCRIM_START_ALPHA_LEGACY, MEDIA_PLAYER_SCRIM_END_ALPHA_LEGACY); } - @VisibleForTesting - protected LayerDrawable addGradientToRecommendationAlbum(Icon artworkIcon, - ColorScheme mutableColorScheme, int width, int height) { - // First try scaling rec card using bitmap drawable. - // If returns null, set drawable bounds. - Drawable albumArt = getScaledRecommendationCover(artworkIcon, width, height); - if (albumArt == null) { - albumArt = getScaledBackground(artworkIcon, width, height); - } - GradientDrawable gradient = (GradientDrawable) mContext.getDrawable( - R.drawable.qs_media_rec_scrim).mutate(); - return setupGradientColorOnDrawable(albumArt, gradient, mutableColorScheme, - MEDIA_REC_SCRIM_START_ALPHA, MEDIA_REC_SCRIM_END_ALPHA); - } - private LayerDrawable setupGradientColorOnDrawable(Drawable albumArt, GradientDrawable gradient, ColorScheme mutableColorScheme, float startAlpha, float endAlpha) { int startColor; @@ -1465,258 +1315,6 @@ public class MediaControlPanel { return controller; } - /** Bind this recommendation view based on the given data. */ - public void bindRecommendation(@NonNull SmartspaceMediaData data) { - if (mRecommendationViewHolder == null) { - return; - } - - if (!data.isValid()) { - Log.e(TAG, "Received an invalid recommendation list; returning"); - return; - } - - if (Trace.isEnabled()) { - Trace.traceBegin(Trace.TRACE_TAG_APP, - "MediaControlPanel#bindRecommendation<" + data.getPackageName() + ">"); - } - - mRecommendationData = data; - mPackageName = data.getPackageName(); - mInstanceId = data.getInstanceId(); - - // Set up recommendation card's header. - ApplicationInfo applicationInfo; - try { - applicationInfo = mContext.getPackageManager() - .getApplicationInfo(data.getPackageName(), 0 /* flags */); - mUid = applicationInfo.uid; - } catch (PackageManager.NameNotFoundException e) { - Log.w(TAG, "Fail to get media recommendation's app info", e); - Trace.endSection(); - return; - } - - CharSequence appName = data.getAppName(mContext); - if (appName == null) { - Log.w(TAG, "Fail to get media recommendation's app name"); - Trace.endSection(); - return; - } - - PackageManager packageManager = mContext.getPackageManager(); - // Set up media source app's logo. - Drawable icon = packageManager.getApplicationIcon(applicationInfo); - fetchAndUpdateRecommendationColors(icon); - - // Set up media rec card's tap action if applicable. - TransitionLayout recommendationCard = mRecommendationViewHolder.getRecommendations(); - setSmartspaceRecItemOnClickListener(recommendationCard, data.getCardAction(), - /* interactedSubcardRank */ -1); - bindRecommendationContentDescription(data); - - List<ImageView> mediaCoverItems = mRecommendationViewHolder.getMediaCoverItems(); - List<ViewGroup> mediaCoverContainers = mRecommendationViewHolder.getMediaCoverContainers(); - List<SmartspaceAction> recommendations = data.getValidRecommendations(); - - boolean hasTitle = false; - boolean hasSubtitle = false; - int fittedRecsNum = getNumberOfFittedRecommendations(); - for (int itemIndex = 0; itemIndex < NUM_REQUIRED_RECOMMENDATIONS; itemIndex++) { - SmartspaceAction recommendation = recommendations.get(itemIndex); - - // Set up media item cover. - ImageView mediaCoverImageView = mediaCoverItems.get(itemIndex); - bindRecommendationArtwork(recommendation, data.getPackageName(), itemIndex); - - // Set up the media item's click listener if applicable. - ViewGroup mediaCoverContainer = mediaCoverContainers.get(itemIndex); - setSmartspaceRecItemOnClickListener(mediaCoverContainer, recommendation, itemIndex); - // Bubble up the long-click event to the card. - mediaCoverContainer.setOnLongClickListener(v -> { - if (mFalsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) return true; - View parent = (View) v.getParent(); - if (parent != null) { - parent.performLongClick(); - } - return true; - }); - - // Set up the accessibility label for the media item. - String artistName = recommendation.getExtras() - .getString(KEY_SMARTSPACE_ARTIST_NAME, ""); - if (artistName.isEmpty()) { - mediaCoverImageView.setContentDescription( - mContext.getString( - R.string.controls_media_smartspace_rec_item_no_artist_description, - recommendation.getTitle(), appName)); - } else { - mediaCoverImageView.setContentDescription( - mContext.getString( - R.string.controls_media_smartspace_rec_item_description, - recommendation.getTitle(), artistName, appName)); - } - - // Set up title - CharSequence title = recommendation.getTitle(); - hasTitle |= !TextUtils.isEmpty(title); - TextView titleView = mRecommendationViewHolder.getMediaTitles().get(itemIndex); - titleView.setText(title); - - // Set up subtitle - // It would look awkward to show a subtitle if we don't have a title. - boolean shouldShowSubtitleText = !TextUtils.isEmpty(title); - CharSequence subtitle = shouldShowSubtitleText ? recommendation.getSubtitle() : ""; - hasSubtitle |= !TextUtils.isEmpty(subtitle); - TextView subtitleView = mRecommendationViewHolder.getMediaSubtitles().get(itemIndex); - subtitleView.setText(subtitle); - - // Set up progress bar - SeekBar mediaProgressBar = - mRecommendationViewHolder.getMediaProgressBars().get(itemIndex); - TextView mediaSubtitle = mRecommendationViewHolder.getMediaSubtitles().get(itemIndex); - // show progress bar if the recommended album is played. - Double progress = MediaDataUtils.getDescriptionProgress(recommendation.getExtras()); - if (progress == null || progress <= 0.0) { - mediaProgressBar.setVisibility(View.GONE); - mediaSubtitle.setVisibility(View.VISIBLE); - } else { - mediaProgressBar.setProgress((int) (progress * 100)); - mediaProgressBar.setVisibility(View.VISIBLE); - mediaSubtitle.setVisibility(View.GONE); - } - } - mSmartspaceMediaItemsCount = NUM_REQUIRED_RECOMMENDATIONS; - - // If there's no subtitles and/or titles for any of the albums, hide those views. - ConstraintSet expandedSet = mMediaViewController.getExpandedLayout(); - ConstraintSet collapsedSet = mMediaViewController.getCollapsedLayout(); - final boolean titlesVisible = hasTitle; - final boolean subtitlesVisible = hasSubtitle; - mRecommendationViewHolder.getMediaTitles().forEach((titleView) -> { - setVisibleAndAlpha(expandedSet, titleView.getId(), titlesVisible); - setVisibleAndAlpha(collapsedSet, titleView.getId(), titlesVisible); - }); - mRecommendationViewHolder.getMediaSubtitles().forEach((subtitleView) -> { - setVisibleAndAlpha(expandedSet, subtitleView.getId(), subtitlesVisible); - setVisibleAndAlpha(collapsedSet, subtitleView.getId(), subtitlesVisible); - }); - - // Media covers visibility. - setMediaCoversVisibility(fittedRecsNum); - - // Guts - Runnable onDismissClickedRunnable = () -> { - closeGuts(); - mMediaDataManagerLazy.get().dismissSmartspaceRecommendation( - data.getTargetId(), MediaViewController.GUTS_ANIMATION_DURATION + 100L); - - Intent dismissIntent = data.getDismissIntent(); - if (dismissIntent == null) { - Log.w(TAG, "Cannot create dismiss action click action: " - + "extras missing dismiss_intent."); - return; - } - - if (dismissIntent.getComponent() != null - && dismissIntent.getComponent().getClassName() - .equals(EXPORTED_SMARTSPACE_TRAMPOLINE_ACTIVITY_NAME)) { - // Dismiss the card Smartspace data through Smartspace trampoline activity. - mContext.startActivity(dismissIntent); - } else { - mBroadcastSender.sendBroadcast(dismissIntent); - } - }; - bindGutsMenuCommon( - /* isDismissible= */ true, - appName.toString(), - mRecommendationViewHolder.getGutsViewHolder(), - onDismissClickedRunnable); - - mController = null; - if (mMetadataAnimationHandler == null || !mMetadataAnimationHandler.isRunning()) { - mMediaViewController.refreshState(); - } - Trace.endSection(); - } - - private Unit updateRecommendationsVisibility() { - int fittedRecsNum = getNumberOfFittedRecommendations(); - setMediaCoversVisibility(fittedRecsNum); - return Unit.INSTANCE; - } - - private void setMediaCoversVisibility(int fittedRecsNum) { - ConstraintSet expandedSet = mMediaViewController.getExpandedLayout(); - ConstraintSet collapsedSet = mMediaViewController.getCollapsedLayout(); - List<ViewGroup> mediaCoverContainers = mRecommendationViewHolder.getMediaCoverContainers(); - // Hide media cover that cannot fit in the recommendation card. - for (int itemIndex = 0; itemIndex < NUM_REQUIRED_RECOMMENDATIONS; itemIndex++) { - setVisibleAndAlpha(expandedSet, mediaCoverContainers.get(itemIndex).getId(), - itemIndex < fittedRecsNum); - setVisibleAndAlpha(collapsedSet, mediaCoverContainers.get(itemIndex).getId(), - itemIndex < fittedRecsNum); - } - } - - @VisibleForTesting - protected int getNumberOfFittedRecommendations() { - Resources res = mContext.getResources(); - Configuration config = res.getConfiguration(); - int defaultDpWidth = res.getInteger(R.integer.default_qs_media_rec_width_dp); - int recCoverWidth = res.getDimensionPixelSize(R.dimen.qs_media_rec_album_width) - + res.getDimensionPixelSize(R.dimen.qs_media_info_spacing) * 2; - - // On landscape, media controls should take half of the screen width. - int displayAvailableDpWidth = config.screenWidthDp; - if (config.orientation == Configuration.ORIENTATION_LANDSCAPE) { - displayAvailableDpWidth = displayAvailableDpWidth / 2; - } - int fittedNum; - if (displayAvailableDpWidth > defaultDpWidth) { - int recCoverDefaultWidth = res.getDimensionPixelSize( - R.dimen.qs_media_rec_default_width); - fittedNum = recCoverDefaultWidth / recCoverWidth; - } else { - int displayAvailableWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, - displayAvailableDpWidth, res.getDisplayMetrics()); - fittedNum = displayAvailableWidth / recCoverWidth; - } - return Math.min(fittedNum, NUM_REQUIRED_RECOMMENDATIONS); - } - - private void fetchAndUpdateRecommendationColors(Drawable appIcon) { - mBackgroundExecutor.execute(() -> { - ColorScheme colorScheme = new ColorScheme( - WallpaperColors.fromDrawable(appIcon), /* darkTheme= */ true); - mMainExecutor.execute(() -> setRecommendationColors(colorScheme)); - }); - } - - private void setRecommendationColors(ColorScheme colorScheme) { - if (mRecommendationViewHolder == null) { - return; - } - - int backgroundColor = MediaColorSchemesKt.surfaceFromScheme(colorScheme); - int textPrimaryColor = MediaColorSchemesKt.textPrimaryFromScheme(colorScheme); - int textSecondaryColor = MediaColorSchemesKt.textSecondaryFromScheme(colorScheme); - - mRecommendationViewHolder.getCardTitle().setTextColor(textPrimaryColor); - - mRecommendationViewHolder.getRecommendations() - .setBackgroundTintList(ColorStateList.valueOf(backgroundColor)); - mRecommendationViewHolder.getMediaTitles().forEach( - (title) -> title.setTextColor(textPrimaryColor)); - mRecommendationViewHolder.getMediaSubtitles().forEach( - (subtitle) -> subtitle.setTextColor(textSecondaryColor)); - mRecommendationViewHolder.getMediaProgressBars().forEach( - (progressBar) -> progressBar.setProgressTintList( - ColorStateList.valueOf(textPrimaryColor))); - - mRecommendationViewHolder.getGutsViewHolder().setColors(colorScheme); - } - private void bindGutsMenuCommon( boolean isDismissible, String appName, @@ -1772,14 +1370,10 @@ public class MediaControlPanel { public void closeGuts(boolean immediate) { if (mMediaViewHolder != null) { mMediaViewHolder.marquee(false, mMediaViewController.GUTS_ANIMATION_DURATION); - } else if (mRecommendationViewHolder != null) { - mRecommendationViewHolder.marquee(false, mMediaViewController.GUTS_ANIMATION_DURATION); } mMediaViewController.closeGuts(immediate); if (mMediaViewHolder != null) { bindPlayerContentDescription(mMediaData); - } else if (mRecommendationViewHolder != null) { - bindRecommendationContentDescription(mRecommendationData); } } @@ -1790,14 +1384,10 @@ public class MediaControlPanel { private void openGuts() { if (mMediaViewHolder != null) { mMediaViewHolder.marquee(true, mMediaViewController.GUTS_ANIMATION_DURATION); - } else if (mRecommendationViewHolder != null) { - mRecommendationViewHolder.marquee(true, mMediaViewController.GUTS_ANIMATION_DURATION); } mMediaViewController.openGuts(); if (mMediaViewHolder != null) { bindPlayerContentDescription(mMediaData); - } else if (mRecommendationViewHolder != null) { - bindRecommendationContentDescription(mRecommendationData); } mLogger.logLongPressOpen(mUid, mPackageName, mInstanceId); } @@ -1822,29 +1412,6 @@ public class MediaControlPanel { } /** - * Scale artwork to fill the background of media covers in recommendation card. - */ - @UiThread - private Drawable getScaledRecommendationCover(Icon artworkIcon, int width, int height) { - if (width == 0 || height == 0) { - return null; - } - if (artworkIcon != null) { - Bitmap bitmap; - if (artworkIcon.getType() == Icon.TYPE_BITMAP - || artworkIcon.getType() == Icon.TYPE_ADAPTIVE_BITMAP) { - Bitmap artworkBitmap = artworkIcon.getBitmap(); - if (artworkBitmap != null) { - bitmap = Bitmap.createScaledBitmap(artworkIcon.getBitmap(), width, - height, false); - return new BitmapDrawable(mContext.getResources(), bitmap); - } - } - } - return null; - } - - /** * Get the current media controller * * @return the controller @@ -1896,64 +1463,5 @@ public class MediaControlPanel { set.setVisibility(actionId, visible ? ConstraintSet.VISIBLE : notVisibleValue); set.setAlpha(actionId, visible ? 1.0f : 0.0f); } - - private void setSmartspaceRecItemOnClickListener( - @NonNull View view, - @NonNull SmartspaceAction action, - int interactedSubcardRank) { - if (view == null || action == null || action.getIntent() == null - || action.getIntent().getExtras() == null) { - Log.e(TAG, "No tap action can be set up"); - return; - } - - view.setOnClickListener(v -> { - if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return; - - if (interactedSubcardRank == -1) { - mLogger.logRecommendationCardTap(mPackageName, mInstanceId); - } else { - mLogger.logRecommendationItemTap(mPackageName, mInstanceId, interactedSubcardRank); - } - - if (shouldSmartspaceRecItemOpenInForeground(action)) { - // Request to unlock the device if the activity needs to be opened in foreground. - mActivityStarter.postStartActivityDismissingKeyguard( - action.getIntent(), - 0 /* delay */, - buildLaunchAnimatorController( - mRecommendationViewHolder.getRecommendations())); - } else { - // Otherwise, open the activity in background directly. - view.getContext().startActivity(action.getIntent()); - } - - // Automatically scroll to the active player once the media is loaded. - mMediaCarouselController.setShouldScrollToKey(true); - }); - } - - /** Returns if the Smartspace action will open the activity in foreground. */ - private boolean shouldSmartspaceRecItemOpenInForeground(SmartspaceAction action) { - if (action == null || action.getIntent() == null - || action.getIntent().getExtras() == null) { - return false; - } - - String intentString = action.getIntent().getExtras().getString(EXTRAS_SMARTSPACE_INTENT); - if (intentString == null) { - return false; - } - - try { - Intent wrapperIntent = Intent.parseUri(intentString, Intent.URI_INTENT_SCHEME); - return wrapperIntent.getBooleanExtra(KEY_SMARTSPACE_OPEN_IN_FOREGROUND, false); - } catch (URISyntaxException e) { - Log.wtf(TAG, "Failed to create intent from URI: " + intentString); - e.printStackTrace(); - } - - return false; - } } diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt index b687dce20b06..dba190022c8b 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt @@ -38,7 +38,6 @@ import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.media.controls.ui.animation.ColorSchemeTransition import com.android.systemui.media.controls.ui.animation.MetadataAnimationHandler import com.android.systemui.media.controls.ui.binder.MediaControlViewBinder -import com.android.systemui.media.controls.ui.binder.MediaRecommendationsViewBinder import com.android.systemui.media.controls.ui.binder.SeekBarObserver import com.android.systemui.media.controls.ui.controller.MediaCarouselController.Companion.calculateAlpha import com.android.systemui.media.controls.ui.view.GutsViewHolder @@ -48,7 +47,6 @@ import com.android.systemui.media.controls.ui.view.MediaViewHolder.Companion.hea import com.android.systemui.media.controls.ui.view.MediaViewHolder.Companion.labelLargeTF import com.android.systemui.media.controls.ui.view.MediaViewHolder.Companion.labelMediumTF import com.android.systemui.media.controls.ui.view.MediaViewHolder.Companion.titleMediumTF -import com.android.systemui.media.controls.ui.view.RecommendationViewHolder import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel import com.android.systemui.res.R @@ -90,15 +88,6 @@ constructor( private val globalSettings: GlobalSettings, ) { - /** - * Indicating that the media view controller is for a notification-based player, session-based - * player, or recommendation - */ - enum class TYPE { - PLAYER, - RECOMMENDATION, - } - companion object { @JvmField val GUTS_ANIMATION_DURATION = 234L } @@ -115,7 +104,6 @@ constructor( private var animationDuration: Long = 0 private var animateNextStateChange: Boolean = false private val measurement = MeasurementOutput(0, 0) - private var type: TYPE = TYPE.PLAYER /** A map containing all viewStates for all locations of this mediaState */ private val viewStates: MutableMap<CacheKey, TransitionViewState?> = mutableMapOf() @@ -203,7 +191,6 @@ constructor( private var isNextButtonAvailable = false /** View holders for controller */ - var recommendationViewHolder: RecommendationViewHolder? = null var mediaViewHolder: MediaViewHolder? = null private lateinit var seekBarObserver: SeekBarObserver @@ -417,13 +404,9 @@ constructor( /** Set the height of UMO background constraints. */ private fun setBackgroundHeights(height: Int) { - val backgroundIds = - if (type == TYPE.PLAYER) { - MediaViewHolder.backgroundIds - } else { - setOf(RecommendationViewHolder.backgroundId) - } - backgroundIds.forEach { id -> expandedLayout.getConstraint(id).layout.mHeight = height } + MediaViewHolder.backgroundIds.forEach { id -> + expandedLayout.getConstraint(id).layout.mHeight = height + } } /** @@ -431,11 +414,7 @@ constructor( * [TransitionViewState]. */ private fun setGutsViewState(viewState: TransitionViewState) { - val controlsIds = - when (type) { - TYPE.PLAYER -> MediaViewHolder.controlsIds - TYPE.RECOMMENDATION -> RecommendationViewHolder.controlsIds - } + val controlsIds = MediaViewHolder.controlsIds val gutsIds = GutsViewHolder.ids controlsIds.forEach { id -> viewState.widgetStates.get(id)?.let { state -> @@ -467,7 +446,6 @@ constructor( squishedViewState.widgetStates.get(id)?.let { state -> state.height = squishedHeight } } - // media player calculateWidgetGroupAlphaForSquishiness( MediaViewHolder.expandedBottomActionIds, squishedViewState.measureHeight.toFloat(), @@ -480,20 +458,6 @@ constructor( squishedViewState, squishFraction, ) - // recommendation card - val titlesTop = - calculateWidgetGroupAlphaForSquishiness( - RecommendationViewHolder.mediaTitlesAndSubtitlesIds, - squishedViewState.measureHeight.toFloat(), - squishedViewState, - squishFraction, - ) - calculateWidgetGroupAlphaForSquishiness( - RecommendationViewHolder.mediaContainersIds, - titlesTop, - squishedViewState, - squishFraction, - ) return squishedViewState } @@ -661,10 +625,10 @@ constructor( * Attach a view to this controller. This may perform measurements if it's not available yet and * should therefore be done carefully. */ - fun attach(transitionLayout: TransitionLayout, type: TYPE) = + fun attach(transitionLayout: TransitionLayout) = traceSection("MediaViewController#attach") { - loadLayoutForType(type) - logger.logMediaLocation("attach $type", currentStartLocation, currentEndLocation) + loadLayoutConstraints() + logger.logMediaLocation("attach", currentStartLocation, currentEndLocation) this.transitionLayout = transitionLayout layoutController.attach(transitionLayout) if (currentEndLocation == MediaHierarchyManager.LOCATION_UNKNOWN) { @@ -691,7 +655,7 @@ constructor( seekBarViewModel.setEnabledChangeListener(enabledChangeListener) val mediaCard = mediaViewHolder.player - attach(mediaViewHolder.player, TYPE.PLAYER) + attach(mediaViewHolder.player) val turbulenceNoiseView = mediaViewHolder.turbulenceNoiseView turbulenceNoiseController = TurbulenceNoiseController(turbulenceNoiseView) @@ -813,15 +777,6 @@ constructor( } } - fun attachRecommendations(recommendationViewHolder: RecommendationViewHolder) { - if (!SceneContainerFlag.isEnabled) return - this.recommendationViewHolder = recommendationViewHolder - - attach(recommendationViewHolder.recommendations, TYPE.RECOMMENDATION) - recsConfigurationChangeListener = - MediaRecommendationsViewBinder::updateRecommendationsVisibility - } - fun bindSeekBar(onSeek: () -> Unit, onBindSeekBar: (SeekBarViewModel) -> Unit) { if (!SceneContainerFlag.isEnabled) return seekBarViewModel.logSeek = onSeek @@ -1026,20 +981,10 @@ constructor( return result } - private fun loadLayoutForType(type: TYPE) { - this.type = type - - // These XML resources contain ConstraintSets that will apply to this player type's layout - when (type) { - TYPE.PLAYER -> { - collapsedLayout.load(context, R.xml.media_session_collapsed) - expandedLayout.load(context, R.xml.media_session_expanded) - } - TYPE.RECOMMENDATION -> { - collapsedLayout.load(context, R.xml.media_recommendations_collapsed) - expandedLayout.load(context, R.xml.media_recommendations_expanded) - } - } + private fun loadLayoutConstraints() { + // These XML resources contain ConstraintSets that will apply to this player's layout + collapsedLayout.load(context, R.xml.media_session_collapsed) + expandedLayout.load(context, R.xml.media_session_expanded) readjustUIUpdateConstraints() refreshState() } diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelCallback.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelCallback.kt index f28edd638b10..2fc44ad3cce6 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelCallback.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelCallback.kt @@ -42,8 +42,7 @@ class MediaViewModelCallback( ) { oldItem.instanceId == newItem.instanceId } else { - oldItem is MediaCommonViewModel.MediaRecommendations && - newItem is MediaCommonViewModel.MediaRecommendations + false } } @@ -56,11 +55,6 @@ class MediaViewModelCallback( ) { oldItem.immediatelyUpdateUi == newItem.immediatelyUpdateUi && oldItem.updateTime == newItem.updateTime - } else if ( - oldItem is MediaCommonViewModel.MediaRecommendations && - newItem is MediaCommonViewModel.MediaRecommendations - ) { - oldItem.key == newItem.key && oldItem.loadingEnabled == newItem.loadingEnabled } else { false } diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/RecommendationViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/RecommendationViewHolder.kt deleted file mode 100644 index 2d028d0213ff..000000000000 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/RecommendationViewHolder.kt +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.media.controls.ui.view - -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.ImageView -import android.widget.SeekBar -import android.widget.TextView -import com.android.internal.widget.CachingIconView -import com.android.systemui.media.controls.ui.drawable.IlluminationDrawable -import com.android.systemui.res.R -import com.android.systemui.util.animation.TransitionLayout - -private const val TAG = "RecommendationViewHolder" - -/** ViewHolder for a Smartspace media recommendation. */ -class RecommendationViewHolder private constructor(itemView: View) { - - val recommendations = itemView as TransitionLayout - - // Recommendation screen - val cardTitle: TextView = itemView.requireViewById(R.id.media_rec_title) - - val mediaCoverContainers = - listOf<ViewGroup>( - itemView.requireViewById(R.id.media_cover1_container), - itemView.requireViewById(R.id.media_cover2_container), - itemView.requireViewById(R.id.media_cover3_container) - ) - val mediaAppIcons: List<CachingIconView> = - mediaCoverContainers.map { it.requireViewById(R.id.media_rec_app_icon) } - val mediaTitles: List<TextView> = - mediaCoverContainers.map { it.requireViewById(R.id.media_title) } - val mediaSubtitles: List<TextView> = - mediaCoverContainers.map { it.requireViewById(R.id.media_subtitle) } - val mediaProgressBars: List<SeekBar> = - mediaCoverContainers.map { - it.requireViewById<SeekBar?>(R.id.media_progress_bar).apply { - // Media playback is in the direction of tape, not time, so it stays LTR - layoutDirection = View.LAYOUT_DIRECTION_LTR - } - } - - val mediaCoverItems: List<ImageView> = - mediaCoverContainers.map { it.requireViewById(R.id.media_cover) } - val gutsViewHolder = GutsViewHolder(itemView) - - init { - (recommendations.background as IlluminationDrawable).let { background -> - mediaCoverContainers.forEach { background.registerLightSource(it) } - background.registerLightSource(gutsViewHolder.cancel) - background.registerLightSource(gutsViewHolder.dismiss) - background.registerLightSource(gutsViewHolder.settings) - } - } - - fun marquee(start: Boolean, delay: Long) { - gutsViewHolder.marquee(start, delay, TAG) - } - - companion object { - /** - * Creates a RecommendationViewHolder. - * - * @param inflater LayoutInflater to use to inflate the layout. - * @param parent Parent of inflated view. - */ - @JvmStatic - fun create(inflater: LayoutInflater, parent: ViewGroup): RecommendationViewHolder { - val itemView = - inflater.inflate(R.layout.media_recommendations, parent, false /* attachToRoot */) - // Because this media view (a TransitionLayout) is used to measure and layout the views - // in various states before being attached to its parent, we can't depend on the default - // LAYOUT_DIRECTION_INHERIT to correctly resolve the ltr direction. - itemView.layoutDirection = View.LAYOUT_DIRECTION_LOCALE - return RecommendationViewHolder(itemView) - } - - // Res Ids for the control components on the recommendation view. - val controlsIds = - setOf( - R.id.media_rec_title, - R.id.media_cover, - R.id.media_cover1_container, - R.id.media_cover2_container, - R.id.media_cover3_container, - R.id.media_title, - R.id.media_subtitle, - ) - - val mediaTitlesAndSubtitlesIds = - setOf( - R.id.media_title, - R.id.media_subtitle, - ) - - val mediaContainersIds = - setOf( - R.id.media_cover1_container, - R.id.media_cover2_container, - R.id.media_cover3_container - ) - - val backgroundId = R.id.sizing_view - } -} diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt index e5f1766fbb28..dfaee4434bcf 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt @@ -49,7 +49,6 @@ constructor( private val visualStabilityProvider: VisualStabilityProvider, private val interactor: MediaCarouselInteractor, private val controlInteractorFactory: MediaControlInteractorFactory, - private val recommendationsViewModel: MediaRecommendationsViewModel, private val logger: MediaUiEventLogger, private val mediaLogger: MediaLogger, ) { @@ -69,7 +68,7 @@ constructor( when (commonModel) { is MediaCommonModel.MediaControl -> add(toViewModel(commonModel)) is MediaCommonModel.MediaRecommendations -> - add(toViewModel(commonModel)) + return@forEach // TODO(b/382680767): remove } } } @@ -95,8 +94,6 @@ constructor( private val mediaControlByInstanceId = mutableMapOf<InstanceId, MediaCommonViewModel.MediaControl>() - private var mediaRecs: MediaCommonViewModel.MediaRecommendations? = null - private var modelsPendingRemoval: MutableSet<MediaCommonModel> = mutableSetOf() private var allowReorder = false @@ -149,37 +146,6 @@ constructor( ) } - private fun toViewModel( - commonModel: MediaCommonModel.MediaRecommendations - ): MediaCommonViewModel.MediaRecommendations { - return mediaRecs?.copy( - key = commonModel.recsLoadingModel.key, - loadingEnabled = interactor.isRecommendationActive(), - ) - ?: MediaCommonViewModel.MediaRecommendations( - key = commonModel.recsLoadingModel.key, - loadingEnabled = interactor.isRecommendationActive(), - recsViewModel = recommendationsViewModel, - onAdded = { commonViewModel -> - mediaLogger.logMediaRecommendationCardAdded( - commonModel.recsLoadingModel.key - ) - onMediaRecommendationAddedOrUpdated( - commonViewModel as MediaCommonViewModel.MediaRecommendations - ) - }, - onRemoved = { immediatelyRemove -> - onMediaRecommendationRemoved(commonModel, immediatelyRemove) - }, - onUpdated = { commonViewModel -> - onMediaRecommendationAddedOrUpdated( - commonViewModel as MediaCommonViewModel.MediaRecommendations - ) - }, - ) - .also { mediaRecs = it } - } - private fun onMediaControlAddedOrUpdated( commonViewModel: MediaCommonViewModel, commonModel: MediaCommonModel.MediaControl, @@ -197,32 +163,6 @@ constructor( } } - private fun onMediaRecommendationAddedOrUpdated( - commonViewModel: MediaCommonViewModel.MediaRecommendations - ) { - if (!interactor.isRecommendationActive()) { - commonViewModel.onRemoved(true) - } - } - - private fun onMediaRecommendationRemoved( - commonModel: MediaCommonModel.MediaRecommendations, - immediatelyRemove: Boolean, - ) { - mediaLogger.logMediaRecommendationCardRemoved(commonModel.recsLoadingModel.key) - if (immediatelyRemove || isReorderingAllowed()) { - interactor.dismissSmartspaceRecommendation(commonModel.recsLoadingModel.key, 0L) - mediaRecs = null - if (!immediatelyRemove) { - // Although it wasn't requested, we were able to process the removal - // immediately since reordering is allowed. So, notify hosts to update - updateHostVisibility() - } - } else { - modelsPendingRemoval.add(commonModel) - } - } - private fun isReorderingAllowed(): Boolean { return visualStabilityProvider.isReorderingAllowed } diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCommonViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCommonViewModel.kt index 52cb173b39cb..d493d57051f7 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCommonViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCommonViewModel.kt @@ -35,13 +35,4 @@ sealed class MediaCommonViewModel { val isMediaFromRec: Boolean = false, val updateTime: Long = 0, ) : MediaCommonViewModel() - - data class MediaRecommendations( - val key: String, - val loadingEnabled: Boolean, - val recsViewModel: MediaRecommendationsViewModel, - override val onAdded: (MediaCommonViewModel) -> Unit, - override val onRemoved: (Boolean) -> Unit, - override val onUpdated: (MediaCommonViewModel) -> Unit, - ) : MediaCommonViewModel() } diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecViewModel.kt deleted file mode 100644 index 77add4035067..000000000000 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecViewModel.kt +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.media.controls.ui.viewmodel - -import android.graphics.drawable.Drawable -import android.graphics.drawable.Icon -import com.android.systemui.animation.Expandable - -/** Models UI state for media recommendation item */ -data class MediaRecViewModel( - val contentDescription: CharSequence, - val title: CharSequence = "", - val subtitle: CharSequence = "", - /** track progress [0 - 100] for the recommendation album. */ - val progress: Int = 0, - val albumIcon: Icon? = null, - val appIcon: Drawable, - val onClicked: ((Expandable, Int) -> Unit), -) diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModel.kt deleted file mode 100644 index 90313ddc736e..000000000000 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModel.kt +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.media.controls.ui.viewmodel - -import android.content.Context -import android.content.Intent -import android.content.pm.PackageManager -import android.graphics.drawable.Drawable -import android.os.Process -import android.util.Log -import com.android.internal.logging.InstanceId -import com.android.systemui.animation.Expandable -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.dagger.qualifiers.Background -import com.android.systemui.media.controls.domain.pipeline.interactor.MediaRecommendationsInteractor -import com.android.systemui.media.controls.shared.model.MediaRecModel -import com.android.systemui.media.controls.shared.model.MediaRecommendationsModel -import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager -import com.android.systemui.media.controls.ui.controller.MediaLocation -import com.android.systemui.media.controls.ui.controller.MediaViewController.Companion.GUTS_ANIMATION_DURATION -import com.android.systemui.media.controls.util.MediaDataUtils -import com.android.systemui.media.controls.util.MediaUiEventLogger -import com.android.systemui.res.R -import javax.inject.Inject -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.flowOn -import kotlinx.coroutines.flow.map - -/** Models UI state and handles user input for media recommendations */ -@SysUISingleton -class MediaRecommendationsViewModel -@Inject -constructor( - @Application private val applicationContext: Context, - @Background private val backgroundDispatcher: CoroutineDispatcher, - private val interactor: MediaRecommendationsInteractor, - private val logger: MediaUiEventLogger, -) { - - val mediaRecsCard: Flow<MediaRecsCardViewModel?> = - interactor.recommendations - .map { recsCard -> toRecsViewModel(recsCard) } - .distinctUntilChanged() - .flowOn(backgroundDispatcher) - - @MediaLocation private var location = MediaHierarchyManager.LOCATION_UNKNOWN - - /** - * Called whenever the recommendation has been expired or removed by the user. This method - * removes the recommendation card entirely from the carousel. - */ - private fun onMediaRecommendationsDismissed( - key: String, - uid: Int, - packageName: String, - dismissIntent: Intent?, - instanceId: InstanceId?, - ) { - logger.logLongPressDismiss(uid, packageName, instanceId) - interactor.removeMediaRecommendations(key, dismissIntent, GUTS_DISMISS_DELAY_MS_DURATION) - } - - private fun onClicked( - expandable: Expandable, - intent: Intent?, - packageName: String, - instanceId: InstanceId?, - index: Int, - ) { - if (intent == null || intent.extras == null) { - Log.e(TAG, "No tap action can be set up") - return - } - - if (index == -1) { - logger.logRecommendationCardTap(packageName, instanceId) - } else { - logger.logRecommendationItemTap(packageName, instanceId, index) - } - - // set the package name of the player added by recommendation once the media is loaded. - interactor.switchToMediaControl(packageName) - - interactor.startClickIntent(expandable, intent) - } - - private suspend fun toRecsViewModel(model: MediaRecommendationsModel): MediaRecsCardViewModel? { - if (!model.areRecommendationsValid) { - Log.e(TAG, "Received an invalid recommendation list") - return null - } - if (model.appName == null || model.uid == Process.INVALID_UID) { - Log.w(TAG, "Fail to get media recommendation's app info") - return null - } - - val appIcon = getIconFromApp(model.packageName) ?: return null - - var areTitlesVisible = false - var areSubtitlesVisible = false - val mediaRecs = - model.mediaRecs.map { mediaRecModel -> - areTitlesVisible = areTitlesVisible || !mediaRecModel.title.isNullOrEmpty() - areSubtitlesVisible = areSubtitlesVisible || !mediaRecModel.subtitle.isNullOrEmpty() - val progress = MediaDataUtils.getDescriptionProgress(mediaRecModel.extras) ?: 0.0 - MediaRecViewModel( - contentDescription = - setUpMediaRecContentDescription(mediaRecModel, model.appName), - title = mediaRecModel.title ?: "", - subtitle = mediaRecModel.subtitle ?: "", - progress = (progress * 100).toInt(), - albumIcon = mediaRecModel.icon, - appIcon = appIcon, - onClicked = { expandable, index -> - onClicked( - expandable, - mediaRecModel.intent, - model.packageName, - model.instanceId, - index, - ) - }, - ) - } - // Subtitles should only be visible if titles are visible. - areSubtitlesVisible = areTitlesVisible && areSubtitlesVisible - - return MediaRecsCardViewModel( - contentDescription = { gutsVisible -> - if (gutsVisible) { - applicationContext.getString( - R.string.controls_media_close_session, - model.appName, - ) - } else { - applicationContext.getString(R.string.controls_media_smartspace_rec_header) - } - }, - onClicked = { expandable -> - onClicked( - expandable, - model.dismissIntent, - model.packageName, - model.instanceId, - index = -1, - ) - }, - onLongClicked = { - logger.logLongPressOpen(model.uid, model.packageName, model.instanceId) - }, - mediaRecs = mediaRecs, - areTitlesVisible = areTitlesVisible, - areSubtitlesVisible = areSubtitlesVisible, - gutsMenu = toGutsViewModel(model), - onLocationChanged = { location = it }, - ) - } - - private fun toGutsViewModel(model: MediaRecommendationsModel): GutsViewModel { - return GutsViewModel( - gutsText = - applicationContext.getString(R.string.controls_media_close_session, model.appName), - onDismissClicked = { - onMediaRecommendationsDismissed( - model.key, - model.uid, - model.packageName, - model.dismissIntent, - model.instanceId, - ) - }, - cancelTextBackground = - applicationContext.getDrawable(R.drawable.qs_media_outline_button), - onSettingsClicked = { - logger.logLongPressSettings(model.uid, model.packageName, model.instanceId) - interactor.startSettings() - }, - ) - } - - private fun setUpMediaRecContentDescription( - mediaRec: MediaRecModel, - appName: CharSequence?, - ): CharSequence { - // Set up the accessibility label for the media item. - val artistName = mediaRec.extras?.getString(KEY_SMARTSPACE_ARTIST_NAME, "") - return if (artistName.isNullOrEmpty()) { - applicationContext.getString( - R.string.controls_media_smartspace_rec_item_no_artist_description, - mediaRec.title, - appName, - ) - } else { - applicationContext.getString( - R.string.controls_media_smartspace_rec_item_description, - mediaRec.title, - artistName, - appName, - ) - } - } - - private fun getIconFromApp(packageName: String): Drawable? { - return try { - applicationContext.packageManager.getApplicationIcon(packageName) - } catch (e: PackageManager.NameNotFoundException) { - Log.w(TAG, "Cannot find icon for package $packageName", e) - null - } - } - - companion object { - private const val TAG = "MediaRecommendationsViewModel" - private const val KEY_SMARTSPACE_ARTIST_NAME = "artist_name" - /** - * Delay duration is based on [GUTS_ANIMATION_DURATION], it should have 100 ms increase in - * order to let the animation end. - */ - private const val GUTS_DISMISS_DELAY_MS_DURATION = 334L - } -} diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecsCardViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecsCardViewModel.kt deleted file mode 100644 index f1f7dc2195d5..000000000000 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecsCardViewModel.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.media.controls.ui.viewmodel - -import com.android.systemui.animation.Expandable - -/** Models UI state for media recommendations card. */ -data class MediaRecsCardViewModel( - val contentDescription: (Boolean) -> CharSequence, - val onClicked: (Expandable) -> Unit, - val onLongClicked: () -> Unit, - val mediaRecs: List<MediaRecViewModel>, - val areTitlesVisible: Boolean, - val areSubtitlesVisible: Boolean, - val gutsMenu: GutsViewModel, - val onLocationChanged: (Int) -> Unit, -) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt index 2001a3ea7ca0..dad08e014c0f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt @@ -422,25 +422,6 @@ class MediaCarouselControllerTest(flags: FlagsParameterization) : SysuiTestCase( assertTrue(MediaPlayerData.playerKeys().elementAt(2).isSsMediaRec) } - @DisableSceneContainer - @Test - fun testOrderWithSmartspace_prioritized_updatingVisibleMediaPlayers() { - verify(mediaDataManager).addListener(capture(listener)) - - testPlayerOrdering() - - // If smartspace is prioritized - listener.value.onSmartspaceMediaDataLoaded( - SMARTSPACE_KEY, - EMPTY_SMARTSPACE_MEDIA_DATA.copy(isActive = true), - true, - ) - - // Then it should be shown immediately after any actively playing controls - assertTrue(MediaPlayerData.playerKeys().elementAt(2).isSsMediaRec) - assertTrue(MediaPlayerData.visiblePlayerKeys().elementAt(2).isSsMediaRec) - } - @Test fun testOrderWithSmartspace_notPrioritized() { testPlayerOrdering() @@ -571,146 +552,6 @@ class MediaCarouselControllerTest(flags: FlagsParameterization) : SysuiTestCase( verify(logger).logRecommendationRemoved(eq(packageName), eq(instanceId!!)) } - @DisableSceneContainer - @Test - fun testMediaLoaded_ScrollToActivePlayer() { - verify(mediaDataManager).addListener(capture(listener)) - - listener.value.onMediaDataLoaded( - PLAYING_LOCAL, - null, - DATA.copy( - active = true, - isPlaying = true, - playbackLocation = MediaData.PLAYBACK_LOCAL, - resumption = false, - ), - ) - listener.value.onMediaDataLoaded( - PAUSED_LOCAL, - null, - DATA.copy( - active = true, - isPlaying = false, - playbackLocation = MediaData.PLAYBACK_LOCAL, - resumption = false, - ), - ) - runAllReady() - // adding a media recommendation card. - listener.value.onSmartspaceMediaDataLoaded( - SMARTSPACE_KEY, - EMPTY_SMARTSPACE_MEDIA_DATA, - false, - ) - mediaCarouselController.shouldScrollToKey = true - // switching between media players. - listener.value.onMediaDataLoaded( - PLAYING_LOCAL, - PLAYING_LOCAL, - DATA.copy( - active = true, - isPlaying = false, - playbackLocation = MediaData.PLAYBACK_LOCAL, - resumption = true, - ), - ) - listener.value.onMediaDataLoaded( - PAUSED_LOCAL, - PAUSED_LOCAL, - DATA.copy( - active = true, - isPlaying = true, - playbackLocation = MediaData.PLAYBACK_LOCAL, - resumption = false, - ), - ) - runAllReady() - - assertEquals( - MediaPlayerData.getMediaPlayerIndex(PAUSED_LOCAL), - mediaCarouselController.mediaCarouselScrollHandler.visibleMediaIndex, - ) - } - - @DisableSceneContainer - @Test - fun testMediaLoadedFromRecommendationCard_ScrollToActivePlayer() { - verify(mediaDataManager).addListener(capture(listener)) - - listener.value.onSmartspaceMediaDataLoaded( - SMARTSPACE_KEY, - EMPTY_SMARTSPACE_MEDIA_DATA.copy(packageName = "PACKAGE_NAME", isActive = true), - false, - ) - listener.value.onMediaDataLoaded( - PLAYING_LOCAL, - null, - DATA.copy( - active = true, - isPlaying = true, - playbackLocation = MediaData.PLAYBACK_LOCAL, - resumption = false, - ), - ) - runAllReady() - - var playerIndex = MediaPlayerData.getMediaPlayerIndex(PLAYING_LOCAL) - assertEquals( - playerIndex, - mediaCarouselController.mediaCarouselScrollHandler.visibleMediaIndex, - ) - assertEquals(playerIndex, 0) - - // Replaying the same media player one more time. - // And check that the card stays in its position. - mediaCarouselController.shouldScrollToKey = true - listener.value.onMediaDataLoaded( - PLAYING_LOCAL, - null, - DATA.copy( - active = true, - isPlaying = true, - playbackLocation = MediaData.PLAYBACK_LOCAL, - resumption = false, - packageName = "PACKAGE_NAME", - ), - ) - runAllReady() - playerIndex = MediaPlayerData.getMediaPlayerIndex(PLAYING_LOCAL) - assertEquals(playerIndex, 0) - } - - @DisableSceneContainer - @Test - fun testRecommendationRemovedWhileNotVisible_updateHostVisibility() { - verify(mediaDataManager).addListener(capture(listener)) - - var result = false - mediaCarouselController.updateHostVisibility = { result = true } - - whenever(visualStabilityProvider.isReorderingAllowed).thenReturn(true) - listener.value.onSmartspaceMediaDataRemoved(SMARTSPACE_KEY, false) - - assertEquals(true, result) - } - - @DisableSceneContainer - @Test - fun testRecommendationRemovedWhileVisible_thenReorders_updateHostVisibility() { - verify(mediaDataManager).addListener(capture(listener)) - - var result = false - mediaCarouselController.updateHostVisibility = { result = true } - - whenever(visualStabilityProvider.isReorderingAllowed).thenReturn(false) - listener.value.onSmartspaceMediaDataRemoved(SMARTSPACE_KEY, false) - assertEquals(false, result) - - visualStabilityCallback.value.onReorderingAllowed() - assertEquals(true, result) - } - @Test fun testGetCurrentVisibleMediaContentIntent() { val clickIntent1 = mock(PendingIntent::class.java) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt index 9543032ef5ec..88fcc706f072 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt @@ -19,16 +19,12 @@ package com.android.systemui.media.controls.ui.controller import android.animation.Animator import android.animation.AnimatorSet import android.app.PendingIntent -import android.app.smartspace.SmartspaceAction -import android.content.Context import android.content.Intent import android.content.pm.ApplicationInfo import android.content.pm.PackageManager -import android.content.res.Configuration import android.graphics.Bitmap import android.graphics.Canvas import android.graphics.Color -import android.graphics.Matrix import android.graphics.drawable.Animatable2 import android.graphics.drawable.AnimatedVectorDrawable import android.graphics.drawable.Drawable @@ -47,7 +43,6 @@ import android.platform.test.flag.junit.DeviceFlagsValueProvider import android.provider.Settings import android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS import android.testing.TestableLooper -import android.util.TypedValue import android.view.View import android.view.ViewGroup import android.view.animation.Interpolator @@ -59,7 +54,6 @@ import android.widget.TextView import androidx.constraintlayout.widget.Barrier import androidx.constraintlayout.widget.ConstraintSet import androidx.lifecycle.LiveData -import androidx.media.utils.MediaConstants import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.logging.InstanceId @@ -72,19 +66,15 @@ import com.android.systemui.broadcast.BroadcastSender import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor import com.android.systemui.flags.DisableSceneContainer import com.android.systemui.media.controls.MediaTestUtils -import com.android.systemui.media.controls.domain.pipeline.EMPTY_SMARTSPACE_MEDIA_DATA import com.android.systemui.media.controls.domain.pipeline.MediaDataManager -import com.android.systemui.media.controls.shared.model.KEY_SMARTSPACE_APP_NAME import com.android.systemui.media.controls.shared.model.MediaAction import com.android.systemui.media.controls.shared.model.MediaButton import com.android.systemui.media.controls.shared.model.MediaData import com.android.systemui.media.controls.shared.model.MediaDeviceData import com.android.systemui.media.controls.shared.model.MediaNotificationAction -import com.android.systemui.media.controls.shared.model.SmartspaceMediaData import com.android.systemui.media.controls.ui.binder.SeekBarObserver import com.android.systemui.media.controls.ui.view.GutsViewHolder import com.android.systemui.media.controls.ui.view.MediaViewHolder -import com.android.systemui.media.controls.ui.view.RecommendationViewHolder import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel import com.android.systemui.media.controls.util.MediaUiEventLogger import com.android.systemui.media.dialog.MediaOutputDialogManager @@ -216,32 +206,9 @@ public class MediaControlPanelTest : SysuiTestCase() { @Mock private lateinit var communalSceneInteractor: CommunalSceneInteractor - @Mock private lateinit var recommendationViewHolder: RecommendationViewHolder - @Mock private lateinit var smartspaceAction: SmartspaceAction - private lateinit var smartspaceData: SmartspaceMediaData - @Mock private lateinit var coverContainer1: ViewGroup - @Mock private lateinit var coverContainer2: ViewGroup - @Mock private lateinit var coverContainer3: ViewGroup - @Mock private lateinit var recAppIconItem: CachingIconView - @Mock private lateinit var recCardTitle: TextView - @Mock private lateinit var coverItem: ImageView - @Mock private lateinit var matrix: Matrix - private lateinit var recTitle1: TextView - private lateinit var recTitle2: TextView - private lateinit var recTitle3: TextView - private lateinit var recSubtitle1: TextView - private lateinit var recSubtitle2: TextView - private lateinit var recSubtitle3: TextView - @Mock private lateinit var recProgressBar1: SeekBar - @Mock private lateinit var recProgressBar2: SeekBar - @Mock private lateinit var recProgressBar3: SeekBar @Mock private lateinit var globalSettings: GlobalSettings - private val intent = - Intent().apply { - putExtras(Bundle().also { it.putString(KEY_SMARTSPACE_APP_NAME, REC_APP_NAME) }) - setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - } + private val intent = Intent().apply { setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) } private val pendingIntent = PendingIntent.getActivity( mContext, @@ -282,7 +249,6 @@ public class MediaControlPanelTest : SysuiTestCase() { mediaOutputDialogManager, mediaCarouselController, falsingManager, - clock, logger, keyguardStateController, activityIntentHelper, @@ -304,27 +270,6 @@ public class MediaControlPanelTest : SysuiTestCase() { initMediaViewHolderMocks() initDeviceMediaData(false, DEVICE_NAME) - - // Set up recommendation view - initRecommendationViewHolderMocks() - - // Set valid recommendation data - val extras = Bundle() - extras.putString(KEY_SMARTSPACE_APP_NAME, REC_APP_NAME) - val intent = - Intent().apply { - putExtras(extras) - setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - } - whenever(smartspaceAction.intent).thenReturn(intent) - whenever(smartspaceAction.extras).thenReturn(extras) - smartspaceData = - EMPTY_SMARTSPACE_MEDIA_DATA.copy( - packageName = PACKAGE, - instanceId = instanceId, - recommendations = listOf(smartspaceAction, smartspaceAction, smartspaceAction), - cardAction = smartspaceAction, - ) } private fun initGutsViewHolderMocks() { @@ -471,49 +416,6 @@ public class MediaControlPanelTest : SysuiTestCase() { whenever(viewHolder.loadingEffectView).thenReturn(loadingEffectView) } - /** Initialize elements for the recommendation view holder */ - private fun initRecommendationViewHolderMocks() { - recTitle1 = TextView(context) - recTitle2 = TextView(context) - recTitle3 = TextView(context) - recSubtitle1 = TextView(context) - recSubtitle2 = TextView(context) - recSubtitle3 = TextView(context) - - whenever(recommendationViewHolder.recommendations).thenReturn(view) - whenever(recommendationViewHolder.mediaAppIcons) - .thenReturn(listOf(recAppIconItem, recAppIconItem, recAppIconItem)) - whenever(recommendationViewHolder.cardTitle).thenReturn(recCardTitle) - whenever(recommendationViewHolder.mediaCoverItems) - .thenReturn(listOf(coverItem, coverItem, coverItem)) - whenever(recommendationViewHolder.mediaCoverContainers) - .thenReturn(listOf(coverContainer1, coverContainer2, coverContainer3)) - whenever(recommendationViewHolder.mediaTitles) - .thenReturn(listOf(recTitle1, recTitle2, recTitle3)) - whenever(recommendationViewHolder.mediaSubtitles) - .thenReturn(listOf(recSubtitle1, recSubtitle2, recSubtitle3)) - whenever(recommendationViewHolder.mediaProgressBars) - .thenReturn(listOf(recProgressBar1, recProgressBar2, recProgressBar3)) - whenever(coverItem.imageMatrix).thenReturn(matrix) - - // set ids for recommendation containers - whenever(coverContainer1.id).thenReturn(1) - whenever(coverContainer2.id).thenReturn(2) - whenever(coverContainer3.id).thenReturn(3) - - whenever(recommendationViewHolder.gutsViewHolder).thenReturn(gutsViewHolder) - - val actionIcon = Icon.createWithResource(context, R.drawable.ic_android) - whenever(smartspaceAction.icon).thenReturn(actionIcon) - - // Needed for card and item action click - val mockContext = mock(Context::class.java) - whenever(view.context).thenReturn(mockContext) - whenever(coverContainer1.context).thenReturn(mockContext) - whenever(coverContainer2.context).thenReturn(mockContext) - whenever(coverContainer3.context).thenReturn(mockContext) - } - @After fun tearDown() { session.release() @@ -1488,169 +1390,6 @@ public class MediaControlPanelTest : SysuiTestCase() { /* ***** END guts tests for the player ***** */ - /* ***** Guts tests for the recommendations ***** */ - - @Test - fun recommendations_longClick_isFalse() { - whenever(falsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)).thenReturn(true) - player.attachRecommendation(recommendationViewHolder) - player.bindRecommendation(smartspaceData) - - val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java) - verify(viewHolder.player).onLongClickListener = captor.capture() - - captor.value.onLongClick(viewHolder.player) - verify(mediaViewController, never()).openGuts() - verify(mediaViewController, never()).closeGuts() - } - - @Test - fun recommendations_longClickWhenGutsClosed_gutsOpens() { - player.attachRecommendation(recommendationViewHolder) - player.bindRecommendation(smartspaceData) - whenever(mediaViewController.isGutsVisible).thenReturn(false) - - val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java) - verify(viewHolder.player).onLongClickListener = captor.capture() - - captor.value.onLongClick(viewHolder.player) - verify(mediaViewController).openGuts() - verify(logger).logLongPressOpen(anyInt(), eq(PACKAGE), eq(instanceId)) - } - - @Test - fun recommendations_longClickWhenGutsOpen_gutsCloses() { - player.attachRecommendation(recommendationViewHolder) - player.bindRecommendation(smartspaceData) - whenever(mediaViewController.isGutsVisible).thenReturn(true) - - val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java) - verify(viewHolder.player).onLongClickListener = captor.capture() - - captor.value.onLongClick(viewHolder.player) - verify(mediaViewController, never()).openGuts() - verify(mediaViewController).closeGuts(false) - } - - @Test - fun recommendations_cancelButtonClick_animation() { - player.attachRecommendation(recommendationViewHolder) - player.bindRecommendation(smartspaceData) - - cancel.callOnClick() - - verify(mediaViewController).closeGuts(false) - } - - @Test - fun recommendations_settingsButtonClick() { - player.attachRecommendation(recommendationViewHolder) - player.bindRecommendation(smartspaceData) - - settings.callOnClick() - verify(logger).logLongPressSettings(anyInt(), eq(PACKAGE), eq(instanceId)) - - val captor = ArgumentCaptor.forClass(Intent::class.java) - verify(activityStarter).startActivity(captor.capture(), eq(true)) - - assertThat(captor.value.action).isEqualTo(ACTION_MEDIA_CONTROLS_SETTINGS) - } - - @Test - fun recommendations_dismissButtonClick() { - val mediaKey = "key for dismissal" - player.attachRecommendation(recommendationViewHolder) - player.bindRecommendation(smartspaceData.copy(targetId = mediaKey)) - - assertThat(dismiss.isEnabled).isEqualTo(true) - dismiss.callOnClick() - verify(logger).logLongPressDismiss(anyInt(), eq(PACKAGE), eq(instanceId)) - verify(mediaDataManager).dismissSmartspaceRecommendation(eq(mediaKey), anyLong()) - } - - @Test - fun recommendation_gutsOpen_contentDescriptionIsForGuts() { - whenever(mediaViewController.isGutsVisible).thenReturn(true) - player.attachRecommendation(recommendationViewHolder) - - val gutsTextString = "gutsText" - whenever(gutsText.text).thenReturn(gutsTextString) - player.bindRecommendation(smartspaceData) - - val descriptionCaptor = ArgumentCaptor.forClass(CharSequence::class.java) - verify(viewHolder.player).contentDescription = descriptionCaptor.capture() - val description = descriptionCaptor.value.toString() - - assertThat(description).isEqualTo(gutsTextString) - } - - @Test - fun recommendation_gutsClosed_contentDescriptionIsForPlayer() { - whenever(mediaViewController.isGutsVisible).thenReturn(false) - player.attachRecommendation(recommendationViewHolder) - - player.bindRecommendation(smartspaceData) - - val descriptionCaptor = ArgumentCaptor.forClass(CharSequence::class.java) - verify(viewHolder.player).contentDescription = descriptionCaptor.capture() - val description = descriptionCaptor.value.toString() - - assertThat(description) - .isEqualTo(context.getString(R.string.controls_media_smartspace_rec_header)) - } - - @Test - fun recommendation_gutsChangesFromOpenToClosed_contentDescriptionUpdated() { - // Start out open - whenever(mediaViewController.isGutsVisible).thenReturn(true) - whenever(gutsText.text).thenReturn("gutsText") - player.attachRecommendation(recommendationViewHolder) - player.bindRecommendation(smartspaceData) - - // Update to closed by long pressing - val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java) - verify(viewHolder.player).onLongClickListener = captor.capture() - reset(viewHolder.player) - - whenever(mediaViewController.isGutsVisible).thenReturn(false) - captor.value.onLongClick(viewHolder.player) - - // Then content description is now the player content description - val descriptionCaptor = ArgumentCaptor.forClass(CharSequence::class.java) - verify(viewHolder.player).contentDescription = descriptionCaptor.capture() - val description = descriptionCaptor.value.toString() - - assertThat(description) - .isEqualTo(context.getString(R.string.controls_media_smartspace_rec_header)) - } - - @Test - fun recommendation_gutsChangesFromClosedToOpen_contentDescriptionUpdated() { - // Start out closed - whenever(mediaViewController.isGutsVisible).thenReturn(false) - val gutsTextString = "gutsText" - whenever(gutsText.text).thenReturn(gutsTextString) - player.attachRecommendation(recommendationViewHolder) - player.bindRecommendation(smartspaceData) - - // Update to open by long pressing - val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java) - verify(viewHolder.player).onLongClickListener = captor.capture() - reset(viewHolder.player) - - whenever(mediaViewController.isGutsVisible).thenReturn(true) - captor.value.onLongClick(viewHolder.player) - - // Then content description is now the guts content description - val descriptionCaptor = ArgumentCaptor.forClass(CharSequence::class.java) - verify(viewHolder.player).contentDescription = descriptionCaptor.capture() - val description = descriptionCaptor.value.toString() - - assertThat(description).isEqualTo(gutsTextString) - } - - /* ***** END guts tests for the recommendations ***** */ - @Test fun actionPlayPauseClick_isLogged() { val semanticActions = @@ -1887,578 +1626,6 @@ public class MediaControlPanelTest : SysuiTestCase() { } @Test - fun recommendation_gutsClosed_longPressOpens() { - player.attachRecommendation(recommendationViewHolder) - player.bindRecommendation(smartspaceData) - whenever(mediaViewController.isGutsVisible).thenReturn(false) - - val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java) - verify(recommendationViewHolder.recommendations).setOnLongClickListener(captor.capture()) - - captor.value.onLongClick(recommendationViewHolder.recommendations) - verify(mediaViewController).openGuts() - verify(logger).logLongPressOpen(anyInt(), eq(PACKAGE), eq(instanceId)) - } - - @Test - fun recommendation_settingsButtonClick_isLogged() { - player.attachRecommendation(recommendationViewHolder) - player.bindRecommendation(smartspaceData) - - settings.callOnClick() - verify(logger).logLongPressSettings(anyInt(), eq(PACKAGE), eq(instanceId)) - - val captor = ArgumentCaptor.forClass(Intent::class.java) - verify(activityStarter).startActivity(captor.capture(), eq(true)) - - assertThat(captor.value.action).isEqualTo(ACTION_MEDIA_CONTROLS_SETTINGS) - } - - @Test - fun recommendation_dismissButton_isLogged() { - player.attachRecommendation(recommendationViewHolder) - player.bindRecommendation(smartspaceData) - - dismiss.callOnClick() - verify(logger).logLongPressDismiss(anyInt(), eq(PACKAGE), eq(instanceId)) - } - - @Test - fun recommendation_tapOnCard_isLogged() { - val captor = ArgumentCaptor.forClass(View.OnClickListener::class.java) - player.attachRecommendation(recommendationViewHolder) - player.bindRecommendation(smartspaceData) - - verify(recommendationViewHolder.recommendations).setOnClickListener(captor.capture()) - captor.value.onClick(recommendationViewHolder.recommendations) - - verify(logger).logRecommendationCardTap(eq(PACKAGE), eq(instanceId)) - } - - @Test - fun recommendation_tapOnItem_isLogged() { - val captor = ArgumentCaptor.forClass(View.OnClickListener::class.java) - player.attachRecommendation(recommendationViewHolder) - player.bindRecommendation(smartspaceData) - - verify(coverContainer1).setOnClickListener(captor.capture()) - captor.value.onClick(recommendationViewHolder.recommendations) - - verify(logger).logRecommendationItemTap(eq(PACKAGE), eq(instanceId), eq(0)) - } - - @Test - fun bindRecommendation_listHasTooFewRecs_notDisplayed() { - player.attachRecommendation(recommendationViewHolder) - val icon = - Icon.createWithResource(context, com.android.settingslib.R.drawable.ic_1x_mobiledata) - val data = - smartspaceData.copy( - recommendations = - listOf( - SmartspaceAction.Builder("id1", "title1") - .setSubtitle("subtitle1") - .setIcon(icon) - .setExtras(Bundle.EMPTY) - .build(), - SmartspaceAction.Builder("id2", "title2") - .setSubtitle("subtitle2") - .setIcon(icon) - .setExtras(Bundle.EMPTY) - .build(), - ) - ) - - player.bindRecommendation(data) - - assertThat(recTitle1.text).isEqualTo("") - verify(mediaViewController, never()).refreshState() - } - - @Test - fun bindRecommendation_listHasTooFewRecsWithIcons_notDisplayed() { - player.attachRecommendation(recommendationViewHolder) - val icon = - Icon.createWithResource(context, com.android.settingslib.R.drawable.ic_1x_mobiledata) - val data = - smartspaceData.copy( - recommendations = - listOf( - SmartspaceAction.Builder("id1", "title1") - .setSubtitle("subtitle1") - .setIcon(icon) - .setExtras(Bundle.EMPTY) - .build(), - SmartspaceAction.Builder("id2", "title2") - .setSubtitle("subtitle2") - .setIcon(icon) - .setExtras(Bundle.EMPTY) - .build(), - SmartspaceAction.Builder("id2", "empty icon 1") - .setSubtitle("subtitle2") - .setIcon(null) - .setExtras(Bundle.EMPTY) - .build(), - SmartspaceAction.Builder("id2", "empty icon 2") - .setSubtitle("subtitle2") - .setIcon(null) - .setExtras(Bundle.EMPTY) - .build(), - ) - ) - - player.bindRecommendation(data) - - assertThat(recTitle1.text).isEqualTo("") - verify(mediaViewController, never()).refreshState() - } - - @Test - fun bindRecommendation_hasTitlesAndSubtitles() { - player.attachRecommendation(recommendationViewHolder) - - val title1 = "Title1" - val title2 = "Title2" - val title3 = "Title3" - val subtitle1 = "Subtitle1" - val subtitle2 = "Subtitle2" - val subtitle3 = "Subtitle3" - val icon = - Icon.createWithResource(context, com.android.settingslib.R.drawable.ic_1x_mobiledata) - - val data = - smartspaceData.copy( - recommendations = - listOf( - SmartspaceAction.Builder("id1", title1) - .setSubtitle(subtitle1) - .setIcon(icon) - .setExtras(Bundle.EMPTY) - .build(), - SmartspaceAction.Builder("id2", title2) - .setSubtitle(subtitle2) - .setIcon(icon) - .setExtras(Bundle.EMPTY) - .build(), - SmartspaceAction.Builder("id3", title3) - .setSubtitle(subtitle3) - .setIcon(icon) - .setExtras(Bundle.EMPTY) - .build(), - ) - ) - player.bindRecommendation(data) - - assertThat(recTitle1.text).isEqualTo(title1) - assertThat(recTitle2.text).isEqualTo(title2) - assertThat(recTitle3.text).isEqualTo(title3) - assertThat(recSubtitle1.text).isEqualTo(subtitle1) - assertThat(recSubtitle2.text).isEqualTo(subtitle2) - assertThat(recSubtitle3.text).isEqualTo(subtitle3) - } - - @Test - fun bindRecommendation_noTitle_subtitleNotShown() { - player.attachRecommendation(recommendationViewHolder) - - val data = - smartspaceData.copy( - recommendations = - listOf( - SmartspaceAction.Builder("id1", "") - .setSubtitle("fake subtitle") - .setIcon( - Icon.createWithResource( - context, - com.android.settingslib.R.drawable.ic_1x_mobiledata, - ) - ) - .setExtras(Bundle.EMPTY) - .build() - ) - ) - player.bindRecommendation(data) - - assertThat(recSubtitle1.text).isEqualTo("") - } - - @Test - fun bindRecommendation_someHaveTitles_allTitleViewsShown() { - useRealConstraintSets() - player.attachRecommendation(recommendationViewHolder) - - val icon = - Icon.createWithResource(context, com.android.settingslib.R.drawable.ic_1x_mobiledata) - val data = - smartspaceData.copy( - recommendations = - listOf( - SmartspaceAction.Builder("id1", "") - .setSubtitle("fake subtitle") - .setIcon(icon) - .setExtras(Bundle.EMPTY) - .build(), - SmartspaceAction.Builder("id2", "title2") - .setSubtitle("fake subtitle") - .setIcon(icon) - .setExtras(Bundle.EMPTY) - .build(), - SmartspaceAction.Builder("id3", "") - .setSubtitle("fake subtitle") - .setIcon(icon) - .setExtras(Bundle.EMPTY) - .build(), - ) - ) - player.bindRecommendation(data) - - assertThat(expandedSet.getVisibility(recTitle1.id)).isEqualTo(ConstraintSet.VISIBLE) - assertThat(expandedSet.getVisibility(recTitle2.id)).isEqualTo(ConstraintSet.VISIBLE) - assertThat(expandedSet.getVisibility(recTitle3.id)).isEqualTo(ConstraintSet.VISIBLE) - } - - @Test - fun bindRecommendation_someHaveSubtitles_allSubtitleViewsShown() { - useRealConstraintSets() - player.attachRecommendation(recommendationViewHolder) - - val icon = - Icon.createWithResource(context, com.android.settingslib.R.drawable.ic_1x_mobiledata) - val data = - smartspaceData.copy( - recommendations = - listOf( - SmartspaceAction.Builder("id1", "") - .setSubtitle("") - .setIcon(icon) - .setExtras(Bundle.EMPTY) - .build(), - SmartspaceAction.Builder("id2", "title2") - .setSubtitle("subtitle2") - .setIcon(icon) - .setExtras(Bundle.EMPTY) - .build(), - SmartspaceAction.Builder("id3", "title3") - .setSubtitle("") - .setIcon(icon) - .setExtras(Bundle.EMPTY) - .build(), - ) - ) - player.bindRecommendation(data) - - assertThat(expandedSet.getVisibility(recSubtitle1.id)).isEqualTo(ConstraintSet.VISIBLE) - assertThat(expandedSet.getVisibility(recSubtitle2.id)).isEqualTo(ConstraintSet.VISIBLE) - assertThat(expandedSet.getVisibility(recSubtitle3.id)).isEqualTo(ConstraintSet.VISIBLE) - } - - @Test - fun bindRecommendation_noneHaveSubtitles_subtitleViewsGone() { - useRealConstraintSets() - player.attachRecommendation(recommendationViewHolder) - val data = - smartspaceData.copy( - recommendations = - listOf( - SmartspaceAction.Builder("id1", "title1") - .setSubtitle("") - .setIcon( - Icon.createWithResource( - context, - com.android.settingslib.R.drawable.ic_1x_mobiledata, - ) - ) - .setExtras(Bundle.EMPTY) - .build(), - SmartspaceAction.Builder("id2", "title2") - .setSubtitle("") - .setIcon(Icon.createWithResource(context, R.drawable.ic_alarm)) - .setExtras(Bundle.EMPTY) - .build(), - SmartspaceAction.Builder("id3", "title3") - .setSubtitle("") - .setIcon( - Icon.createWithResource( - context, - com.android.settingslib.R.drawable.ic_3g_mobiledata, - ) - ) - .setExtras(Bundle.EMPTY) - .build(), - ) - ) - - player.bindRecommendation(data) - - assertThat(expandedSet.getVisibility(recSubtitle1.id)).isEqualTo(ConstraintSet.GONE) - assertThat(expandedSet.getVisibility(recSubtitle2.id)).isEqualTo(ConstraintSet.GONE) - assertThat(expandedSet.getVisibility(recSubtitle3.id)).isEqualTo(ConstraintSet.GONE) - } - - @Test - fun bindRecommendation_noneHaveTitles_titleAndSubtitleViewsGone() { - useRealConstraintSets() - player.attachRecommendation(recommendationViewHolder) - val data = - smartspaceData.copy( - recommendations = - listOf( - SmartspaceAction.Builder("id1", "") - .setSubtitle("subtitle1") - .setIcon( - Icon.createWithResource( - context, - com.android.settingslib.R.drawable.ic_1x_mobiledata, - ) - ) - .setExtras(Bundle.EMPTY) - .build(), - SmartspaceAction.Builder("id2", "") - .setSubtitle("subtitle2") - .setIcon(Icon.createWithResource(context, R.drawable.ic_alarm)) - .setExtras(Bundle.EMPTY) - .build(), - SmartspaceAction.Builder("id3", "") - .setSubtitle("subtitle3") - .setIcon( - Icon.createWithResource( - context, - com.android.settingslib.R.drawable.ic_3g_mobiledata, - ) - ) - .setExtras(Bundle.EMPTY) - .build(), - ) - ) - - player.bindRecommendation(data) - - assertThat(expandedSet.getVisibility(recTitle1.id)).isEqualTo(ConstraintSet.GONE) - assertThat(expandedSet.getVisibility(recTitle2.id)).isEqualTo(ConstraintSet.GONE) - assertThat(expandedSet.getVisibility(recTitle3.id)).isEqualTo(ConstraintSet.GONE) - assertThat(expandedSet.getVisibility(recSubtitle1.id)).isEqualTo(ConstraintSet.GONE) - assertThat(expandedSet.getVisibility(recSubtitle2.id)).isEqualTo(ConstraintSet.GONE) - assertThat(expandedSet.getVisibility(recSubtitle3.id)).isEqualTo(ConstraintSet.GONE) - assertThat(collapsedSet.getVisibility(recTitle1.id)).isEqualTo(ConstraintSet.GONE) - assertThat(collapsedSet.getVisibility(recTitle2.id)).isEqualTo(ConstraintSet.GONE) - assertThat(collapsedSet.getVisibility(recTitle3.id)).isEqualTo(ConstraintSet.GONE) - assertThat(collapsedSet.getVisibility(recSubtitle1.id)).isEqualTo(ConstraintSet.GONE) - assertThat(collapsedSet.getVisibility(recSubtitle2.id)).isEqualTo(ConstraintSet.GONE) - assertThat(collapsedSet.getVisibility(recSubtitle3.id)).isEqualTo(ConstraintSet.GONE) - } - - @Test - fun bindRecommendation_setAfterExecutors() { - val albumArt = getColorIcon(Color.RED) - val data = - smartspaceData.copy( - recommendations = - listOf( - SmartspaceAction.Builder("id1", "title1") - .setSubtitle("subtitle1") - .setIcon(albumArt) - .setExtras(Bundle.EMPTY) - .build(), - SmartspaceAction.Builder("id2", "title2") - .setSubtitle("subtitle1") - .setIcon(albumArt) - .setExtras(Bundle.EMPTY) - .build(), - SmartspaceAction.Builder("id3", "title3") - .setSubtitle("subtitle1") - .setIcon(albumArt) - .setExtras(Bundle.EMPTY) - .build(), - ) - ) - - player.attachRecommendation(recommendationViewHolder) - player.bindRecommendation(data) - bgExecutor.runAllReady() - mainExecutor.runAllReady() - - verify(recCardTitle).setTextColor(any<Int>()) - verify(recAppIconItem, times(3)).setImageDrawable(any<Drawable>()) - verify(coverItem, times(3)).setImageDrawable(any<Drawable>()) - verify(coverItem, times(3)).imageMatrix = any() - } - - @Test - fun bindRecommendationWithProgressBars() { - useRealConstraintSets() - val albumArt = getColorIcon(Color.RED) - val bundle = - Bundle().apply { - putInt( - MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS, - MediaConstants.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED, - ) - putDouble(MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE, 0.5) - } - val data = - smartspaceData.copy( - recommendations = - listOf( - SmartspaceAction.Builder("id1", "title1") - .setSubtitle("subtitle1") - .setIcon(albumArt) - .setExtras(bundle) - .build(), - SmartspaceAction.Builder("id2", "title2") - .setSubtitle("subtitle1") - .setIcon(albumArt) - .setExtras(Bundle.EMPTY) - .build(), - SmartspaceAction.Builder("id3", "title3") - .setSubtitle("subtitle1") - .setIcon(albumArt) - .setExtras(Bundle.EMPTY) - .build(), - ) - ) - - player.attachRecommendation(recommendationViewHolder) - player.bindRecommendation(data) - - verify(recProgressBar1).setProgress(50) - verify(recProgressBar1).visibility = View.VISIBLE - verify(recProgressBar2).visibility = View.GONE - verify(recProgressBar3).visibility = View.GONE - assertThat(recSubtitle1.visibility).isEqualTo(View.GONE) - assertThat(recSubtitle2.visibility).isEqualTo(View.VISIBLE) - assertThat(recSubtitle3.visibility).isEqualTo(View.VISIBLE) - } - - @Test - fun bindRecommendation_carouselNotFitThreeRecs_OrientationPortrait() { - useRealConstraintSets() - val albumArt = getColorIcon(Color.RED) - val data = - smartspaceData.copy( - recommendations = - listOf( - SmartspaceAction.Builder("id1", "title1") - .setSubtitle("subtitle1") - .setIcon(albumArt) - .setExtras(Bundle.EMPTY) - .build(), - SmartspaceAction.Builder("id2", "title2") - .setSubtitle("subtitle1") - .setIcon(albumArt) - .setExtras(Bundle.EMPTY) - .build(), - SmartspaceAction.Builder("id3", "title3") - .setSubtitle("subtitle1") - .setIcon(albumArt) - .setExtras(Bundle.EMPTY) - .build(), - ) - ) - - // set the screen width less than the width of media controls. - player.context.resources.configuration.screenWidthDp = 350 - player.context.resources.configuration.orientation = Configuration.ORIENTATION_PORTRAIT - player.attachRecommendation(recommendationViewHolder) - player.bindRecommendation(data) - - val res = player.context.resources - val displayAvailableWidth = - TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 350f, res.displayMetrics).toInt() - val recCoverWidth: Int = - (res.getDimensionPixelSize(R.dimen.qs_media_rec_album_width) + - res.getDimensionPixelSize(R.dimen.qs_media_info_spacing) * 2) - val numOfRecs = displayAvailableWidth / recCoverWidth - - assertThat(player.numberOfFittedRecommendations).isEqualTo(numOfRecs) - recommendationViewHolder.mediaCoverContainers.forEachIndexed { index, container -> - if (index < numOfRecs) { - assertThat(expandedSet.getVisibility(container.id)).isEqualTo(ConstraintSet.VISIBLE) - assertThat(collapsedSet.getVisibility(container.id)) - .isEqualTo(ConstraintSet.VISIBLE) - } else { - assertThat(expandedSet.getVisibility(container.id)).isEqualTo(ConstraintSet.GONE) - assertThat(collapsedSet.getVisibility(container.id)).isEqualTo(ConstraintSet.GONE) - } - } - } - - @Test - fun bindRecommendation_carouselNotFitThreeRecs_OrientationLandscape() { - useRealConstraintSets() - val albumArt = getColorIcon(Color.RED) - val data = - smartspaceData.copy( - recommendations = - listOf( - SmartspaceAction.Builder("id1", "title1") - .setSubtitle("subtitle1") - .setIcon(albumArt) - .setExtras(Bundle.EMPTY) - .build(), - SmartspaceAction.Builder("id2", "title2") - .setSubtitle("subtitle1") - .setIcon(albumArt) - .setExtras(Bundle.EMPTY) - .build(), - SmartspaceAction.Builder("id3", "title3") - .setSubtitle("subtitle1") - .setIcon(albumArt) - .setExtras(Bundle.EMPTY) - .build(), - ) - ) - - // set the screen width less than the width of media controls. - // We should have dp width less than 378 to test. In landscape we should have 2x. - player.context.resources.configuration.screenWidthDp = 700 - player.context.resources.configuration.orientation = Configuration.ORIENTATION_LANDSCAPE - player.attachRecommendation(recommendationViewHolder) - player.bindRecommendation(data) - - val res = player.context.resources - val displayAvailableWidth = - TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 350f, res.displayMetrics).toInt() - val recCoverWidth: Int = - (res.getDimensionPixelSize(R.dimen.qs_media_rec_album_width) + - res.getDimensionPixelSize(R.dimen.qs_media_info_spacing) * 2) - val numOfRecs = displayAvailableWidth / recCoverWidth - - assertThat(player.numberOfFittedRecommendations).isEqualTo(numOfRecs) - recommendationViewHolder.mediaCoverContainers.forEachIndexed { index, container -> - if (index < numOfRecs) { - assertThat(expandedSet.getVisibility(container.id)).isEqualTo(ConstraintSet.VISIBLE) - assertThat(collapsedSet.getVisibility(container.id)) - .isEqualTo(ConstraintSet.VISIBLE) - } else { - assertThat(expandedSet.getVisibility(container.id)).isEqualTo(ConstraintSet.GONE) - assertThat(collapsedSet.getVisibility(container.id)).isEqualTo(ConstraintSet.GONE) - } - } - } - - @Test - fun addTwoRecommendationGradients_differentStates() { - // Setup redArtwork and its color scheme. - val redArt = getColorIcon(Color.RED) - val redWallpaperColor = player.getWallpaperColor(redArt) - val redColorScheme = ColorScheme(redWallpaperColor, true, Style.CONTENT) - - // Setup greenArt and its color scheme. - val greenArt = getColorIcon(Color.GREEN) - val greenWallpaperColor = player.getWallpaperColor(greenArt) - val greenColorScheme = ColorScheme(greenWallpaperColor, true, Style.CONTENT) - - // Add gradient to both icons. - val redArtwork = player.addGradientToRecommendationAlbum(redArt, redColorScheme, 10, 10) - val greenArtwork = - player.addGradientToRecommendationAlbum(greenArt, greenColorScheme, 10, 10) - - // They should have different constant states as they have different gradient color. - assertThat(redArtwork.getDrawable(1).constantState) - .isNotEqualTo(greenArtwork.getDrawable(1).constantState) - } - - @Test fun onButtonClick_playsTouchRipple() { val semanticActions = MediaButton( diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt index e765b6f77155..760f73c726a2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt @@ -42,7 +42,6 @@ import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.media.controls.ui.view.GutsViewHolder import com.android.systemui.media.controls.ui.view.MediaHost import com.android.systemui.media.controls.ui.view.MediaViewHolder -import com.android.systemui.media.controls.ui.view.RecommendationViewHolder import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel import com.android.systemui.res.R import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffectView @@ -79,7 +78,6 @@ class MediaViewControllerTest : SysuiTestCase() { private val configurationController = com.android.systemui.statusbar.phone.ConfigurationControllerImpl(context) private var player = TransitionLayout(context, /* attrs */ null, /* defStyleAttr */ 0) - private var recommendation = TransitionLayout(context, /* attrs */ null, /* defStyleAttr */ 0) private val clock = FakeSystemClock() private lateinit var mainExecutor: FakeExecutor private lateinit var seekBar: SeekBar @@ -110,9 +108,6 @@ class MediaViewControllerTest : SysuiTestCase() { @Mock private lateinit var mockCopiedState: TransitionViewState @Mock private lateinit var detailWidgetState: WidgetState @Mock private lateinit var controlWidgetState: WidgetState - @Mock private lateinit var mediaTitleWidgetState: WidgetState - @Mock private lateinit var mediaSubTitleWidgetState: WidgetState - @Mock private lateinit var mediaContainerWidgetState: WidgetState @Mock private lateinit var seekBarViewModel: SeekBarViewModel @Mock private lateinit var seekBarData: LiveData<SeekBarViewModel.Progress> @Mock private lateinit var globalSettings: GlobalSettings @@ -145,7 +140,7 @@ class MediaViewControllerTest : SysuiTestCase() { context: Context, animId: Int, motionInterpolator: Interpolator?, - vararg targets: View? + vararg targets: View?, ): AnimatorSet { return mockAnimator } @@ -158,7 +153,7 @@ class MediaViewControllerTest : SysuiTestCase() { fun testOrientationChanged_heightOfPlayerIsUpdated() { val newConfig = Configuration() - mediaViewController.attach(player, MediaViewController.TYPE.PLAYER) + mediaViewController.attach(player) // Change the height to see the effect of orientation change. MediaViewHolder.backgroundIds.forEach { id -> mediaViewController.expandedLayout.getConstraint(id).layout.mHeight = 10 @@ -177,30 +172,8 @@ class MediaViewControllerTest : SysuiTestCase() { } @Test - fun testOrientationChanged_heightOfRecCardIsUpdated() { - val newConfig = Configuration() - - mediaViewController.attach(recommendation, MediaViewController.TYPE.RECOMMENDATION) - // Change the height to see the effect of orientation change. - mediaViewController.expandedLayout - .getConstraint(RecommendationViewHolder.backgroundId) - .layout - .mHeight = 10 - newConfig.orientation = ORIENTATION_LANDSCAPE - configurationController.onConfigurationChanged(newConfig) - - assertTrue( - mediaViewController.expandedLayout - .getConstraint(RecommendationViewHolder.backgroundId) - .layout - .mHeight == - context.resources.getDimensionPixelSize(R.dimen.qs_media_session_height_expanded) - ) - } - - @Test fun testObtainViewState_applySquishFraction_toPlayerTransitionViewState_height() { - mediaViewController.attach(player, MediaViewController.TYPE.PLAYER) + mediaViewController.attach(player) player.measureState = TransitionViewState().apply { this.height = 100 @@ -224,29 +197,8 @@ class MediaViewControllerTest : SysuiTestCase() { } @Test - fun testObtainViewState_applySquishFraction_toRecommendationTransitionViewState_height() { - mediaViewController.attach(recommendation, MediaViewController.TYPE.RECOMMENDATION) - recommendation.measureState = TransitionViewState().apply { this.height = 100 } - mediaHostStateHolder.expansion = 1f - val widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY) - val heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY) - mediaHostStateHolder.measurementInput = - MeasurementInput(widthMeasureSpec, heightMeasureSpec) - - // Test no squish - mediaHostStateHolder.squishFraction = 1f - assertTrue(mediaViewController.obtainViewState(mediaHostStateHolder)!!.height == 100) - assertTrue(mediaViewController.obtainViewState(mediaHostStateHolder)!!.measureHeight == 100) - - // Test half squish - mediaHostStateHolder.squishFraction = 0.5f - assertTrue(mediaViewController.obtainViewState(mediaHostStateHolder)!!.height == 50) - assertTrue(mediaViewController.obtainViewState(mediaHostStateHolder)!!.measureHeight == 100) - } - - @Test fun testObtainViewState_expandedMatchesParentHeight() { - mediaViewController.attach(player, MediaViewController.TYPE.PLAYER) + mediaViewController.attach(player) player.measureState = TransitionViewState().apply { this.height = 100 @@ -283,7 +235,7 @@ class MediaViewControllerTest : SysuiTestCase() { .thenReturn( mutableMapOf( R.id.media_progress_bar to controlWidgetState, - R.id.header_artist to detailWidgetState + R.id.header_artist to detailWidgetState, ) ) whenever(mockCopiedState.measureHeight).thenReturn(200) @@ -311,7 +263,7 @@ class MediaViewControllerTest : SysuiTestCase() { .thenReturn( mutableMapOf( R.id.media_progress_bar to controlWidgetState, - R.id.header_artist to detailWidgetState + R.id.header_artist to detailWidgetState, ) ) whenever(mockCopiedState.measureHeight).thenReturn(200) @@ -332,46 +284,6 @@ class MediaViewControllerTest : SysuiTestCase() { verify(detailWidgetState, never()).alpha = floatThat { it > 0 } } - @Test - fun testSquishViewState_applySquishFraction_toTransitionViewState_alpha_forRecommendation() { - whenever(mockViewState.copy()).thenReturn(mockCopiedState) - whenever(mockCopiedState.widgetStates) - .thenReturn( - mutableMapOf( - R.id.media_title to mediaTitleWidgetState, - R.id.media_subtitle to mediaSubTitleWidgetState, - R.id.media_cover1_container to mediaContainerWidgetState - ) - ) - whenever(mockCopiedState.measureHeight).thenReturn(360) - // media container widgets occupy [20, 300] - whenever(mediaContainerWidgetState.y).thenReturn(20F) - whenever(mediaContainerWidgetState.height).thenReturn(280) - whenever(mediaContainerWidgetState.alpha).thenReturn(1F) - // media title widgets occupy [320, 330] - whenever(mediaTitleWidgetState.y).thenReturn(320F) - whenever(mediaTitleWidgetState.height).thenReturn(10) - whenever(mediaTitleWidgetState.alpha).thenReturn(1F) - // media subtitle widgets occupy [340, 350] - whenever(mediaSubTitleWidgetState.y).thenReturn(340F) - whenever(mediaSubTitleWidgetState.height).thenReturn(10) - whenever(mediaSubTitleWidgetState.alpha).thenReturn(1F) - - // in current beizer, when the progress reach 0.38, the result will be 0.5 - mediaViewController.squishViewState(mockViewState, 307.6F / 360F) - verify(mediaContainerWidgetState).alpha = floatThat { kotlin.math.abs(it - 0.5F) < delta } - mediaViewController.squishViewState(mockViewState, 320F / 360F) - verify(mediaContainerWidgetState).alpha = floatThat { kotlin.math.abs(it - 1.0F) < delta } - // media title and media subtitle are in same widget group, should be calculate together and - // have same alpha - mediaViewController.squishViewState(mockViewState, 353.8F / 360F) - verify(mediaTitleWidgetState).alpha = floatThat { kotlin.math.abs(it - 0.5F) < delta } - verify(mediaSubTitleWidgetState).alpha = floatThat { kotlin.math.abs(it - 0.5F) < delta } - mediaViewController.squishViewState(mockViewState, 360F / 360F) - verify(mediaTitleWidgetState).alpha = floatThat { kotlin.math.abs(it - 1.0F) < delta } - verify(mediaSubTitleWidgetState).alpha = floatThat { kotlin.math.abs(it - 1.0F) < delta } - } - @EnableSceneContainer @Test fun attachPlayer_seekBarDisabled_seekBarVisibilityIsSetToInvisible() { diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaRecommendationsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaRecommendationsInteractorKosmos.kt deleted file mode 100644 index 1edd405f4af6..000000000000 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaRecommendationsInteractorKosmos.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.media.controls.domain.pipeline.interactor - -import android.content.applicationContext -import com.android.systemui.broadcast.broadcastSender -import com.android.systemui.kosmos.Kosmos -import com.android.systemui.kosmos.applicationCoroutineScope -import com.android.systemui.media.controls.data.repository.mediaFilterRepository -import com.android.systemui.media.controls.domain.pipeline.mediaDataProcessor -import com.android.systemui.plugins.activityStarter - -val Kosmos.mediaRecommendationsInteractor by - Kosmos.Fixture { - MediaRecommendationsInteractor( - applicationScope = applicationCoroutineScope, - applicationContext = applicationContext, - repository = mediaFilterRepository, - mediaDataProcessor = mediaDataProcessor, - broadcastSender = broadcastSender, - activityStarter = activityStarter, - ) - } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelKosmos.kt index 5e6434d84538..976b4046f58d 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelKosmos.kt @@ -37,7 +37,6 @@ val Kosmos.mediaCarouselViewModel by visualStabilityProvider = visualStabilityProvider, interactor = mediaCarouselInteractor, controlInteractorFactory = mediaControlInteractorFactory, - recommendationsViewModel = mediaRecommendationsViewModel, logger = mediaUiEventLogger, mediaLogger = mediaLogger, ) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModelKosmos.kt deleted file mode 100644 index 34a527781979..000000000000 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModelKosmos.kt +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.media.controls.ui.viewmodel - -import android.content.applicationContext -import com.android.systemui.kosmos.Kosmos -import com.android.systemui.kosmos.testDispatcher -import com.android.systemui.media.controls.domain.pipeline.interactor.mediaRecommendationsInteractor -import com.android.systemui.media.controls.util.mediaUiEventLogger - -val Kosmos.mediaRecommendationsViewModel by - Kosmos.Fixture { - MediaRecommendationsViewModel( - applicationContext = applicationContext, - backgroundDispatcher = testDispatcher, - interactor = mediaRecommendationsInteractor, - logger = mediaUiEventLogger, - ) - } |