Intensify recommendation gradient and scale the cover.
Improve the view of new recommendation card by using a new gradient and scale the cover to 125%. Also, fix the change in gradient color in media players and recommendation items by using drawable mutations in order not to change the original state that is shared among drawables of the same resource. Bug: 271163892 Fixes: 268525377 Test: atest MediaControlPanelTest Test: checked the new view of recommendation card. Change-Id: Ie3619ea4ebbfcea082eb87d3e461347b7bcc4f8d
This commit is contained in:
25
packages/SystemUI/res/drawable/qs_media_rec_scrim.xml
Normal file
25
packages/SystemUI/res/drawable/qs_media_rec_scrim.xml
Normal file
@@ -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>
|
||||
@@ -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 -->
|
||||
|
||||
@@ -31,12 +31,15 @@ import android.content.Intent;
|
||||
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 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;
|
||||
@@ -145,6 +147,12 @@ public class MediaControlPanel {
|
||||
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 @@ public class MediaControlPanel {
|
||||
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 @@ public class MediaControlPanel {
|
||||
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 @@ public class MediaControlPanel {
|
||||
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 @@ public class MediaControlPanel {
|
||||
// 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 @@ public class MediaControlPanel {
|
||||
// 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 @@ public class MediaControlPanel {
|
||||
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 });
|
||||
}
|
||||
@@ -1585,6 +1619,29 @@ public class MediaControlPanel {
|
||||
return drawable;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
|
||||
@@ -27,6 +27,7 @@ import android.content.pm.PackageManager
|
||||
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.EMPTY_SMARTSPACE_MEDIA_DATA
|
||||
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 @@ public class MediaControlPanelTest : SysuiTestCase() {
|
||||
@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
|
||||
@@ -699,6 +703,33 @@ public class MediaControlPanelTest : SysuiTestCase() {
|
||||
verify(albumView, times(3)).setImageDrawable(any(Drawable::class.java))
|
||||
}
|
||||
|
||||
@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 @@ public class MediaControlPanelTest : SysuiTestCase() {
|
||||
.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 @@ public class MediaControlPanelTest : SysuiTestCase() {
|
||||
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
|
||||
@@ -2188,6 +2221,34 @@ public class MediaControlPanelTest : SysuiTestCase() {
|
||||
verify(recSubtitleMock3).visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
@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)
|
||||
|
||||
Reference in New Issue
Block a user