diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java index d9ee8f3f06b45..21cab57507c68 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java @@ -18,6 +18,8 @@ package com.android.systemui.media; import static android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS; +import static com.android.systemui.media.SmartspaceMediaDataKt.NUM_REQUIRED_RECOMMENDATIONS; + import android.animation.Animator; import android.animation.AnimatorInflater; import android.animation.AnimatorSet; @@ -102,7 +104,6 @@ public class MediaControlPanel { + ".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 int MEDIA_RECOMMENDATION_MAX_NUM = 3; 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 String KEY_SMARTSPACE_APP_NAME = "KEY_SMARTSPACE_APP_NAME"; @@ -949,16 +950,14 @@ public class MediaControlPanel { return; } + if (!data.isValid()) { + Log.e(TAG, "Received an invalid recommendation list; returning"); + return; + } + mSmartspaceId = SmallHash.hash(data.getTargetId()); mPackageName = data.getPackageName(); mInstanceId = data.getInstanceId(); - TransitionLayout recommendationCard = mRecommendationViewHolder.getRecommendations(); - - List mediaRecommendationList = data.getRecommendations(); - if (mediaRecommendationList == null || mediaRecommendationList.isEmpty()) { - Log.w(TAG, "Empty media recommendations"); - return; - } // Set up recommendation card's header. ApplicationInfo applicationInfo; @@ -994,6 +993,7 @@ public class MediaControlPanel { } // Set up media rec card's tap action if applicable. + TransitionLayout recommendationCard = mRecommendationViewHolder.getRecommendations(); setSmartspaceRecItemOnClickListener(recommendationCard, data.getCardAction(), /* interactedSubcardRank */ -1); // Set up media rec card's accessibility label. @@ -1002,29 +1002,20 @@ public class MediaControlPanel { List mediaCoverItems = mRecommendationViewHolder.getMediaCoverItems(); List mediaCoverContainers = mRecommendationViewHolder.getMediaCoverContainers(); - int mediaRecommendationNum = Math.min(mediaRecommendationList.size(), - MEDIA_RECOMMENDATION_MAX_NUM); + List recommendations = data.getValidRecommendations(); boolean hasTitle = false; boolean hasSubtitle = false; - int uiComponentIndex = 0; - for (int itemIndex = 0; - itemIndex < mediaRecommendationNum && uiComponentIndex < mediaRecommendationNum; - itemIndex++) { - SmartspaceAction recommendation = mediaRecommendationList.get(itemIndex); - if (recommendation.getIcon() == null) { - Log.w(TAG, "No media cover is provided. Skipping this item..."); - continue; - } + for (int itemIndex = 0; itemIndex < NUM_REQUIRED_RECOMMENDATIONS; itemIndex++) { + SmartspaceAction recommendation = recommendations.get(itemIndex); // Set up media item cover. - ImageView mediaCoverImageView = mediaCoverItems.get(uiComponentIndex); + ImageView mediaCoverImageView = mediaCoverItems.get(itemIndex); mediaCoverImageView.setImageIcon(recommendation.getIcon()); // Set up the media item's click listener if applicable. - ViewGroup mediaCoverContainer = mediaCoverContainers.get(uiComponentIndex); - setSmartspaceRecItemOnClickListener(mediaCoverContainer, recommendation, - uiComponentIndex); + ViewGroup mediaCoverContainer = mediaCoverContainers.get(itemIndex); + setSmartspaceRecItemOnClickListener(mediaCoverContainer, recommendation, itemIndex); // Bubble up the long-click event to the card. mediaCoverContainer.setOnLongClickListener(v -> { View parent = (View) v.getParent(); @@ -1053,8 +1044,7 @@ public class MediaControlPanel { // Set up title CharSequence title = recommendation.getTitle(); hasTitle |= !TextUtils.isEmpty(title); - TextView titleView = - mRecommendationViewHolder.getMediaTitles().get(uiComponentIndex); + TextView titleView = mRecommendationViewHolder.getMediaTitles().get(itemIndex); titleView.setText(title); // Set up subtitle @@ -1062,13 +1052,10 @@ public class MediaControlPanel { boolean shouldShowSubtitleText = !TextUtils.isEmpty(title); CharSequence subtitle = shouldShowSubtitleText ? recommendation.getSubtitle() : ""; hasSubtitle |= !TextUtils.isEmpty(subtitle); - TextView subtitleView = - mRecommendationViewHolder.getMediaSubtitles().get(uiComponentIndex); + TextView subtitleView = mRecommendationViewHolder.getMediaSubtitles().get(itemIndex); subtitleView.setText(subtitle); - - uiComponentIndex++; } - mSmartspaceMediaItemsCount = uiComponentIndex; + mSmartspaceMediaItemsCount = NUM_REQUIRED_RECOMMENDATIONS; // If there's no subtitles and/or titles for any of the albums, hide those views. ConstraintSet expandedSet = mMediaViewController.getExpandedLayout(); diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt index 647d3efa5916a..81efdf591b41a 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt @@ -166,7 +166,7 @@ class MediaDataFilter @Inject constructor( shouldPrioritizeMutable = true } - if (!data.isValid) { + if (!data.isValid()) { Log.d(TAG, "Invalid recommendation data. Skip showing the rec card") return } @@ -203,7 +203,6 @@ class MediaDataFilter @Inject constructor( if (smartspaceMediaData.isActive) { smartspaceMediaData = EMPTY_SMARTSPACE_MEDIA_DATA.copy( targetId = smartspaceMediaData.targetId, - isValid = smartspaceMediaData.isValid, instanceId = smartspaceMediaData.instanceId) } listeners.forEach { it.onSmartspaceMediaDataRemoved(key, immediately) } @@ -260,7 +259,6 @@ class MediaDataFilter @Inject constructor( } smartspaceMediaData = EMPTY_SMARTSPACE_MEDIA_DATA.copy( targetId = smartspaceMediaData.targetId, - isValid = smartspaceMediaData.isValid, instanceId = smartspaceMediaData.instanceId) mediaDataManager.dismissSmartspaceRecommendation(smartspaceMediaData.targetId, delay = 0L) @@ -272,13 +270,13 @@ class MediaDataFilter @Inject constructor( */ fun hasActiveMediaOrRecommendation() = userEntries.any { it.value.active } || - (smartspaceMediaData.isActive && smartspaceMediaData.isValid) + (smartspaceMediaData.isActive && smartspaceMediaData.isValid()) /** * Are there any media entries we should display? */ fun hasAnyMediaOrRecommendation() = userEntries.isNotEmpty() || - (smartspaceMediaData.isActive && smartspaceMediaData.isValid) + (smartspaceMediaData.isActive && smartspaceMediaData.isValid()) /** * Are there any media notifications active (excluding the recommendation)? diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt index b2751cec5d9ed..8aafd02603113 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt @@ -105,7 +105,6 @@ private val LOADING = MediaData( internal val EMPTY_SMARTSPACE_MEDIA_DATA = SmartspaceMediaData( targetId = "INVALID", isActive = false, - isValid = false, packageName = "INVALID", cardAction = null, recommendations = emptyList(), @@ -534,7 +533,7 @@ class MediaDataManager( * connection session. */ fun dismissSmartspaceRecommendation(key: String, delay: Long) { - if (smartspaceMediaData.targetId != key || !smartspaceMediaData.isValid) { + if (smartspaceMediaData.targetId != key || !smartspaceMediaData.isValid()) { // If this doesn't match, or we've already invalidated the data, no action needed return } @@ -1222,7 +1221,6 @@ class MediaDataManager( return SmartspaceMediaData( targetId = target.smartspaceTargetId, isActive = isActive, - isValid = true, packageName = it, cardAction = target.baseAction, recommendations = target.iconGrid, diff --git a/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaData.kt b/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaData.kt index 930c5a8de1253..50a96f6014434 100644 --- a/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaData.kt +++ b/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaData.kt @@ -30,10 +30,6 @@ data class SmartspaceMediaData( * Indicates if the status is active. */ val isActive: Boolean, - /** - * Indicates if all the required data field is valid. - */ - val isValid: Boolean, /** * Package name of the media recommendations' provider-app. */ @@ -58,4 +54,19 @@ data class SmartspaceMediaData( * Instance ID for [MediaUiEventLogger] */ val instanceId: InstanceId -) +) { + /** + * Indicates if all the data is valid. + * + * TODO(b/230333302): Make MediaControlPanel more flexible so that we can display fewer than + * [NUM_REQUIRED_RECOMMENDATIONS]. + */ + fun isValid() = getValidRecommendations().size >= NUM_REQUIRED_RECOMMENDATIONS + + /** + * Returns the list of [recommendations] that have valid data. + */ + fun getValidRecommendations() = recommendations.filter { it.icon != null } +} + +const val NUM_REQUIRED_RECOMMENDATIONS = 3 diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt index b8c85bb417264..2bf96a0a40039 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt @@ -270,7 +270,7 @@ public class MediaControlPanelTest : SysuiTestCase() { smartspaceData = EMPTY_SMARTSPACE_MEDIA_DATA.copy( packageName = PACKAGE, instanceId = instanceId, - recommendations = listOf(smartspaceAction), + recommendations = listOf(smartspaceAction, smartspaceAction, smartspaceAction), cardAction = smartspaceAction ) } @@ -1447,6 +1447,66 @@ public class MediaControlPanelTest : SysuiTestCase() { verify(logger).logRecommendationItemTap(eq(PACKAGE), eq(instanceId), eq(0)) } + @Test + fun bindRecommendation_listHasTooFewRecs_notDisplayed() { + player.attachRecommendation(recommendationViewHolder) + val icon = Icon.createWithResource(context, 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, 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) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt index 1f9490ab38515..6a532d74967f1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt @@ -110,7 +110,7 @@ class MediaDataFilterTest : SysuiTestCase() { `when`(smartspaceData.targetId).thenReturn(SMARTSPACE_KEY) `when`(smartspaceData.isActive).thenReturn(true) - `when`(smartspaceData.isValid).thenReturn(true) + `when`(smartspaceData.isValid()).thenReturn(true) `when`(smartspaceData.packageName).thenReturn(SMARTSPACE_PACKAGE) `when`(smartspaceData.recommendations).thenReturn(listOf(smartspaceMediaRecommendationItem)) `when`(smartspaceData.headphoneConnectionTimeMillis).thenReturn( @@ -196,24 +196,110 @@ class MediaDataFilterTest : SysuiTestCase() { } @Test - fun testHasAnyMediaOrRecommendation() { - assertThat(mediaDataFilter.hasAnyMediaOrRecommendation()).isFalse() + fun hasAnyMedia_noMediaSet_returnsFalse() { + assertThat(mediaDataFilter.hasAnyMedia()).isFalse() + } + @Test + fun hasAnyMedia_mediaSet_returnsTrue() { mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = dataMain) - assertThat(mediaDataFilter.hasAnyMediaOrRecommendation()).isTrue() + assertThat(mediaDataFilter.hasAnyMedia()).isTrue() } @Test - fun testHasActiveMediaOrRecommendation() { - assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse() - val data = dataMain.copy(active = true) + fun hasAnyMedia_recommendationSet_returnsFalse() { + mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) + assertThat(mediaDataFilter.hasAnyMedia()).isFalse() + } + + @Test + fun hasAnyMediaOrRecommendation_noMediaSet_returnsFalse() { + assertThat(mediaDataFilter.hasAnyMediaOrRecommendation()).isFalse() + } + + @Test + fun hasAnyMediaOrRecommendation_mediaSet_returnsTrue() { + mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = dataMain) + + assertThat(mediaDataFilter.hasAnyMediaOrRecommendation()).isTrue() + } + + @Test + fun hasAnyMediaOrRecommendation_recommendationSet_returnsTrue() { + mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) + + assertThat(mediaDataFilter.hasAnyMediaOrRecommendation()).isTrue() + } + + @Test + fun hasActiveMedia_noMediaSet_returnsFalse() { + assertThat(mediaDataFilter.hasActiveMedia()).isFalse() + } + + @Test + fun hasActiveMedia_inactiveMediaSet_returnsFalse() { + val data = dataMain.copy(active = false) mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = data) - assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isTrue() + + assertThat(mediaDataFilter.hasActiveMedia()).isFalse() + } + + @Test + fun hasActiveMedia_activeMediaSet_returnsTrue() { + val data = dataMain.copy(active = true) + mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = data) + assertThat(mediaDataFilter.hasActiveMedia()).isTrue() } + @Test + fun hasActiveMediaOrRecommendation_nothingSet_returnsFalse() { + assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse() + } + + @Test + fun hasActiveMediaOrRecommendation_inactiveMediaSet_returnsFalse() { + val data = dataMain.copy(active = false) + mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = data) + + assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse() + } + + @Test + fun hasActiveMediaOrRecommendation_activeMediaSet_returnsTrue() { + val data = dataMain.copy(active = true) + mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = data) + + assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isTrue() + } + + @Test + fun hasActiveMediaOrRecommendation_inactiveRecommendationSet_returnsFalse() { + `when`(smartspaceData.isActive).thenReturn(false) + mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) + + assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse() + } + + @Test + fun hasActiveMediaOrRecommendation_invalidRecommendationSet_returnsFalse() { + `when`(smartspaceData.isValid()).thenReturn(false) + mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) + + assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse() + } + + @Test + fun hasActiveMediaOrRecommendation_activeAndValidRecommendationSet_returnsTrue() { + `when`(smartspaceData.isActive).thenReturn(true) + `when`(smartspaceData.isValid()).thenReturn(true) + mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) + + assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isTrue() + } + @Test fun testHasAnyMediaOrRecommendation_onlyCurrentUser() { assertThat(mediaDataFilter.hasAnyMediaOrRecommendation()).isFalse() @@ -332,7 +418,7 @@ class MediaDataFilterTest : SysuiTestCase() { @Test fun testOnSmartspaceMediaDataLoaded_hasRecentMedia_activeInvalidRec_usesMedia() { - `when`(smartspaceData.isValid).thenReturn(false) + `when`(smartspaceData.isValid()).thenReturn(false) // WHEN we have media that was recently played, but not currently active val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime()) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt index 0cbceb6700b43..6d079895646c9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt @@ -7,6 +7,7 @@ import android.app.smartspace.SmartspaceAction import android.app.smartspace.SmartspaceTarget import android.content.Intent import android.graphics.Bitmap +import android.graphics.drawable.Icon import android.media.MediaDescription import android.media.MediaMetadata import android.media.session.MediaController @@ -95,6 +96,7 @@ class MediaDataManagerTest : SysuiTestCase() { lateinit var smartspaceMediaDataProvider: SmartspaceMediaDataProvider @Mock lateinit var mediaSmartspaceTarget: SmartspaceTarget @Mock private lateinit var mediaRecommendationItem: SmartspaceAction + lateinit var validRecommendationList: List @Mock private lateinit var mediaSmartspaceBaseAction: SmartspaceAction @Mock private lateinit var mediaFlags: MediaFlags @Mock private lateinit var logger: MediaUiEventLogger @@ -170,12 +172,17 @@ class MediaDataManagerTest : SysuiTestCase() { putString("package_name", PACKAGE_NAME) putParcelable("dismiss_intent", DISMISS_INTENT) } + val icon = Icon.createWithResource(context, android.R.drawable.ic_media_play) whenever(mediaSmartspaceBaseAction.extras).thenReturn(recommendationExtras) whenever(mediaSmartspaceTarget.baseAction).thenReturn(mediaSmartspaceBaseAction) whenever(mediaRecommendationItem.extras).thenReturn(recommendationExtras) + whenever(mediaRecommendationItem.icon).thenReturn(icon) + validRecommendationList = listOf( + mediaRecommendationItem, mediaRecommendationItem, mediaRecommendationItem + ) whenever(mediaSmartspaceTarget.smartspaceTargetId).thenReturn(KEY_MEDIA_SMARTSPACE) whenever(mediaSmartspaceTarget.featureType).thenReturn(SmartspaceTarget.FEATURE_MEDIA) - whenever(mediaSmartspaceTarget.iconGrid).thenReturn(listOf(mediaRecommendationItem)) + whenever(mediaSmartspaceTarget.iconGrid).thenReturn(validRecommendationList) whenever(mediaSmartspaceTarget.creationTimeMillis).thenReturn(1234L) whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(false) whenever(logger.getNewInstanceId()).thenReturn(instanceIdSequence.newInstanceId()) @@ -505,10 +512,9 @@ class MediaDataManagerTest : SysuiTestCase() { eq(SmartspaceMediaData( targetId = KEY_MEDIA_SMARTSPACE, isActive = true, - isValid = true, packageName = PACKAGE_NAME, cardAction = mediaSmartspaceBaseAction, - recommendations = listOf(mediaRecommendationItem), + recommendations = validRecommendationList, dismissIntent = DISMISS_INTENT, headphoneConnectionTimeMillis = 1234L, instanceId = InstanceId.fakeInstanceId(instanceId))), @@ -527,7 +533,6 @@ class MediaDataManagerTest : SysuiTestCase() { eq(EMPTY_SMARTSPACE_MEDIA_DATA.copy( targetId = KEY_MEDIA_SMARTSPACE, isActive = true, - isValid = false, dismissIntent = DISMISS_INTENT, headphoneConnectionTimeMillis = 1234L, instanceId = InstanceId.fakeInstanceId(instanceId))), @@ -553,7 +558,6 @@ class MediaDataManagerTest : SysuiTestCase() { eq(EMPTY_SMARTSPACE_MEDIA_DATA.copy( targetId = KEY_MEDIA_SMARTSPACE, isActive = true, - isValid = false, dismissIntent = null, headphoneConnectionTimeMillis = 1234L, instanceId = InstanceId.fakeInstanceId(instanceId))), diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/SmartspaceMediaDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/SmartspaceMediaDataTest.kt new file mode 100644 index 0000000000000..b5078bc37b840 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/media/SmartspaceMediaDataTest.kt @@ -0,0 +1,108 @@ +package com.android.systemui.media + +import android.app.smartspace.SmartspaceAction +import android.graphics.drawable.Icon +import androidx.test.filters.SmallTest +import com.android.internal.logging.InstanceId +import com.android.systemui.R +import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat +import org.junit.Test + +@SmallTest +class SmartspaceMediaDataTest : SysuiTestCase() { + + private val icon: Icon = Icon.createWithResource(context, R.drawable.ic_media_play) + + @Test + fun getValidRecommendations_onlyReturnsRecsWithIcons() { + val withIcon1 = SmartspaceAction.Builder("id", "title").setIcon(icon).build() + val withIcon2 = SmartspaceAction.Builder("id", "title").setIcon(icon).build() + val withoutIcon1 = SmartspaceAction.Builder("id", "title").setIcon(null).build() + val withoutIcon2 = SmartspaceAction.Builder("id", "title").setIcon(null).build() + val recommendations = listOf(withIcon1, withoutIcon1, withIcon2, withoutIcon2) + + val data = DEFAULT_DATA.copy(recommendations = recommendations) + + assertThat(data.getValidRecommendations()).isEqualTo(listOf(withIcon1, withIcon2)) + } + + @Test + fun isValid_emptyList_returnsFalse() { + val data = DEFAULT_DATA.copy(recommendations = listOf()) + + assertThat(data.isValid()).isFalse() + } + + @Test + fun isValid_tooFewRecs_returnsFalse() { + val data = DEFAULT_DATA.copy( + recommendations = listOf( + SmartspaceAction.Builder("id", "title").setIcon(icon).build() + ) + ) + + assertThat(data.isValid()).isFalse() + } + + @Test + fun isValid_tooFewRecsWithIcons_returnsFalse() { + val recommendations = mutableListOf() + // Add one fewer recommendation w/ icon than the number required + for (i in 1 until NUM_REQUIRED_RECOMMENDATIONS) { + recommendations.add( + SmartspaceAction.Builder("id", "title").setIcon(icon).build() + ) + } + for (i in 1 until 3) { + recommendations.add( + SmartspaceAction.Builder("id", "title").setIcon(null).build() + ) + } + + val data = DEFAULT_DATA.copy(recommendations = recommendations) + + assertThat(data.isValid()).isFalse() + } + + @Test + fun isValid_enoughRecsWithIcons_returnsTrue() { + val recommendations = mutableListOf() + // Add the number of required recommendations + for (i in 0 until NUM_REQUIRED_RECOMMENDATIONS) { + recommendations.add( + SmartspaceAction.Builder("id", "title").setIcon(icon).build() + ) + } + + val data = DEFAULT_DATA.copy(recommendations = recommendations) + + assertThat(data.isValid()).isTrue() + } + + @Test + fun isValid_manyRecsWithIcons_returnsTrue() { + val recommendations = mutableListOf() + // Add more than enough recommendations + for (i in 0 until NUM_REQUIRED_RECOMMENDATIONS + 3) { + recommendations.add( + SmartspaceAction.Builder("id", "title").setIcon(icon).build() + ) + } + + val data = DEFAULT_DATA.copy(recommendations = recommendations) + + assertThat(data.isValid()).isTrue() + } +} + +private val DEFAULT_DATA = SmartspaceMediaData( + targetId = "INVALID", + isActive = false, + packageName = "INVALID", + cardAction = null, + recommendations = emptyList(), + dismissIntent = null, + headphoneConnectionTimeMillis = 0, + instanceId = InstanceId.fakeInstanceId(-1) +)