Merge changes from topic "colorSchemeChanges" into tm-dev
* changes: Add 2 chroma to vibrant theme's specification Fix color scheme rotation Add content color scheme for media
This commit is contained in:
committed by
Android (Google) Code Review
commit
e320ce441d
@@ -21,7 +21,6 @@ import android.app.WallpaperColors
|
||||
import android.graphics.Color
|
||||
import com.android.internal.graphics.ColorUtils
|
||||
import com.android.internal.graphics.cam.Cam
|
||||
import com.android.internal.graphics.cam.CamUtils.lstarFromInt
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.math.max
|
||||
import kotlin.math.roundToInt
|
||||
@@ -50,7 +49,7 @@ internal interface Hue {
|
||||
val previousHue = hueAndRotations[previousIndex].first
|
||||
if (ColorScheme.angleIsBetween(sourceHue, thisHue, previousHue)) {
|
||||
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 {
|
||||
override fun get(sourceColor: Cam): Double {
|
||||
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) {
|
||||
fun shades(sourceColor: Cam): List<Int> {
|
||||
val hue = hue.get(sourceColor)
|
||||
@@ -184,8 +195,8 @@ enum class Style(internal val coreSpec: CoreSpec) {
|
||||
a1 = TonalSpec(HueSource(), ChromaMinimum(48.0)),
|
||||
a2 = TonalSpec(HueVibrantSecondary(), ChromaConstant(24.0)),
|
||||
a3 = TonalSpec(HueVibrantTertiary(), ChromaConstant(32.0)),
|
||||
n1 = TonalSpec(HueSource(), ChromaConstant(10.0)),
|
||||
n2 = TonalSpec(HueSource(), ChromaConstant(12.0))
|
||||
n1 = TonalSpec(HueSource(), ChromaConstant(12.0)),
|
||||
n2 = TonalSpec(HueSource(), ChromaConstant(14.0))
|
||||
)),
|
||||
EXPRESSIVE(CoreSpec(
|
||||
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)),
|
||||
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(
|
||||
@@ -231,7 +249,7 @@ class ColorScheme(
|
||||
darkTheme: Boolean,
|
||||
style: Style = Style.TONAL_SPOT
|
||||
):
|
||||
this(getSeedColor(wallpaperColors), darkTheme, style)
|
||||
this(getSeedColor(wallpaperColors, style != Style.CONTENT), darkTheme, style)
|
||||
|
||||
val allAccentColors: List<Int>
|
||||
get() {
|
||||
@@ -260,7 +278,7 @@ class ColorScheme(
|
||||
val proposedSeedCam = Cam.fromInt(seed)
|
||||
val seedArgb = if (seed == Color.TRANSPARENT) {
|
||||
GOOGLE_BLUE
|
||||
} else if (proposedSeedCam.chroma < 5) {
|
||||
} else if (style != Style.CONTENT && proposedSeedCam.chroma < 5) {
|
||||
GOOGLE_BLUE
|
||||
} else {
|
||||
seed
|
||||
@@ -289,22 +307,26 @@ class ColorScheme(
|
||||
* Identifies a color to create a color scheme from.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
@ColorInt
|
||||
fun getSeedColor(wallpaperColors: WallpaperColors): Int {
|
||||
return getSeedColors(wallpaperColors).first()
|
||||
fun getSeedColor(wallpaperColors: WallpaperColors, filter: Boolean = true): Int {
|
||||
return getSeedColors(wallpaperColors, filter).first()
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters and ranks colors from WallpaperColors.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
@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 }
|
||||
.toDouble()
|
||||
val totalPopulationMeaningless = (totalPopulation == 0.0)
|
||||
@@ -317,9 +339,12 @@ class ColorScheme(
|
||||
val distinctColors = wallpaperColors.mainColors.map {
|
||||
it.toArgb()
|
||||
}.distinct().filter {
|
||||
Cam.fromInt(it).chroma >= MIN_CHROMA
|
||||
if (!filter) {
|
||||
true
|
||||
} else {
|
||||
Cam.fromInt(it).chroma >= MIN_CHROMA
|
||||
}
|
||||
}.toList()
|
||||
|
||||
if (distinctColors.isEmpty()) {
|
||||
return listOf(GOOGLE_BLUE)
|
||||
}
|
||||
@@ -332,7 +357,7 @@ class ColorScheme(
|
||||
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.
|
||||
val hueProportions = huePopulations(intToCam, intToProportion)
|
||||
val hueProportions = huePopulations(intToCam, intToProportion, filter)
|
||||
// Map each color to the percentage of the image with its hue.
|
||||
val intToHueProportion = wallpaperColors.allColors.mapValues {
|
||||
val cam = intToCam[it.key]!!
|
||||
@@ -346,13 +371,12 @@ class ColorScheme(
|
||||
// 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
|
||||
// in the image.
|
||||
val filteredIntToCam = intToCam.filter {
|
||||
val filteredIntToCam = if (!filter) intToCam else (intToCam.filter {
|
||||
val cam = it.value
|
||||
val lstar = lstarFromInt(it.key)
|
||||
val proportion = intToHueProportion[it.key]!!
|
||||
cam.chroma >= MIN_CHROMA &&
|
||||
(totalPopulationMeaningless || proportion > 0.01)
|
||||
}
|
||||
})
|
||||
// Sort the colors by score, from high to low.
|
||||
val intToScoreIntermediate = filteredIntToCam.mapValues {
|
||||
score(it.value, intToHueProportion[it.key]!!)
|
||||
@@ -444,7 +468,8 @@ class ColorScheme(
|
||||
|
||||
private fun huePopulations(
|
||||
camByColor: Map<Int, Cam>,
|
||||
populationByColor: Map<Int, Double>
|
||||
populationByColor: Map<Int, Double>,
|
||||
filter: Boolean = true
|
||||
): List<Double> {
|
||||
val huePopulation = List(size = 360, init = { 0.0 }).toMutableList()
|
||||
|
||||
@@ -452,7 +477,7 @@ class ColorScheme(
|
||||
val population = populationByColor[entry.key]!!
|
||||
val cam = camByColor[entry.key]!!
|
||||
val hue = cam.hue.roundToInt() % 360
|
||||
if (cam.chroma <= MIN_CHROMA) {
|
||||
if (filter && cam.chroma <= MIN_CHROMA) {
|
||||
continue
|
||||
}
|
||||
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.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.shared.system.SysUiStatsLog;
|
||||
@@ -627,7 +628,7 @@ public class MediaControlPanel {
|
||||
if (artworkIcon != null) {
|
||||
WallpaperColors wallpaperColors = WallpaperColors
|
||||
.fromBitmap(artworkIcon.getBitmap());
|
||||
mutableColorScheme = new ColorScheme(wallpaperColors, true);
|
||||
mutableColorScheme = new ColorScheme(wallpaperColors, true, Style.CONTENT);
|
||||
artwork = getScaledBackground(artworkIcon, width, height);
|
||||
isArtworkBound = true;
|
||||
} else {
|
||||
@@ -637,7 +638,8 @@ public class MediaControlPanel {
|
||||
try {
|
||||
Drawable icon = mContext.getPackageManager()
|
||||
.getApplicationIcon(data.getPackageName());
|
||||
mutableColorScheme = new ColorScheme(WallpaperColors.fromDrawable(icon), true);
|
||||
mutableColorScheme = new ColorScheme(WallpaperColors.fromDrawable(icon), true,
|
||||
Style.CONTENT);
|
||||
} catch (PackageManager.NameNotFoundException 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 java.io.PrintWriter;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -639,6 +640,11 @@ public class ThemeOverlayController extends CoreStartable implements Dumpable {
|
||||
}
|
||||
|
||||
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;
|
||||
final String overlayPackageJson = mSecureSettings.getStringForUser(
|
||||
Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES,
|
||||
@@ -648,6 +654,9 @@ public class ThemeOverlayController extends CoreStartable implements Dumpable {
|
||||
JSONObject object = new JSONObject(overlayPackageJson);
|
||||
style = Style.valueOf(
|
||||
object.getString(ThemeOverlayApplier.OVERLAY_CATEGORY_THEME_STYLE));
|
||||
if (!validStyles.contains(style)) {
|
||||
style = Style.TONAL_SPOT;
|
||||
}
|
||||
} catch (JSONException | IllegalArgumentException e) {
|
||||
Log.i(TAG, "Failed to parse THEME_CUSTOMIZATION_OVERLAY_PACKAGES.", e);
|
||||
style = Style.TONAL_SPOT;
|
||||
|
||||
@@ -133,7 +133,7 @@ public class ColorSchemeTest extends SysuiTestCase {
|
||||
Style.VIBRANT /* style */);
|
||||
int neutralMid = colorScheme.getNeutral1().get(colorScheme.getNeutral1().size() / 2);
|
||||
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
|
||||
|
||||
@@ -74,6 +74,8 @@ import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@@ -345,7 +347,9 @@ public class ThemeOverlayControllerTest extends SysuiTestCase {
|
||||
@Test
|
||||
public void onSettingChanged_honorThemeStyle() {
|
||||
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);
|
||||
|
||||
String jsonString = "{\"android.theme.customization.system_palette\":\"A16B00\","
|
||||
|
||||
Reference in New Issue
Block a user