Merge changes from topic "colorSchemeChanges" into tm-dev am: e320ce441d
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/18164075 Change-Id: I3e7cc5987a4f420ffea98b8e387cbe5d1c5b4eb8 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -21,7 +21,6 @@ import android.app.WallpaperColors
|
|||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import com.android.internal.graphics.ColorUtils
|
import com.android.internal.graphics.ColorUtils
|
||||||
import com.android.internal.graphics.cam.Cam
|
import com.android.internal.graphics.cam.Cam
|
||||||
import com.android.internal.graphics.cam.CamUtils.lstarFromInt
|
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
@@ -50,7 +49,7 @@ internal interface Hue {
|
|||||||
val previousHue = hueAndRotations[previousIndex].first
|
val previousHue = hueAndRotations[previousIndex].first
|
||||||
if (ColorScheme.angleIsBetween(sourceHue, thisHue, previousHue)) {
|
if (ColorScheme.angleIsBetween(sourceHue, thisHue, previousHue)) {
|
||||||
return ColorScheme.wrapDegreesDouble(sourceHue.toDouble() +
|
return ColorScheme.wrapDegreesDouble(sourceHue.toDouble() +
|
||||||
hueAndRotations[previousIndex].first)
|
hueAndRotations[previousIndex].second)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,12 +142,24 @@ internal class ChromaMinimum(val chroma: Double) : Chroma {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal class ChromaMultiple(val multiple: Double) : Chroma {
|
||||||
|
override fun get(sourceColor: Cam): Double {
|
||||||
|
return sourceColor.chroma * multiple
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal class ChromaConstant(val chroma: Double) : Chroma {
|
internal class ChromaConstant(val chroma: Double) : Chroma {
|
||||||
override fun get(sourceColor: Cam): Double {
|
override fun get(sourceColor: Cam): Double {
|
||||||
return chroma
|
return chroma
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal class ChromaSource : Chroma {
|
||||||
|
override fun get(sourceColor: Cam): Double {
|
||||||
|
return sourceColor.chroma.toDouble()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal class TonalSpec(val hue: Hue = HueSource(), val chroma: Chroma) {
|
internal class TonalSpec(val hue: Hue = HueSource(), val chroma: Chroma) {
|
||||||
fun shades(sourceColor: Cam): List<Int> {
|
fun shades(sourceColor: Cam): List<Int> {
|
||||||
val hue = hue.get(sourceColor)
|
val hue = hue.get(sourceColor)
|
||||||
@@ -184,8 +195,8 @@ enum class Style(internal val coreSpec: CoreSpec) {
|
|||||||
a1 = TonalSpec(HueSource(), ChromaMinimum(48.0)),
|
a1 = TonalSpec(HueSource(), ChromaMinimum(48.0)),
|
||||||
a2 = TonalSpec(HueVibrantSecondary(), ChromaConstant(24.0)),
|
a2 = TonalSpec(HueVibrantSecondary(), ChromaConstant(24.0)),
|
||||||
a3 = TonalSpec(HueVibrantTertiary(), ChromaConstant(32.0)),
|
a3 = TonalSpec(HueVibrantTertiary(), ChromaConstant(32.0)),
|
||||||
n1 = TonalSpec(HueSource(), ChromaConstant(10.0)),
|
n1 = TonalSpec(HueSource(), ChromaConstant(12.0)),
|
||||||
n2 = TonalSpec(HueSource(), ChromaConstant(12.0))
|
n2 = TonalSpec(HueSource(), ChromaConstant(14.0))
|
||||||
)),
|
)),
|
||||||
EXPRESSIVE(CoreSpec(
|
EXPRESSIVE(CoreSpec(
|
||||||
a1 = TonalSpec(HueAdd(240.0), ChromaConstant(40.0)),
|
a1 = TonalSpec(HueAdd(240.0), ChromaConstant(40.0)),
|
||||||
@@ -208,6 +219,13 @@ enum class Style(internal val coreSpec: CoreSpec) {
|
|||||||
n1 = TonalSpec(HueSource(), ChromaConstant(10.0)),
|
n1 = TonalSpec(HueSource(), ChromaConstant(10.0)),
|
||||||
n2 = TonalSpec(HueSource(), ChromaConstant(16.0))
|
n2 = TonalSpec(HueSource(), ChromaConstant(16.0))
|
||||||
)),
|
)),
|
||||||
|
CONTENT(CoreSpec(
|
||||||
|
a1 = TonalSpec(HueSource(), ChromaSource()),
|
||||||
|
a2 = TonalSpec(HueSource(), ChromaMultiple(0.33)),
|
||||||
|
a3 = TonalSpec(HueSource(), ChromaMultiple(0.66)),
|
||||||
|
n1 = TonalSpec(HueSource(), ChromaMultiple(0.0833)),
|
||||||
|
n2 = TonalSpec(HueSource(), ChromaMultiple(0.1666))
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
|
|
||||||
class ColorScheme(
|
class ColorScheme(
|
||||||
@@ -231,7 +249,7 @@ class ColorScheme(
|
|||||||
darkTheme: Boolean,
|
darkTheme: Boolean,
|
||||||
style: Style = Style.TONAL_SPOT
|
style: Style = Style.TONAL_SPOT
|
||||||
):
|
):
|
||||||
this(getSeedColor(wallpaperColors), darkTheme, style)
|
this(getSeedColor(wallpaperColors, style != Style.CONTENT), darkTheme, style)
|
||||||
|
|
||||||
val allAccentColors: List<Int>
|
val allAccentColors: List<Int>
|
||||||
get() {
|
get() {
|
||||||
@@ -260,7 +278,7 @@ class ColorScheme(
|
|||||||
val proposedSeedCam = Cam.fromInt(seed)
|
val proposedSeedCam = Cam.fromInt(seed)
|
||||||
val seedArgb = if (seed == Color.TRANSPARENT) {
|
val seedArgb = if (seed == Color.TRANSPARENT) {
|
||||||
GOOGLE_BLUE
|
GOOGLE_BLUE
|
||||||
} else if (proposedSeedCam.chroma < 5) {
|
} else if (style != Style.CONTENT && proposedSeedCam.chroma < 5) {
|
||||||
GOOGLE_BLUE
|
GOOGLE_BLUE
|
||||||
} else {
|
} else {
|
||||||
seed
|
seed
|
||||||
@@ -289,22 +307,26 @@ class ColorScheme(
|
|||||||
* Identifies a color to create a color scheme from.
|
* Identifies a color to create a color scheme from.
|
||||||
*
|
*
|
||||||
* @param wallpaperColors Colors extracted from an image via quantization.
|
* @param wallpaperColors Colors extracted from an image via quantization.
|
||||||
|
* @param filter If false, allow colors that have low chroma, creating grayscale themes.
|
||||||
* @return ARGB int representing the color
|
* @return ARGB int representing the color
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
|
@JvmOverloads
|
||||||
@ColorInt
|
@ColorInt
|
||||||
fun getSeedColor(wallpaperColors: WallpaperColors): Int {
|
fun getSeedColor(wallpaperColors: WallpaperColors, filter: Boolean = true): Int {
|
||||||
return getSeedColors(wallpaperColors).first()
|
return getSeedColors(wallpaperColors, filter).first()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filters and ranks colors from WallpaperColors.
|
* Filters and ranks colors from WallpaperColors.
|
||||||
*
|
*
|
||||||
* @param wallpaperColors Colors extracted from an image via quantization.
|
* @param wallpaperColors Colors extracted from an image via quantization.
|
||||||
|
* @param filter If false, allow colors that have low chroma, creating grayscale themes.
|
||||||
* @return List of ARGB ints, ordered from highest scoring to lowest.
|
* @return List of ARGB ints, ordered from highest scoring to lowest.
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun getSeedColors(wallpaperColors: WallpaperColors): List<Int> {
|
@JvmOverloads
|
||||||
|
fun getSeedColors(wallpaperColors: WallpaperColors, filter: Boolean = true): List<Int> {
|
||||||
val totalPopulation = wallpaperColors.allColors.values.reduce { a, b -> a + b }
|
val totalPopulation = wallpaperColors.allColors.values.reduce { a, b -> a + b }
|
||||||
.toDouble()
|
.toDouble()
|
||||||
val totalPopulationMeaningless = (totalPopulation == 0.0)
|
val totalPopulationMeaningless = (totalPopulation == 0.0)
|
||||||
@@ -317,9 +339,12 @@ class ColorScheme(
|
|||||||
val distinctColors = wallpaperColors.mainColors.map {
|
val distinctColors = wallpaperColors.mainColors.map {
|
||||||
it.toArgb()
|
it.toArgb()
|
||||||
}.distinct().filter {
|
}.distinct().filter {
|
||||||
|
if (!filter) {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
Cam.fromInt(it).chroma >= MIN_CHROMA
|
Cam.fromInt(it).chroma >= MIN_CHROMA
|
||||||
|
}
|
||||||
}.toList()
|
}.toList()
|
||||||
|
|
||||||
if (distinctColors.isEmpty()) {
|
if (distinctColors.isEmpty()) {
|
||||||
return listOf(GOOGLE_BLUE)
|
return listOf(GOOGLE_BLUE)
|
||||||
}
|
}
|
||||||
@@ -332,7 +357,7 @@ class ColorScheme(
|
|||||||
val intToCam = wallpaperColors.allColors.mapValues { Cam.fromInt(it.key) }
|
val intToCam = wallpaperColors.allColors.mapValues { Cam.fromInt(it.key) }
|
||||||
|
|
||||||
// Get an array with 360 slots. A slot contains the percentage of colors with that hue.
|
// Get an array with 360 slots. A slot contains the percentage of colors with that hue.
|
||||||
val hueProportions = huePopulations(intToCam, intToProportion)
|
val hueProportions = huePopulations(intToCam, intToProportion, filter)
|
||||||
// Map each color to the percentage of the image with its hue.
|
// Map each color to the percentage of the image with its hue.
|
||||||
val intToHueProportion = wallpaperColors.allColors.mapValues {
|
val intToHueProportion = wallpaperColors.allColors.mapValues {
|
||||||
val cam = intToCam[it.key]!!
|
val cam = intToCam[it.key]!!
|
||||||
@@ -346,13 +371,12 @@ class ColorScheme(
|
|||||||
// Remove any inappropriate seed colors. For example, low chroma colors look grayscale
|
// Remove any inappropriate seed colors. For example, low chroma colors look grayscale
|
||||||
// raising their chroma will turn them to a much louder color that may not have been
|
// raising their chroma will turn them to a much louder color that may not have been
|
||||||
// in the image.
|
// in the image.
|
||||||
val filteredIntToCam = intToCam.filter {
|
val filteredIntToCam = if (!filter) intToCam else (intToCam.filter {
|
||||||
val cam = it.value
|
val cam = it.value
|
||||||
val lstar = lstarFromInt(it.key)
|
|
||||||
val proportion = intToHueProportion[it.key]!!
|
val proportion = intToHueProportion[it.key]!!
|
||||||
cam.chroma >= MIN_CHROMA &&
|
cam.chroma >= MIN_CHROMA &&
|
||||||
(totalPopulationMeaningless || proportion > 0.01)
|
(totalPopulationMeaningless || proportion > 0.01)
|
||||||
}
|
})
|
||||||
// Sort the colors by score, from high to low.
|
// Sort the colors by score, from high to low.
|
||||||
val intToScoreIntermediate = filteredIntToCam.mapValues {
|
val intToScoreIntermediate = filteredIntToCam.mapValues {
|
||||||
score(it.value, intToHueProportion[it.key]!!)
|
score(it.value, intToHueProportion[it.key]!!)
|
||||||
@@ -444,7 +468,8 @@ class ColorScheme(
|
|||||||
|
|
||||||
private fun huePopulations(
|
private fun huePopulations(
|
||||||
camByColor: Map<Int, Cam>,
|
camByColor: Map<Int, Cam>,
|
||||||
populationByColor: Map<Int, Double>
|
populationByColor: Map<Int, Double>,
|
||||||
|
filter: Boolean = true
|
||||||
): List<Double> {
|
): List<Double> {
|
||||||
val huePopulation = List(size = 360, init = { 0.0 }).toMutableList()
|
val huePopulation = List(size = 360, init = { 0.0 }).toMutableList()
|
||||||
|
|
||||||
@@ -452,7 +477,7 @@ class ColorScheme(
|
|||||||
val population = populationByColor[entry.key]!!
|
val population = populationByColor[entry.key]!!
|
||||||
val cam = camByColor[entry.key]!!
|
val cam = camByColor[entry.key]!!
|
||||||
val hue = cam.hue.roundToInt() % 360
|
val hue = cam.hue.roundToInt() % 360
|
||||||
if (cam.chroma <= MIN_CHROMA) {
|
if (filter && cam.chroma <= MIN_CHROMA) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
huePopulation[hue] = huePopulation[hue] + population
|
huePopulation[hue] = huePopulation[hue] + population
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ import com.android.systemui.dagger.qualifiers.Background;
|
|||||||
import com.android.systemui.dagger.qualifiers.Main;
|
import com.android.systemui.dagger.qualifiers.Main;
|
||||||
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
|
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
|
||||||
import com.android.systemui.monet.ColorScheme;
|
import com.android.systemui.monet.ColorScheme;
|
||||||
|
import com.android.systemui.monet.Style;
|
||||||
import com.android.systemui.plugins.ActivityStarter;
|
import com.android.systemui.plugins.ActivityStarter;
|
||||||
import com.android.systemui.plugins.FalsingManager;
|
import com.android.systemui.plugins.FalsingManager;
|
||||||
import com.android.systemui.shared.system.SysUiStatsLog;
|
import com.android.systemui.shared.system.SysUiStatsLog;
|
||||||
@@ -627,7 +628,7 @@ public class MediaControlPanel {
|
|||||||
if (artworkIcon != null) {
|
if (artworkIcon != null) {
|
||||||
WallpaperColors wallpaperColors = WallpaperColors
|
WallpaperColors wallpaperColors = WallpaperColors
|
||||||
.fromBitmap(artworkIcon.getBitmap());
|
.fromBitmap(artworkIcon.getBitmap());
|
||||||
mutableColorScheme = new ColorScheme(wallpaperColors, true);
|
mutableColorScheme = new ColorScheme(wallpaperColors, true, Style.CONTENT);
|
||||||
artwork = getScaledBackground(artworkIcon, width, height);
|
artwork = getScaledBackground(artworkIcon, width, height);
|
||||||
isArtworkBound = true;
|
isArtworkBound = true;
|
||||||
} else {
|
} else {
|
||||||
@@ -637,7 +638,8 @@ public class MediaControlPanel {
|
|||||||
try {
|
try {
|
||||||
Drawable icon = mContext.getPackageManager()
|
Drawable icon = mContext.getPackageManager()
|
||||||
.getApplicationIcon(data.getPackageName());
|
.getApplicationIcon(data.getPackageName());
|
||||||
mutableColorScheme = new ColorScheme(WallpaperColors.fromDrawable(icon), true);
|
mutableColorScheme = new ColorScheme(WallpaperColors.fromDrawable(icon), true,
|
||||||
|
Style.CONTENT);
|
||||||
} catch (PackageManager.NameNotFoundException e) {
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
Log.w(TAG, "Cannot find icon for package " + data.getPackageName(), e);
|
Log.w(TAG, "Cannot find icon for package " + data.getPackageName(), e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ import org.json.JSONException;
|
|||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -639,6 +640,11 @@ public class ThemeOverlayController extends CoreStartable implements Dumpable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Style fetchThemeStyleFromSetting() {
|
private Style fetchThemeStyleFromSetting() {
|
||||||
|
// Allow-list of Style objects that can be created from a setting string, i.e. can be
|
||||||
|
// used as a system-wide theme.
|
||||||
|
// - Content intentionally excluded, intended for media player, not system-wide
|
||||||
|
List<Style> validStyles = Arrays.asList(Style.EXPRESSIVE, Style.SPRITZ, Style.TONAL_SPOT,
|
||||||
|
Style.FRUIT_SALAD, Style.RAINBOW, Style.VIBRANT);
|
||||||
Style style = mThemeStyle;
|
Style style = mThemeStyle;
|
||||||
final String overlayPackageJson = mSecureSettings.getStringForUser(
|
final String overlayPackageJson = mSecureSettings.getStringForUser(
|
||||||
Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES,
|
Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES,
|
||||||
@@ -648,6 +654,9 @@ public class ThemeOverlayController extends CoreStartable implements Dumpable {
|
|||||||
JSONObject object = new JSONObject(overlayPackageJson);
|
JSONObject object = new JSONObject(overlayPackageJson);
|
||||||
style = Style.valueOf(
|
style = Style.valueOf(
|
||||||
object.getString(ThemeOverlayApplier.OVERLAY_CATEGORY_THEME_STYLE));
|
object.getString(ThemeOverlayApplier.OVERLAY_CATEGORY_THEME_STYLE));
|
||||||
|
if (!validStyles.contains(style)) {
|
||||||
|
style = Style.TONAL_SPOT;
|
||||||
|
}
|
||||||
} catch (JSONException | IllegalArgumentException e) {
|
} catch (JSONException | IllegalArgumentException e) {
|
||||||
Log.i(TAG, "Failed to parse THEME_CUSTOMIZATION_OVERLAY_PACKAGES.", e);
|
Log.i(TAG, "Failed to parse THEME_CUSTOMIZATION_OVERLAY_PACKAGES.", e);
|
||||||
style = Style.TONAL_SPOT;
|
style = Style.TONAL_SPOT;
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ public class ColorSchemeTest extends SysuiTestCase {
|
|||||||
Style.VIBRANT /* style */);
|
Style.VIBRANT /* style */);
|
||||||
int neutralMid = colorScheme.getNeutral1().get(colorScheme.getNeutral1().size() / 2);
|
int neutralMid = colorScheme.getNeutral1().get(colorScheme.getNeutral1().size() / 2);
|
||||||
Cam cam = Cam.fromInt(neutralMid);
|
Cam cam = Cam.fromInt(neutralMid);
|
||||||
Assert.assertTrue("chroma was " + cam.getChroma(), Math.floor(cam.getChroma()) <= 10.0);
|
Assert.assertTrue("chroma was " + cam.getChroma(), Math.floor(cam.getChroma()) <= 12.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -74,6 +74,8 @@ import org.mockito.Captor;
|
|||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
@@ -345,7 +347,9 @@ public class ThemeOverlayControllerTest extends SysuiTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void onSettingChanged_honorThemeStyle() {
|
public void onSettingChanged_honorThemeStyle() {
|
||||||
when(mDeviceProvisionedController.isUserSetup(anyInt())).thenReturn(true);
|
when(mDeviceProvisionedController.isUserSetup(anyInt())).thenReturn(true);
|
||||||
for (Style style : Style.values()) {
|
List<Style> validStyles = Arrays.asList(Style.EXPRESSIVE, Style.SPRITZ, Style.TONAL_SPOT,
|
||||||
|
Style.FRUIT_SALAD, Style.RAINBOW, Style.VIBRANT);
|
||||||
|
for (Style style : validStyles) {
|
||||||
reset(mSecureSettings);
|
reset(mSecureSettings);
|
||||||
|
|
||||||
String jsonString = "{\"android.theme.customization.system_palette\":\"A16B00\","
|
String jsonString = "{\"android.theme.customization.system_palette\":\"A16B00\","
|
||||||
|
|||||||
Reference in New Issue
Block a user