diff --git a/packages/SystemUI/res/layout/media_recommendation_view.xml b/packages/SystemUI/res/layout/media_recommendation_view.xml
new file mode 100644
index 0000000000000..101fad97beb52
--- /dev/null
+++ b/packages/SystemUI/res/layout/media_recommendation_view.xml
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/media_recommendations.xml b/packages/SystemUI/res/layout/media_recommendations.xml
new file mode 100644
index 0000000000000..65fc19c5b2a45
--- /dev/null
+++ b/packages/SystemUI/res/layout/media_recommendations.xml
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index dfc01501a9997..7d3a22c58ca59 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1068,8 +1068,13 @@
16dp
88dp
+ 110dp
+ 108dp
+ 77dp
16dp
8dp
+ 22dp
+ 12dp
16dp
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index e60835cc5ea42..a1d14ac973316 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2360,6 +2360,8 @@
Play %1$s by %2$s from %3$s
Play %1$s from %2$s
+
+ For You
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 9846fc251a271..58b0234023ae0 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -678,6 +678,17 @@
+
+
+
+
diff --git a/packages/SystemUI/res/xml/media_recommendations_view_collapsed.xml b/packages/SystemUI/res/xml/media_recommendations_view_collapsed.xml
new file mode 100644
index 0000000000000..d3be3c7de5ad9
--- /dev/null
+++ b/packages/SystemUI/res/xml/media_recommendations_view_collapsed.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/SystemUI/res/xml/media_recommendations_view_expanded.xml b/packages/SystemUI/res/xml/media_recommendations_view_expanded.xml
new file mode 100644
index 0000000000000..88c70552e9e88
--- /dev/null
+++ b/packages/SystemUI/res/xml/media_recommendations_view_expanded.xml
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 47c41fe1e3ba2..12d36bca9a951 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -365,6 +365,10 @@ object Flags {
// TODO(b/266157412): Tracking Bug
val MEDIA_RETAIN_SESSIONS = unreleasedFlag(913, "media_retain_sessions")
+ // TODO(b/266739309): Tracking Bug
+ @JvmField
+ val MEDIA_RECOMMENDATION_CARD_UPDATE = unreleasedFlag(914, "media_recommendation_card_update")
+
// 1000 - dock
val SIMULATE_DOCK_THROUGH_CHARGING = releasedFlag(1000, "simulate_dock_through_charging")
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/RecommendationViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/RecommendationViewHolder.kt
index 1a10b18a5a696..8c1ec166fb2eb 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/RecommendationViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/RecommendationViewHolder.kt
@@ -21,6 +21,7 @@ import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
+import com.android.internal.widget.CachingIconView
import com.android.systemui.R
import com.android.systemui.media.controls.models.GutsViewHolder
import com.android.systemui.media.controls.ui.IlluminationDrawable
@@ -29,18 +30,15 @@ import com.android.systemui.util.animation.TransitionLayout
private const val TAG = "RecommendationViewHolder"
/** ViewHolder for a Smartspace media recommendation. */
-class RecommendationViewHolder private constructor(itemView: View) {
+class RecommendationViewHolder private constructor(itemView: View, updatedView: Boolean) {
val recommendations = itemView as TransitionLayout
// Recommendation screen
- val cardIcon = itemView.requireViewById(R.id.recommendation_card_icon)
- val mediaCoverItems =
- listOf(
- itemView.requireViewById(R.id.media_cover1),
- itemView.requireViewById(R.id.media_cover2),
- itemView.requireViewById(R.id.media_cover3)
- )
+ lateinit var cardIcon: ImageView
+ lateinit var mediaAppIcons: List
+ lateinit var cardTitle: TextView
+
val mediaCoverContainers =
listOf(
itemView.requireViewById(R.id.media_cover1_container),
@@ -48,21 +46,45 @@ class RecommendationViewHolder private constructor(itemView: View) {
itemView.requireViewById(R.id.media_cover3_container)
)
val mediaTitles: List =
- listOf(
- itemView.requireViewById(R.id.media_title1),
- itemView.requireViewById(R.id.media_title2),
- itemView.requireViewById(R.id.media_title3)
- )
+ if (updatedView) {
+ mediaCoverContainers.map { it.requireViewById(R.id.media_title) }
+ } else {
+ listOf(
+ itemView.requireViewById(R.id.media_title1),
+ itemView.requireViewById(R.id.media_title2),
+ itemView.requireViewById(R.id.media_title3)
+ )
+ }
val mediaSubtitles: List =
- listOf(
- itemView.requireViewById(R.id.media_subtitle1),
- itemView.requireViewById(R.id.media_subtitle2),
- itemView.requireViewById(R.id.media_subtitle3)
- )
+ if (updatedView) {
+ mediaCoverContainers.map { it.requireViewById(R.id.media_subtitle) }
+ } else {
+ listOf(
+ itemView.requireViewById(R.id.media_subtitle1),
+ itemView.requireViewById(R.id.media_subtitle2),
+ itemView.requireViewById(R.id.media_subtitle3)
+ )
+ }
+ val mediaCoverItems: List =
+ if (updatedView) {
+ mediaCoverContainers.map { it.requireViewById(R.id.media_cover) }
+ } else {
+ listOf(
+ itemView.requireViewById(R.id.media_cover1),
+ itemView.requireViewById(R.id.media_cover2),
+ itemView.requireViewById(R.id.media_cover3)
+ )
+ }
val gutsViewHolder = GutsViewHolder(itemView)
init {
+ if (updatedView) {
+ mediaAppIcons = mediaCoverContainers.map { it.requireViewById(R.id.media_rec_app_icon) }
+ cardTitle = itemView.requireViewById(R.id.media_rec_title)
+ } else {
+ cardIcon = itemView.requireViewById(R.id.recommendation_card_icon)
+ }
(recommendations.background as IlluminationDrawable).let { background ->
mediaCoverContainers.forEach { background.registerLightSource(it) }
background.registerLightSource(gutsViewHolder.cancel)
@@ -83,36 +105,52 @@ class RecommendationViewHolder private constructor(itemView: View) {
* @param parent Parent of inflated view.
*/
@JvmStatic
- fun create(inflater: LayoutInflater, parent: ViewGroup): RecommendationViewHolder {
+ fun create(
+ inflater: LayoutInflater,
+ parent: ViewGroup,
+ updatedView: Boolean,
+ ): RecommendationViewHolder {
val itemView =
- inflater.inflate(
- R.layout.media_smartspace_recommendations,
- parent,
- false /* attachToRoot */
- )
+ if (updatedView) {
+ inflater.inflate(
+ R.layout.media_recommendations,
+ parent,
+ false /* attachToRoot */
+ )
+ } else {
+ inflater.inflate(
+ R.layout.media_smartspace_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)
+ return RecommendationViewHolder(itemView, updatedView)
}
// Res Ids for the control components on the recommendation view.
val controlsIds =
setOf(
R.id.recommendation_card_icon,
+ R.id.media_rec_title,
R.id.media_cover1,
R.id.media_cover2,
R.id.media_cover3,
+ R.id.media_cover,
R.id.media_cover1_container,
R.id.media_cover2_container,
R.id.media_cover3_container,
R.id.media_title1,
R.id.media_title2,
R.id.media_title3,
+ R.id.media_title,
R.id.media_subtitle1,
R.id.media_subtitle2,
- R.id.media_subtitle3
+ R.id.media_subtitle3,
+ R.id.media_subtitle,
)
val mediaTitlesAndSubtitlesIds =
@@ -120,9 +158,11 @@ class RecommendationViewHolder private constructor(itemView: View) {
R.id.media_title1,
R.id.media_title2,
R.id.media_title3,
+ R.id.media_title,
R.id.media_subtitle1,
R.id.media_subtitle2,
- R.id.media_subtitle3
+ R.id.media_subtitle3,
+ R.id.media_subtitle,
)
val mediaContainersIds =
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
index e7f7647797cd6..b2ad155227433 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
@@ -43,6 +43,7 @@ import com.android.systemui.media.controls.models.recommendation.RecommendationV
import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
import com.android.systemui.media.controls.pipeline.MediaDataManager
import com.android.systemui.media.controls.ui.MediaControlPanel.SMARTSPACE_CARD_DISMISS_EVENT
+import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.media.controls.util.SmallHash
import com.android.systemui.plugins.ActivityStarter
@@ -88,7 +89,8 @@ constructor(
falsingManager: FalsingManager,
dumpManager: DumpManager,
private val logger: MediaUiEventLogger,
- private val debugLogger: MediaCarouselControllerLogger
+ private val debugLogger: MediaCarouselControllerLogger,
+ private val mediaFlags: MediaFlags,
) : Dumpable {
/** The current width of the carousel */
private var currentCarouselWidth: Int = 0
@@ -647,7 +649,11 @@ constructor(
val newRecs = mediaControlPanelFactory.get()
newRecs.attachRecommendation(
- RecommendationViewHolder.create(LayoutInflater.from(context), mediaContent)
+ RecommendationViewHolder.create(
+ LayoutInflater.from(context),
+ mediaContent,
+ mediaFlags.isRecommendationCardUpdateEnabled()
+ )
)
newRecs.mediaViewController.sizeChangedListener = this::updateCarouselDimensions
val lp =
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
index 45d50f0e4976d..9250a580dfa05 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
@@ -61,6 +61,7 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
+import androidx.appcompat.content.res.AppCompatResources;
import androidx.constraintlayout.widget.ConstraintSet;
import com.android.internal.annotations.VisibleForTesting;
@@ -752,43 +753,16 @@ public class MediaControlPanel {
int width = mMediaViewHolder.getAlbumView().getMeasuredWidth();
int height = mMediaViewHolder.getAlbumView().getMeasuredHeight();
- // WallpaperColors.fromBitmap takes a good amount of time. We do that work
- // on the background executor to avoid stalling animations on the UI Thread.
mBackgroundExecutor.execute(() -> {
// Album art
ColorScheme mutableColorScheme = null;
Drawable artwork;
boolean isArtworkBound;
Icon artworkIcon = data.getArtwork();
- WallpaperColors wallpaperColors = null;
- if (artworkIcon != null) {
- if (artworkIcon.getType() == Icon.TYPE_BITMAP
- || artworkIcon.getType() == Icon.TYPE_ADAPTIVE_BITMAP) {
- // Avoids extra processing if this is already a valid bitmap
- wallpaperColors = WallpaperColors
- .fromBitmap(artworkIcon.getBitmap());
- } else {
- Drawable artworkDrawable = artworkIcon.loadDrawable(mContext);
- if (artworkDrawable != null) {
- wallpaperColors = WallpaperColors
- .fromDrawable(artworkIcon.loadDrawable(mContext));
- }
- }
- }
+ WallpaperColors wallpaperColors = getWallpaperColor(artworkIcon);
if (wallpaperColors != null) {
mutableColorScheme = new ColorScheme(wallpaperColors, true, Style.CONTENT);
- Drawable albumArt = getScaledBackground(artworkIcon, width, height);
- GradientDrawable gradient = (GradientDrawable) mContext
- .getDrawable(R.drawable.qs_media_scrim);
- gradient.setColors(new int[] {
- ColorUtilKt.getColorWithAlpha(
- MediaColorSchemesKt.backgroundStartFromScheme(mutableColorScheme),
- 0.25f),
- ColorUtilKt.getColorWithAlpha(
- MediaColorSchemesKt.backgroundEndFromScheme(mutableColorScheme),
- 0.9f),
- });
- artwork = new LayerDrawable(new Drawable[] { albumArt, gradient });
+ artwork = addGradientToIcon(artworkIcon, mutableColorScheme, width, height);
isArtworkBound = true;
} else {
// If there's no artwork, use colors from the app icon
@@ -867,6 +841,96 @@ 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 = mRecommendationViewHolder.getMediaCoverContainers().get(0).getMeasuredWidth();
+ int height = mRecommendationViewHolder.getMediaCoverContainers().get(0).getMeasuredHeight();
+
+ 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 = addGradientToIcon(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);
+ 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.
+ private WallpaperColors getWallpaperColor(Icon artworkIcon) {
+ if (artworkIcon != null) {
+ if (artworkIcon.getType() == Icon.TYPE_BITMAP
+ || artworkIcon.getType() == Icon.TYPE_ADAPTIVE_BITMAP) {
+ // Avoids extra processing if this is already a valid bitmap
+ return WallpaperColors
+ .fromBitmap(artworkIcon.getBitmap());
+ } else {
+ Drawable artworkDrawable = artworkIcon.loadDrawable(mContext);
+ if (artworkDrawable != null) {
+ return WallpaperColors
+ .fromDrawable(artworkIcon.loadDrawable(mContext));
+ }
+ }
+ }
+ return null;
+ }
+
+ private LayerDrawable addGradientToIcon(
+ Icon artworkIcon,
+ ColorScheme mutableColorScheme,
+ int width,
+ int height
+ ) {
+ Drawable albumArt = getScaledBackground(artworkIcon, width, height);
+ GradientDrawable gradient = (GradientDrawable) AppCompatResources
+ .getDrawable(mContext, R.drawable.qs_media_scrim);
+ gradient.setColors(new int[] {
+ ColorUtilKt.getColorWithAlpha(
+ MediaColorSchemesKt.backgroundStartFromScheme(mutableColorScheme),
+ 0.25f),
+ ColorUtilKt.getColorWithAlpha(
+ MediaColorSchemesKt.backgroundEndFromScheme(mutableColorScheme),
+ 0.9f),
+ });
+ return new LayerDrawable(new Drawable[] { albumArt, gradient });
+ }
+
private void scaleTransitionDrawableLayer(TransitionDrawable transitionDrawable, int layer,
int targetWidth, int targetHeight) {
Drawable drawable = transitionDrawable.getDrawable(layer);
@@ -1224,8 +1288,10 @@ public class MediaControlPanel {
PackageManager packageManager = mContext.getPackageManager();
// Set up media source app's logo.
Drawable icon = packageManager.getApplicationIcon(applicationInfo);
- ImageView headerLogoImageView = mRecommendationViewHolder.getCardIcon();
- headerLogoImageView.setImageDrawable(icon);
+ if (!mFeatureFlags.isEnabled(Flags.MEDIA_RECOMMENDATION_CARD_UPDATE)) {
+ ImageView headerLogoImageView = mRecommendationViewHolder.getCardIcon();
+ headerLogoImageView.setImageDrawable(icon);
+ }
fetchAndUpdateRecommendationColors(icon);
// Set up media rec card's tap action if applicable.
@@ -1245,7 +1311,15 @@ public class MediaControlPanel {
// Set up media item cover.
ImageView mediaCoverImageView = mediaCoverItems.get(itemIndex);
- mediaCoverImageView.setImageIcon(recommendation.getIcon());
+ if (mFeatureFlags.isEnabled(Flags.MEDIA_RECOMMENDATION_CARD_UPDATE)) {
+ bindRecommendationArtwork(
+ recommendation,
+ data.getPackageName(),
+ itemIndex
+ );
+ } else {
+ mediaCoverImageView.setImageIcon(recommendation.getIcon());
+ }
// Set up the media item's click listener if applicable.
ViewGroup mediaCoverContainer = mediaCoverContainers.get(itemIndex);
@@ -1275,7 +1349,6 @@ public class MediaControlPanel {
recommendation.getTitle(), artistName, appName));
}
-
// Set up title
CharSequence title = recommendation.getTitle();
hasTitle |= !TextUtils.isEmpty(title);
@@ -1353,6 +1426,10 @@ public class MediaControlPanel {
int textPrimaryColor = MediaColorSchemesKt.textPrimaryFromScheme(colorScheme);
int textSecondaryColor = MediaColorSchemesKt.textSecondaryFromScheme(colorScheme);
+ if (mFeatureFlags.isEnabled(Flags.MEDIA_RECOMMENDATION_CARD_UPDATE)) {
+ mRecommendationViewHolder.getCardTitle().setTextColor(textPrimaryColor);
+ }
+
mRecommendationViewHolder.getRecommendations()
.setBackgroundTintList(ColorStateList.valueOf(backgroundColor));
mRecommendationViewHolder.getMediaTitles().forEach(
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
index 2ec7be6eaa328..1e6002cdc7170 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
@@ -25,6 +25,7 @@ import com.android.systemui.media.controls.models.GutsViewHolder
import com.android.systemui.media.controls.models.player.MediaViewHolder
import com.android.systemui.media.controls.models.recommendation.RecommendationViewHolder
import com.android.systemui.media.controls.ui.MediaCarouselController.Companion.calculateAlpha
+import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.animation.MeasurementOutput
import com.android.systemui.util.animation.TransitionLayout
@@ -45,7 +46,8 @@ constructor(
private val context: Context,
private val configurationController: ConfigurationController,
private val mediaHostStatesManager: MediaHostStatesManager,
- private val logger: MediaViewLogger
+ private val logger: MediaViewLogger,
+ private val mediaFlags: MediaFlags,
) {
/**
@@ -646,8 +648,13 @@ constructor(
expandedLayout.load(context, R.xml.media_session_expanded)
}
TYPE.RECOMMENDATION -> {
- collapsedLayout.load(context, R.xml.media_recommendation_collapsed)
- expandedLayout.load(context, R.xml.media_recommendation_expanded)
+ if (mediaFlags.isRecommendationCardUpdateEnabled()) {
+ collapsedLayout.load(context, R.xml.media_recommendations_view_collapsed)
+ expandedLayout.load(context, R.xml.media_recommendations_view_expanded)
+ } else {
+ collapsedLayout.load(context, R.xml.media_recommendation_collapsed)
+ expandedLayout.load(context, R.xml.media_recommendation_expanded)
+ }
}
}
refreshState()
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
index ab03930e42ac2..81efa3688aab8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
@@ -51,4 +51,8 @@ class MediaFlags @Inject constructor(private val featureFlags: FeatureFlags) {
* whether the underlying notification was dismissed
*/
fun isRetainingPlayersEnabled() = featureFlags.isEnabled(Flags.MEDIA_RETAIN_SESSIONS)
+
+ /** Check whether we show the updated recommendation card. */
+ fun isRecommendationCardUpdateEnabled() =
+ featureFlags.isEnabled(Flags.MEDIA_RECOMMENDATION_CARD_UPDATE)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
index e4e95e580a7c9..5e5dc8b20c652 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
@@ -33,6 +33,7 @@ import com.android.systemui.media.controls.models.recommendation.SmartspaceMedia
import com.android.systemui.media.controls.pipeline.EMPTY_SMARTSPACE_MEDIA_DATA
import com.android.systemui.media.controls.pipeline.MediaDataManager
import com.android.systemui.media.controls.ui.MediaHierarchyManager.Companion.LOCATION_QS
+import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
@@ -87,6 +88,7 @@ class MediaCarouselControllerTest : SysuiTestCase() {
@Mock lateinit var smartspaceMediaData: SmartspaceMediaData
@Mock lateinit var mediaCarousel: MediaScrollView
@Mock lateinit var pageIndicator: PageIndicator
+ @Mock lateinit var mediaFlags: MediaFlags
@Captor lateinit var listener: ArgumentCaptor
@Captor
lateinit var configListener: ArgumentCaptor
@@ -114,7 +116,8 @@ class MediaCarouselControllerTest : SysuiTestCase() {
falsingManager,
dumpManager,
logger,
- debugLogger
+ debugLogger,
+ mediaFlags,
)
verify(configurationController).addCallback(capture(configListener))
verify(mediaDataManager).addListener(capture(listener))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
index b35dd266e4220..ce22b19b3721a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
@@ -204,6 +204,9 @@ public class MediaControlPanelTest : SysuiTestCase() {
@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
private lateinit var coverItem1: ImageView
private lateinit var coverItem2: ImageView
private lateinit var coverItem3: ImageView
@@ -220,6 +223,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
this.set(Flags.UMO_TURBULENCE_NOISE, false)
this.set(Flags.MEDIA_FALSING_PENALTY, true)
this.set(Flags.MEDIA_EXPLICIT_INDICATOR, true)
+ this.set(Flags.MEDIA_RECOMMENDATION_CARD_UPDATE, false)
}
@JvmField @Rule val mockito = MockitoJUnit.rule()
@@ -2058,6 +2062,51 @@ public class MediaControlPanelTest : SysuiTestCase() {
assertThat(expandedSet.getVisibility(recSubtitle3.id)).isEqualTo(ConstraintSet.GONE)
}
+ @Test
+ fun bindRecommendation_setAfterExecutors() {
+ fakeFeatureFlag.set(Flags.MEDIA_RECOMMENDATION_CARD_UPDATE, true)
+ whenever(recommendationViewHolder.mediaAppIcons)
+ .thenReturn(listOf(recAppIconItem, recAppIconItem, recAppIconItem))
+ whenever(recommendationViewHolder.cardTitle).thenReturn(recCardTitle)
+ whenever(recommendationViewHolder.mediaCoverItems)
+ .thenReturn(listOf(coverItem, coverItem, coverItem))
+
+ val bmp = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888)
+ val canvas = Canvas(bmp)
+ canvas.drawColor(Color.RED)
+ val albumArt = Icon.createWithBitmap(bmp)
+ 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())
+ verify(recAppIconItem, times(3)).setImageDrawable(any(Drawable::class.java))
+ verify(coverItem, times(3)).setImageDrawable(any(Drawable::class.java))
+ }
+
@Test
fun onButtonClick_touchRippleFlagEnabled_playsTouchRipple() {
fakeFeatureFlag.set(Flags.UMO_SURFACE_RIPPLE, true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaViewControllerTest.kt
index 4ed6d7cf6bd0f..2f7eac2ad4aea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaViewControllerTest.kt
@@ -22,6 +22,7 @@ import android.view.View
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.util.animation.MeasurementInput
import com.android.systemui.util.animation.TransitionLayout
import com.android.systemui.util.animation.TransitionViewState
@@ -55,6 +56,7 @@ class MediaViewControllerTest : SysuiTestCase() {
@Mock private lateinit var mediaTitleWidgetState: WidgetState
@Mock private lateinit var mediaSubTitleWidgetState: WidgetState
@Mock private lateinit var mediaContainerWidgetState: WidgetState
+ @Mock private lateinit var mediaFlags: MediaFlags
val delta = 0.1F
@@ -64,7 +66,13 @@ class MediaViewControllerTest : SysuiTestCase() {
fun setup() {
MockitoAnnotations.initMocks(this)
mediaViewController =
- MediaViewController(context, configurationController, mediaHostStatesManager, logger)
+ MediaViewController(
+ context,
+ configurationController,
+ mediaHostStatesManager,
+ logger,
+ mediaFlags,
+ )
}
@Test