Merge "Intensify recommendation gradient and scale the cover." into tm-qpr-dev
diff --git a/packages/SystemUI/res/drawable/qs_media_rec_scrim.xml b/packages/SystemUI/res/drawable/qs_media_rec_scrim.xml
new file mode 100644
index 0000000..de0a620
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_media_rec_scrim.xml
@@ -0,0 +1,25 @@
+<?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
index c54c4e4..a4aeba1 100644
--- a/packages/SystemUI/res/layout/media_recommendation_view.xml
+++ b/packages/SystemUI/res/layout/media_recommendation_view.xml
@@ -22,9 +22,10 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:translationZ="0dp"
- android:scaleType="centerCrop"
+ android:scaleType="matrix"
android:adjustViewBounds="true"
android:clipToOutline="true"
+ android:layerType="hardware"
android:background="@drawable/bg_smartspace_media_item"/>
<!-- App icon -->
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 4ab93da..b1d6f97 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
@@ -31,12 +31,15 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
+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.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;
@@ -62,7 +65,6 @@
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;
@@ -145,6 +147,12 @@
private static final int SMARTSPACE_CARD_CLICK_EVENT = 760;
protected static final int SMARTSPACE_CARD_DISMISS_EVENT = 761;
+ private static final float REC_MEDIA_COVER_SCALE_FACTOR = 1.25f;
+ private static final float MEDIA_SCRIM_START_ALPHA = 0.25f;
+ private static final float MEDIA_REC_SCRIM_START_ALPHA = 0.15f;
+ private static final float MEDIA_PLAYER_SCRIM_END_ALPHA = 0.9f;
+ 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
@@ -776,7 +784,7 @@
WallpaperColors wallpaperColors = getWallpaperColor(artworkIcon);
if (wallpaperColors != null) {
mutableColorScheme = new ColorScheme(wallpaperColors, true, Style.CONTENT);
- artwork = addGradientToIcon(artworkIcon, mutableColorScheme, width, height);
+ artwork = addGradientToPlayerAlbum(artworkIcon, mutableColorScheme, width, height);
isArtworkBound = true;
} else {
// If there's no artwork, use colors from the app icon
@@ -866,8 +874,9 @@
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();
+ 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
@@ -877,7 +886,8 @@
WallpaperColors wallpaperColors = getWallpaperColor(artworkIcon);
if (wallpaperColors != null) {
mutableColorScheme = new ColorScheme(wallpaperColors, true, Style.CONTENT);
- artwork = addGradientToIcon(artworkIcon, mutableColorScheme, width, height);
+ artwork = addGradientToRecommendationAlbum(artworkIcon, mutableColorScheme, width,
+ height);
} else {
artwork = new ColorDrawable(Color.TRANSPARENT);
}
@@ -886,6 +896,11 @@
// 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.
@@ -907,7 +922,8 @@
// 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) {
+ @VisibleForTesting
+ protected WallpaperColors getWallpaperColor(Icon artworkIcon) {
if (artworkIcon != null) {
if (artworkIcon.getType() == Icon.TYPE_BITMAP
|| artworkIcon.getType() == Icon.TYPE_ADAPTIVE_BITMAP) {
@@ -925,22 +941,40 @@
return null;
}
- private LayerDrawable addGradientToIcon(
- Icon artworkIcon,
- ColorScheme mutableColorScheme,
- int width,
- int height
- ) {
+ @VisibleForTesting
+ protected LayerDrawable addGradientToPlayerAlbum(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);
+ GradientDrawable gradient = (GradientDrawable) mContext.getDrawable(
+ R.drawable.qs_media_scrim).mutate();
+ return setupGradientColorOnDrawable(albumArt, gradient, mutableColorScheme,
+ MEDIA_SCRIM_START_ALPHA, MEDIA_PLAYER_SCRIM_END_ALPHA);
+ }
+
+ @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) {
gradient.setColors(new int[] {
ColorUtilKt.getColorWithAlpha(
MediaColorSchemesKt.backgroundStartFromScheme(mutableColorScheme),
- 0.25f),
+ startAlpha),
ColorUtilKt.getColorWithAlpha(
MediaColorSchemesKt.backgroundEndFromScheme(mutableColorScheme),
- 0.9f),
+ endAlpha),
});
return new LayerDrawable(new Drawable[] { albumArt, gradient });
}
@@ -1586,6 +1620,29 @@
}
/**
+ * 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
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 55a33b6..9a5f080 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
@@ -27,6 +27,7 @@
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
@@ -78,6 +79,8 @@
import com.android.systemui.media.controls.pipeline.MediaDataManager
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.media.dialog.MediaOutputDialogFactory
+import com.android.systemui.monet.ColorScheme
+import com.android.systemui.monet.Style
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.statusbar.NotificationLockscreenUserManager
@@ -214,6 +217,7 @@
@Mock private lateinit var recSubtitleMock2: TextView
@Mock private lateinit var recSubtitleMock3: TextView
@Mock private lateinit var coverItem: ImageView
+ @Mock private lateinit var matrix: Matrix
private lateinit var coverItem1: ImageView
private lateinit var coverItem2: ImageView
private lateinit var coverItem3: ImageView
@@ -700,6 +704,33 @@
}
@Test
+ fun addTwoPlayerGradients_differentStates() {
+ // Setup redArtwork and its color scheme.
+ val redBmp = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888)
+ val redCanvas = Canvas(redBmp)
+ redCanvas.drawColor(Color.RED)
+ val redArt = Icon.createWithBitmap(redBmp)
+ val redWallpaperColor = player.getWallpaperColor(redArt)
+ val redColorScheme = ColorScheme(redWallpaperColor, true, Style.CONTENT)
+
+ // Setup greenArt and its color scheme.
+ val greenBmp = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888)
+ val greenCanvas = Canvas(greenBmp)
+ greenCanvas.drawColor(Color.GREEN)
+ val greenArt = Icon.createWithBitmap(greenBmp)
+ val greenWallpaperColor = player.getWallpaperColor(greenArt)
+ val greenColorScheme = ColorScheme(greenWallpaperColor, true, Style.CONTENT)
+
+ // Add gradient to both icons.
+ val redArtwork = player.addGradientToPlayerAlbum(redArt, redColorScheme, 10, 10)
+ val greenArtwork = player.addGradientToPlayerAlbum(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 bind_seekBarDisabled_hasActions_seekBarVisibilityIsSetToInvisible() {
useRealConstraintSets()
@@ -2092,6 +2123,7 @@
.thenReturn(listOf(recProgressBar1, recProgressBar2, recProgressBar3))
whenever(recommendationViewHolder.mediaSubtitles)
.thenReturn(listOf(recSubtitleMock1, recSubtitleMock2, recSubtitleMock3))
+ whenever(coverItem.imageMatrix).thenReturn(matrix)
val bmp = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bmp)
@@ -2127,6 +2159,7 @@
verify(recCardTitle).setTextColor(any<Int>())
verify(recAppIconItem, times(3)).setImageDrawable(any(Drawable::class.java))
verify(coverItem, times(3)).setImageDrawable(any(Drawable::class.java))
+ verify(coverItem, times(3)).imageMatrix = any()
}
@Test
@@ -2189,6 +2222,34 @@
}
@Test
+ fun addTwoRecommendationGradients_differentStates() {
+ // Setup redArtwork and its color scheme.
+ val redBmp = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888)
+ val redCanvas = Canvas(redBmp)
+ redCanvas.drawColor(Color.RED)
+ val redArt = Icon.createWithBitmap(redBmp)
+ val redWallpaperColor = player.getWallpaperColor(redArt)
+ val redColorScheme = ColorScheme(redWallpaperColor, true, Style.CONTENT)
+
+ // Setup greenArt and its color scheme.
+ val greenBmp = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888)
+ val greenCanvas = Canvas(greenBmp)
+ greenCanvas.drawColor(Color.GREEN)
+ val greenArt = Icon.createWithBitmap(greenBmp)
+ 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_touchRippleFlagEnabled_playsTouchRipple() {
fakeFeatureFlag.set(Flags.UMO_SURFACE_RIPPLE, true)
val semanticActions =