Merge "Intensify recommendation gradient and scale the cover." into tm-qpr-dev

This commit is contained in:
Michael Mikhail
2023-03-03 19:02:06 +00:00
committed by Android (Google) Code Review
4 changed files with 161 additions and 17 deletions

View 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>

View File

@@ -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 -->

View File

@@ -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
*

View File

@@ -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)