Merge changes I4f49bb96,I8c8ee829 into tm-dev am: 0423dff207

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/17988653

Change-Id: Iac225e5b15e1f8ee1872d3196ef71b8b093fd807
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
TreeHugger Robot
2022-04-29 22:26:54 +00:00
committed by Automerger Merge Worker
8 changed files with 312 additions and 73 deletions

View File

@@ -18,6 +18,8 @@ package com.android.systemui.media;
import static android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS; 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.Animator;
import android.animation.AnimatorInflater; import android.animation.AnimatorInflater;
import android.animation.AnimatorSet; import android.animation.AnimatorSet;
@@ -102,7 +104,6 @@ public class MediaControlPanel {
+ ".android.apps.gsa.staticplugins.opa.smartspace.ExportedSmartspaceTrampolineActivity"; + ".android.apps.gsa.staticplugins.opa.smartspace.ExportedSmartspaceTrampolineActivity";
private static final String EXTRAS_SMARTSPACE_INTENT = private static final String EXTRAS_SMARTSPACE_INTENT =
"com.google.android.apps.gsa.smartspace.extra.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_ARTIST_NAME = "artist_name";
private static final String KEY_SMARTSPACE_OPEN_IN_FOREGROUND = "KEY_OPEN_IN_FOREGROUND"; 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"; private static final String KEY_SMARTSPACE_APP_NAME = "KEY_SMARTSPACE_APP_NAME";
@@ -949,16 +950,14 @@ public class MediaControlPanel {
return; return;
} }
if (!data.isValid()) {
Log.e(TAG, "Received an invalid recommendation list; returning");
return;
}
mSmartspaceId = SmallHash.hash(data.getTargetId()); mSmartspaceId = SmallHash.hash(data.getTargetId());
mPackageName = data.getPackageName(); mPackageName = data.getPackageName();
mInstanceId = data.getInstanceId(); mInstanceId = data.getInstanceId();
TransitionLayout recommendationCard = mRecommendationViewHolder.getRecommendations();
List<SmartspaceAction> mediaRecommendationList = data.getRecommendations();
if (mediaRecommendationList == null || mediaRecommendationList.isEmpty()) {
Log.w(TAG, "Empty media recommendations");
return;
}
// Set up recommendation card's header. // Set up recommendation card's header.
ApplicationInfo applicationInfo; ApplicationInfo applicationInfo;
@@ -994,6 +993,7 @@ public class MediaControlPanel {
} }
// Set up media rec card's tap action if applicable. // Set up media rec card's tap action if applicable.
TransitionLayout recommendationCard = mRecommendationViewHolder.getRecommendations();
setSmartspaceRecItemOnClickListener(recommendationCard, data.getCardAction(), setSmartspaceRecItemOnClickListener(recommendationCard, data.getCardAction(),
/* interactedSubcardRank */ -1); /* interactedSubcardRank */ -1);
// Set up media rec card's accessibility label. // Set up media rec card's accessibility label.
@@ -1002,29 +1002,20 @@ public class MediaControlPanel {
List<ImageView> mediaCoverItems = mRecommendationViewHolder.getMediaCoverItems(); List<ImageView> mediaCoverItems = mRecommendationViewHolder.getMediaCoverItems();
List<ViewGroup> mediaCoverContainers = mRecommendationViewHolder.getMediaCoverContainers(); List<ViewGroup> mediaCoverContainers = mRecommendationViewHolder.getMediaCoverContainers();
int mediaRecommendationNum = Math.min(mediaRecommendationList.size(), List<SmartspaceAction> recommendations = data.getValidRecommendations();
MEDIA_RECOMMENDATION_MAX_NUM);
boolean hasTitle = false; boolean hasTitle = false;
boolean hasSubtitle = false; boolean hasSubtitle = false;
int uiComponentIndex = 0; for (int itemIndex = 0; itemIndex < NUM_REQUIRED_RECOMMENDATIONS; itemIndex++) {
for (int itemIndex = 0; SmartspaceAction recommendation = recommendations.get(itemIndex);
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;
}
// Set up media item cover. // Set up media item cover.
ImageView mediaCoverImageView = mediaCoverItems.get(uiComponentIndex); ImageView mediaCoverImageView = mediaCoverItems.get(itemIndex);
mediaCoverImageView.setImageIcon(recommendation.getIcon()); mediaCoverImageView.setImageIcon(recommendation.getIcon());
// Set up the media item's click listener if applicable. // Set up the media item's click listener if applicable.
ViewGroup mediaCoverContainer = mediaCoverContainers.get(uiComponentIndex); ViewGroup mediaCoverContainer = mediaCoverContainers.get(itemIndex);
setSmartspaceRecItemOnClickListener(mediaCoverContainer, recommendation, setSmartspaceRecItemOnClickListener(mediaCoverContainer, recommendation, itemIndex);
uiComponentIndex);
// Bubble up the long-click event to the card. // Bubble up the long-click event to the card.
mediaCoverContainer.setOnLongClickListener(v -> { mediaCoverContainer.setOnLongClickListener(v -> {
View parent = (View) v.getParent(); View parent = (View) v.getParent();
@@ -1053,8 +1044,7 @@ public class MediaControlPanel {
// Set up title // Set up title
CharSequence title = recommendation.getTitle(); CharSequence title = recommendation.getTitle();
hasTitle |= !TextUtils.isEmpty(title); hasTitle |= !TextUtils.isEmpty(title);
TextView titleView = TextView titleView = mRecommendationViewHolder.getMediaTitles().get(itemIndex);
mRecommendationViewHolder.getMediaTitles().get(uiComponentIndex);
titleView.setText(title); titleView.setText(title);
// Set up subtitle // Set up subtitle
@@ -1062,13 +1052,10 @@ public class MediaControlPanel {
boolean shouldShowSubtitleText = !TextUtils.isEmpty(title); boolean shouldShowSubtitleText = !TextUtils.isEmpty(title);
CharSequence subtitle = shouldShowSubtitleText ? recommendation.getSubtitle() : ""; CharSequence subtitle = shouldShowSubtitleText ? recommendation.getSubtitle() : "";
hasSubtitle |= !TextUtils.isEmpty(subtitle); hasSubtitle |= !TextUtils.isEmpty(subtitle);
TextView subtitleView = TextView subtitleView = mRecommendationViewHolder.getMediaSubtitles().get(itemIndex);
mRecommendationViewHolder.getMediaSubtitles().get(uiComponentIndex);
subtitleView.setText(subtitle); 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. // If there's no subtitles and/or titles for any of the albums, hide those views.
ConstraintSet expandedSet = mMediaViewController.getExpandedLayout(); ConstraintSet expandedSet = mMediaViewController.getExpandedLayout();
@@ -1301,7 +1288,7 @@ public class MediaControlPanel {
} }
logSmartspaceCardReported(SMARTSPACE_CARD_CLICK_EVENT, logSmartspaceCardReported(SMARTSPACE_CARD_CLICK_EVENT,
interactedSubcardRank, interactedSubcardRank,
getSmartspaceSubCardCardinality()); mSmartspaceMediaItemsCount);
if (shouldSmartspaceRecItemOpenInForeground(action)) { if (shouldSmartspaceRecItemOpenInForeground(action)) {
// Request to unlock the device if the activity needs to be opened in foreground. // Request to unlock the device if the activity needs to be opened in foreground.
@@ -1386,13 +1373,4 @@ public class MediaControlPanel {
interactedSubcardRank, interactedSubcardRank,
interactedSubcardCardinality); interactedSubcardCardinality);
} }
}
private int getSmartspaceSubCardCardinality() {
if (!mMediaCarouselController.getMediaCarouselScrollHandler().getQsExpanded()
&& mSmartspaceMediaItemsCount > 3) {
return 3;
}
return mSmartspaceMediaItemsCount;
}
}

View File

@@ -166,7 +166,7 @@ class MediaDataFilter @Inject constructor(
shouldPrioritizeMutable = true shouldPrioritizeMutable = true
} }
if (!data.isValid) { if (!data.isValid()) {
Log.d(TAG, "Invalid recommendation data. Skip showing the rec card") Log.d(TAG, "Invalid recommendation data. Skip showing the rec card")
return return
} }
@@ -203,7 +203,6 @@ class MediaDataFilter @Inject constructor(
if (smartspaceMediaData.isActive) { if (smartspaceMediaData.isActive) {
smartspaceMediaData = EMPTY_SMARTSPACE_MEDIA_DATA.copy( smartspaceMediaData = EMPTY_SMARTSPACE_MEDIA_DATA.copy(
targetId = smartspaceMediaData.targetId, targetId = smartspaceMediaData.targetId,
isValid = smartspaceMediaData.isValid,
instanceId = smartspaceMediaData.instanceId) instanceId = smartspaceMediaData.instanceId)
} }
listeners.forEach { it.onSmartspaceMediaDataRemoved(key, immediately) } listeners.forEach { it.onSmartspaceMediaDataRemoved(key, immediately) }
@@ -260,7 +259,6 @@ class MediaDataFilter @Inject constructor(
} }
smartspaceMediaData = EMPTY_SMARTSPACE_MEDIA_DATA.copy( smartspaceMediaData = EMPTY_SMARTSPACE_MEDIA_DATA.copy(
targetId = smartspaceMediaData.targetId, targetId = smartspaceMediaData.targetId,
isValid = smartspaceMediaData.isValid,
instanceId = smartspaceMediaData.instanceId) instanceId = smartspaceMediaData.instanceId)
mediaDataManager.dismissSmartspaceRecommendation(smartspaceMediaData.targetId, mediaDataManager.dismissSmartspaceRecommendation(smartspaceMediaData.targetId,
delay = 0L) delay = 0L)
@@ -272,13 +270,13 @@ class MediaDataFilter @Inject constructor(
*/ */
fun hasActiveMediaOrRecommendation() = fun hasActiveMediaOrRecommendation() =
userEntries.any { it.value.active } || userEntries.any { it.value.active } ||
(smartspaceMediaData.isActive && smartspaceMediaData.isValid) (smartspaceMediaData.isActive && smartspaceMediaData.isValid())
/** /**
* Are there any media entries we should display? * Are there any media entries we should display?
*/ */
fun hasAnyMediaOrRecommendation() = userEntries.isNotEmpty() || fun hasAnyMediaOrRecommendation() = userEntries.isNotEmpty() ||
(smartspaceMediaData.isActive && smartspaceMediaData.isValid) (smartspaceMediaData.isActive && smartspaceMediaData.isValid())
/** /**
* Are there any media notifications active (excluding the recommendation)? * Are there any media notifications active (excluding the recommendation)?

View File

@@ -105,7 +105,6 @@ private val LOADING = MediaData(
internal val EMPTY_SMARTSPACE_MEDIA_DATA = SmartspaceMediaData( internal val EMPTY_SMARTSPACE_MEDIA_DATA = SmartspaceMediaData(
targetId = "INVALID", targetId = "INVALID",
isActive = false, isActive = false,
isValid = false,
packageName = "INVALID", packageName = "INVALID",
cardAction = null, cardAction = null,
recommendations = emptyList(), recommendations = emptyList(),
@@ -551,7 +550,7 @@ class MediaDataManager(
* connection session. * connection session.
*/ */
fun dismissSmartspaceRecommendation(key: String, delay: Long) { 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 // If this doesn't match, or we've already invalidated the data, no action needed
return return
} }
@@ -1240,7 +1239,6 @@ class MediaDataManager(
return SmartspaceMediaData( return SmartspaceMediaData(
targetId = target.smartspaceTargetId, targetId = target.smartspaceTargetId,
isActive = isActive, isActive = isActive,
isValid = true,
packageName = it, packageName = it,
cardAction = target.baseAction, cardAction = target.baseAction,
recommendations = target.iconGrid, recommendations = target.iconGrid,

View File

@@ -30,10 +30,6 @@ data class SmartspaceMediaData(
* Indicates if the status is active. * Indicates if the status is active.
*/ */
val isActive: Boolean, val isActive: Boolean,
/**
* Indicates if all the required data field is valid.
*/
val isValid: Boolean,
/** /**
* Package name of the media recommendations' provider-app. * Package name of the media recommendations' provider-app.
*/ */
@@ -58,4 +54,19 @@ data class SmartspaceMediaData(
* Instance ID for [MediaUiEventLogger] * Instance ID for [MediaUiEventLogger]
*/ */
val instanceId: InstanceId 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

View File

@@ -124,7 +124,6 @@ public class MediaControlPanelTest : SysuiTestCase() {
@Mock private lateinit var collapsedSet: ConstraintSet @Mock private lateinit var collapsedSet: ConstraintSet
@Mock private lateinit var mediaOutputDialogFactory: MediaOutputDialogFactory @Mock private lateinit var mediaOutputDialogFactory: MediaOutputDialogFactory
@Mock private lateinit var mediaCarouselController: MediaCarouselController @Mock private lateinit var mediaCarouselController: MediaCarouselController
@Mock private lateinit var mediaCarouselScrollHandler: MediaCarouselScrollHandler
@Mock private lateinit var falsingManager: FalsingManager @Mock private lateinit var falsingManager: FalsingManager
@Mock private lateinit var transitionParent: ViewGroup @Mock private lateinit var transitionParent: ViewGroup
private lateinit var appIcon: ImageView private lateinit var appIcon: ImageView
@@ -270,7 +269,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
smartspaceData = EMPTY_SMARTSPACE_MEDIA_DATA.copy( smartspaceData = EMPTY_SMARTSPACE_MEDIA_DATA.copy(
packageName = PACKAGE, packageName = PACKAGE,
instanceId = instanceId, instanceId = instanceId,
recommendations = listOf(smartspaceAction), recommendations = listOf(smartspaceAction, smartspaceAction, smartspaceAction),
cardAction = smartspaceAction cardAction = smartspaceAction
) )
} }
@@ -294,9 +293,6 @@ public class MediaControlPanelTest : SysuiTestCase() {
*/ */
private fun initMediaViewHolderMocks() { private fun initMediaViewHolderMocks() {
whenever(seekBarViewModel.progress).thenReturn(seekBarData) whenever(seekBarViewModel.progress).thenReturn(seekBarData)
whenever(mediaCarouselController.mediaCarouselScrollHandler)
.thenReturn(mediaCarouselScrollHandler)
whenever(mediaCarouselScrollHandler.qsExpanded).thenReturn(false)
// Set up mock views for the players // Set up mock views for the players
appIcon = ImageView(context) appIcon = ImageView(context)
@@ -1447,6 +1443,66 @@ public class MediaControlPanelTest : SysuiTestCase() {
verify(logger).logRecommendationItemTap(eq(PACKAGE), eq(instanceId), eq(0)) 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 @Test
fun bindRecommendation_hasTitlesAndSubtitles() { fun bindRecommendation_hasTitlesAndSubtitles() {
player.attachRecommendation(recommendationViewHolder) player.attachRecommendation(recommendationViewHolder)

View File

@@ -110,7 +110,7 @@ class MediaDataFilterTest : SysuiTestCase() {
`when`(smartspaceData.targetId).thenReturn(SMARTSPACE_KEY) `when`(smartspaceData.targetId).thenReturn(SMARTSPACE_KEY)
`when`(smartspaceData.isActive).thenReturn(true) `when`(smartspaceData.isActive).thenReturn(true)
`when`(smartspaceData.isValid).thenReturn(true) `when`(smartspaceData.isValid()).thenReturn(true)
`when`(smartspaceData.packageName).thenReturn(SMARTSPACE_PACKAGE) `when`(smartspaceData.packageName).thenReturn(SMARTSPACE_PACKAGE)
`when`(smartspaceData.recommendations).thenReturn(listOf(smartspaceMediaRecommendationItem)) `when`(smartspaceData.recommendations).thenReturn(listOf(smartspaceMediaRecommendationItem))
`when`(smartspaceData.headphoneConnectionTimeMillis).thenReturn( `when`(smartspaceData.headphoneConnectionTimeMillis).thenReturn(
@@ -196,24 +196,110 @@ class MediaDataFilterTest : SysuiTestCase() {
} }
@Test @Test
fun testHasAnyMediaOrRecommendation() { fun hasAnyMedia_noMediaSet_returnsFalse() {
assertThat(mediaDataFilter.hasAnyMediaOrRecommendation()).isFalse() assertThat(mediaDataFilter.hasAnyMedia()).isFalse()
}
@Test
fun hasAnyMedia_mediaSet_returnsTrue() {
mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = dataMain) mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = dataMain)
assertThat(mediaDataFilter.hasAnyMediaOrRecommendation()).isTrue()
assertThat(mediaDataFilter.hasAnyMedia()).isTrue() assertThat(mediaDataFilter.hasAnyMedia()).isTrue()
} }
@Test @Test
fun testHasActiveMediaOrRecommendation() { fun hasAnyMedia_recommendationSet_returnsFalse() {
assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse() mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
val data = dataMain.copy(active = true)
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) 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() 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 @Test
fun testHasAnyMediaOrRecommendation_onlyCurrentUser() { fun testHasAnyMediaOrRecommendation_onlyCurrentUser() {
assertThat(mediaDataFilter.hasAnyMediaOrRecommendation()).isFalse() assertThat(mediaDataFilter.hasAnyMediaOrRecommendation()).isFalse()
@@ -332,7 +418,7 @@ class MediaDataFilterTest : SysuiTestCase() {
@Test @Test
fun testOnSmartspaceMediaDataLoaded_hasRecentMedia_activeInvalidRec_usesMedia() { 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 // WHEN we have media that was recently played, but not currently active
val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime()) val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())

View File

@@ -7,6 +7,7 @@ import android.app.smartspace.SmartspaceAction
import android.app.smartspace.SmartspaceTarget import android.app.smartspace.SmartspaceTarget
import android.content.Intent import android.content.Intent
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.drawable.Icon
import android.media.MediaDescription import android.media.MediaDescription
import android.media.MediaMetadata import android.media.MediaMetadata
import android.media.session.MediaController import android.media.session.MediaController
@@ -97,6 +98,7 @@ class MediaDataManagerTest : SysuiTestCase() {
lateinit var smartspaceMediaDataProvider: SmartspaceMediaDataProvider lateinit var smartspaceMediaDataProvider: SmartspaceMediaDataProvider
@Mock lateinit var mediaSmartspaceTarget: SmartspaceTarget @Mock lateinit var mediaSmartspaceTarget: SmartspaceTarget
@Mock private lateinit var mediaRecommendationItem: SmartspaceAction @Mock private lateinit var mediaRecommendationItem: SmartspaceAction
lateinit var validRecommendationList: List<SmartspaceAction>
@Mock private lateinit var mediaSmartspaceBaseAction: SmartspaceAction @Mock private lateinit var mediaSmartspaceBaseAction: SmartspaceAction
@Mock private lateinit var mediaFlags: MediaFlags @Mock private lateinit var mediaFlags: MediaFlags
@Mock private lateinit var logger: MediaUiEventLogger @Mock private lateinit var logger: MediaUiEventLogger
@@ -172,12 +174,17 @@ class MediaDataManagerTest : SysuiTestCase() {
putString("package_name", PACKAGE_NAME) putString("package_name", PACKAGE_NAME)
putParcelable("dismiss_intent", DISMISS_INTENT) putParcelable("dismiss_intent", DISMISS_INTENT)
} }
val icon = Icon.createWithResource(context, android.R.drawable.ic_media_play)
whenever(mediaSmartspaceBaseAction.extras).thenReturn(recommendationExtras) whenever(mediaSmartspaceBaseAction.extras).thenReturn(recommendationExtras)
whenever(mediaSmartspaceTarget.baseAction).thenReturn(mediaSmartspaceBaseAction) whenever(mediaSmartspaceTarget.baseAction).thenReturn(mediaSmartspaceBaseAction)
whenever(mediaRecommendationItem.extras).thenReturn(recommendationExtras) whenever(mediaRecommendationItem.extras).thenReturn(recommendationExtras)
whenever(mediaRecommendationItem.icon).thenReturn(icon)
validRecommendationList = listOf(
mediaRecommendationItem, mediaRecommendationItem, mediaRecommendationItem
)
whenever(mediaSmartspaceTarget.smartspaceTargetId).thenReturn(KEY_MEDIA_SMARTSPACE) whenever(mediaSmartspaceTarget.smartspaceTargetId).thenReturn(KEY_MEDIA_SMARTSPACE)
whenever(mediaSmartspaceTarget.featureType).thenReturn(SmartspaceTarget.FEATURE_MEDIA) whenever(mediaSmartspaceTarget.featureType).thenReturn(SmartspaceTarget.FEATURE_MEDIA)
whenever(mediaSmartspaceTarget.iconGrid).thenReturn(listOf(mediaRecommendationItem)) whenever(mediaSmartspaceTarget.iconGrid).thenReturn(validRecommendationList)
whenever(mediaSmartspaceTarget.creationTimeMillis).thenReturn(1234L) whenever(mediaSmartspaceTarget.creationTimeMillis).thenReturn(1234L)
whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(false) whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(false)
whenever(logger.getNewInstanceId()).thenReturn(instanceIdSequence.newInstanceId()) whenever(logger.getNewInstanceId()).thenReturn(instanceIdSequence.newInstanceId())
@@ -507,10 +514,9 @@ class MediaDataManagerTest : SysuiTestCase() {
eq(SmartspaceMediaData( eq(SmartspaceMediaData(
targetId = KEY_MEDIA_SMARTSPACE, targetId = KEY_MEDIA_SMARTSPACE,
isActive = true, isActive = true,
isValid = true,
packageName = PACKAGE_NAME, packageName = PACKAGE_NAME,
cardAction = mediaSmartspaceBaseAction, cardAction = mediaSmartspaceBaseAction,
recommendations = listOf(mediaRecommendationItem), recommendations = validRecommendationList,
dismissIntent = DISMISS_INTENT, dismissIntent = DISMISS_INTENT,
headphoneConnectionTimeMillis = 1234L, headphoneConnectionTimeMillis = 1234L,
instanceId = InstanceId.fakeInstanceId(instanceId))), instanceId = InstanceId.fakeInstanceId(instanceId))),
@@ -529,7 +535,6 @@ class MediaDataManagerTest : SysuiTestCase() {
eq(EMPTY_SMARTSPACE_MEDIA_DATA.copy( eq(EMPTY_SMARTSPACE_MEDIA_DATA.copy(
targetId = KEY_MEDIA_SMARTSPACE, targetId = KEY_MEDIA_SMARTSPACE,
isActive = true, isActive = true,
isValid = false,
dismissIntent = DISMISS_INTENT, dismissIntent = DISMISS_INTENT,
headphoneConnectionTimeMillis = 1234L, headphoneConnectionTimeMillis = 1234L,
instanceId = InstanceId.fakeInstanceId(instanceId))), instanceId = InstanceId.fakeInstanceId(instanceId))),
@@ -555,7 +560,6 @@ class MediaDataManagerTest : SysuiTestCase() {
eq(EMPTY_SMARTSPACE_MEDIA_DATA.copy( eq(EMPTY_SMARTSPACE_MEDIA_DATA.copy(
targetId = KEY_MEDIA_SMARTSPACE, targetId = KEY_MEDIA_SMARTSPACE,
isActive = true, isActive = true,
isValid = false,
dismissIntent = null, dismissIntent = null,
headphoneConnectionTimeMillis = 1234L, headphoneConnectionTimeMillis = 1234L,
instanceId = InstanceId.fakeInstanceId(instanceId))), instanceId = InstanceId.fakeInstanceId(instanceId))),

View File

@@ -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<SmartspaceAction>()
// 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<SmartspaceAction>()
// 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<SmartspaceAction>()
// 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)
)