Merge "Add monet to AOSP" into sc-v2-dev
This commit is contained in:
@@ -74,6 +74,11 @@ final class DisabledWallpaperManager extends WallpaperManager {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int unsupportedInt() {
|
||||||
|
if (DEBUG) Log.w(TAG, "unsupported method called; returning -1", new Exception());
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Drawable getDrawable() {
|
public Drawable getDrawable() {
|
||||||
return unsupported();
|
return unsupported();
|
||||||
@@ -189,12 +194,12 @@ final class DisabledWallpaperManager extends WallpaperManager {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getWallpaperId(int which) {
|
public int getWallpaperId(int which) {
|
||||||
return unsupported();
|
return unsupportedInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getWallpaperIdForUser(int which, int userId) {
|
public int getWallpaperIdForUser(int which, int userId) {
|
||||||
return unsupported();
|
return unsupportedInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -209,7 +214,8 @@ final class DisabledWallpaperManager extends WallpaperManager {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int setResource(int resid, int which) throws IOException {
|
public int setResource(int resid, int which) throws IOException {
|
||||||
return unsupported();
|
unsupported();
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -220,19 +226,22 @@ final class DisabledWallpaperManager extends WallpaperManager {
|
|||||||
@Override
|
@Override
|
||||||
public int setBitmap(Bitmap fullImage, Rect visibleCropHint, boolean allowBackup)
|
public int setBitmap(Bitmap fullImage, Rect visibleCropHint, boolean allowBackup)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
return unsupported();
|
unsupported();
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int setBitmap(Bitmap fullImage, Rect visibleCropHint, boolean allowBackup, int which)
|
public int setBitmap(Bitmap fullImage, Rect visibleCropHint, boolean allowBackup, int which)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
return unsupported();
|
unsupported();
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int setBitmap(Bitmap fullImage, Rect visibleCropHint, boolean allowBackup, int which,
|
public int setBitmap(Bitmap fullImage, Rect visibleCropHint, boolean allowBackup, int which,
|
||||||
int userId) throws IOException {
|
int userId) throws IOException {
|
||||||
return unsupported();
|
unsupported();
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -243,13 +252,15 @@ final class DisabledWallpaperManager extends WallpaperManager {
|
|||||||
@Override
|
@Override
|
||||||
public int setStream(InputStream bitmapData, Rect visibleCropHint, boolean allowBackup)
|
public int setStream(InputStream bitmapData, Rect visibleCropHint, boolean allowBackup)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
return unsupported();
|
unsupported();
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int setStream(InputStream bitmapData, Rect visibleCropHint, boolean allowBackup,
|
public int setStream(InputStream bitmapData, Rect visibleCropHint, boolean allowBackup,
|
||||||
int which) throws IOException {
|
int which) throws IOException {
|
||||||
return unsupported();
|
unsupported();
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -259,12 +270,12 @@ final class DisabledWallpaperManager extends WallpaperManager {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getDesiredMinimumWidth() {
|
public int getDesiredMinimumWidth() {
|
||||||
return unsupported();
|
return unsupportedInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getDesiredMinimumHeight() {
|
public int getDesiredMinimumHeight() {
|
||||||
return unsupported();
|
return unsupportedInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ android_library {
|
|||||||
"iconloader_base",
|
"iconloader_base",
|
||||||
"SystemUI-tags",
|
"SystemUI-tags",
|
||||||
"SystemUI-proto",
|
"SystemUI-proto",
|
||||||
|
"monet",
|
||||||
"dagger2",
|
"dagger2",
|
||||||
"jsr330",
|
"jsr330",
|
||||||
"lottie",
|
"lottie",
|
||||||
@@ -179,6 +180,7 @@ android_library {
|
|||||||
"mockito-target-extended-minus-junit4",
|
"mockito-target-extended-minus-junit4",
|
||||||
"testables",
|
"testables",
|
||||||
"truth-prebuilt",
|
"truth-prebuilt",
|
||||||
|
"monet",
|
||||||
"dagger2",
|
"dagger2",
|
||||||
"jsr330",
|
"jsr330",
|
||||||
"WindowManager-Shell",
|
"WindowManager-Shell",
|
||||||
|
|||||||
31
packages/SystemUI/monet/Android.bp
Normal file
31
packages/SystemUI/monet/Android.bp
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
//
|
||||||
|
// Copyright (C) 2021 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.
|
||||||
|
//
|
||||||
|
package {
|
||||||
|
default_applicable_licenses: ["Android-Apache-2.0"],
|
||||||
|
}
|
||||||
|
|
||||||
|
java_library {
|
||||||
|
name: "monet",
|
||||||
|
platform_apis: true,
|
||||||
|
static_libs: [
|
||||||
|
"androidx.annotation_annotation",
|
||||||
|
"androidx.core_core",
|
||||||
|
],
|
||||||
|
srcs: [
|
||||||
|
"src/**/*.java",
|
||||||
|
"src/**/*.kt",
|
||||||
|
],
|
||||||
|
}
|
||||||
20
packages/SystemUI/monet/AndroidManifest.xml
Normal file
20
packages/SystemUI/monet/AndroidManifest.xml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright (C) 2021 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="com.android.systemui.monet">
|
||||||
|
</manifest>
|
||||||
@@ -0,0 +1,246 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.systemui.monet
|
||||||
|
|
||||||
|
import android.annotation.ColorInt
|
||||||
|
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.roundToInt
|
||||||
|
|
||||||
|
const val TAG = "ColorScheme"
|
||||||
|
|
||||||
|
const val ACCENT1_CHROMA = 48.0f
|
||||||
|
const val ACCENT2_CHROMA = 16.0f
|
||||||
|
const val ACCENT3_CHROMA = 32.0f
|
||||||
|
const val ACCENT3_HUE_SHIFT = 60.0f
|
||||||
|
|
||||||
|
const val NEUTRAL1_CHROMA = 4.0f
|
||||||
|
const val NEUTRAL2_CHROMA = 8.0f
|
||||||
|
|
||||||
|
const val GOOGLE_BLUE = 0xFF1b6ef3.toInt()
|
||||||
|
|
||||||
|
const val MIN_CHROMA = 15
|
||||||
|
const val MIN_LSTAR = 10
|
||||||
|
|
||||||
|
public class ColorScheme(@ColorInt seed: Int, val darkTheme: Boolean) {
|
||||||
|
|
||||||
|
val accent1: List<Int>
|
||||||
|
val accent2: List<Int>
|
||||||
|
val accent3: List<Int>
|
||||||
|
val neutral1: List<Int>
|
||||||
|
val neutral2: List<Int>
|
||||||
|
|
||||||
|
constructor(wallpaperColors: WallpaperColors, darkTheme: Boolean):
|
||||||
|
this(getSeedColor(wallpaperColors), darkTheme)
|
||||||
|
|
||||||
|
val allAccentColors: List<Int>
|
||||||
|
get() {
|
||||||
|
val allColors = mutableListOf<Int>()
|
||||||
|
allColors.addAll(accent1)
|
||||||
|
allColors.addAll(accent2)
|
||||||
|
allColors.addAll(accent3)
|
||||||
|
return allColors
|
||||||
|
}
|
||||||
|
|
||||||
|
val allNeutralColors: List<Int>
|
||||||
|
get() {
|
||||||
|
val allColors = mutableListOf<Int>()
|
||||||
|
allColors.addAll(neutral1)
|
||||||
|
allColors.addAll(neutral2)
|
||||||
|
return allColors
|
||||||
|
}
|
||||||
|
|
||||||
|
val backgroundColor
|
||||||
|
get() = ColorUtils.setAlphaComponent(if (darkTheme) neutral1[8] else neutral1[0], 0xFF)
|
||||||
|
|
||||||
|
val accentColor
|
||||||
|
get() = ColorUtils.setAlphaComponent(if (darkTheme) accent1[2] else accent1[6], 0xFF)
|
||||||
|
|
||||||
|
init {
|
||||||
|
val seedArgb = if (seed == Color.TRANSPARENT) GOOGLE_BLUE else seed
|
||||||
|
val camSeed = Cam.fromInt(seedArgb)
|
||||||
|
val hue = camSeed.hue
|
||||||
|
val chroma = camSeed.chroma.coerceAtLeast(ACCENT1_CHROMA)
|
||||||
|
accent1 = Shades.of(hue, chroma).toList()
|
||||||
|
accent2 = Shades.of(hue, ACCENT2_CHROMA).toList()
|
||||||
|
accent3 = Shades.of(hue + ACCENT3_HUE_SHIFT, ACCENT3_CHROMA).toList()
|
||||||
|
neutral1 = Shades.of(hue, NEUTRAL1_CHROMA).toList()
|
||||||
|
neutral2 = Shades.of(hue, NEUTRAL2_CHROMA).toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return "ColorScheme {\n" +
|
||||||
|
" neutral1: ${humanReadable(neutral1)}\n" +
|
||||||
|
" neutral2: ${humanReadable(neutral2)}\n" +
|
||||||
|
" accent1: ${humanReadable(accent1)}\n" +
|
||||||
|
" accent2: ${humanReadable(accent2)}\n" +
|
||||||
|
" accent3: ${humanReadable(accent3)}\n" +
|
||||||
|
"}"
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Identifies a color to create a color scheme from.
|
||||||
|
*
|
||||||
|
* @param wallpaperColors Colors extracted from an image via quantization.
|
||||||
|
* @return ARGB int representing the color
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
@ColorInt
|
||||||
|
fun getSeedColor(wallpaperColors: WallpaperColors): Int {
|
||||||
|
return getSeedColors(wallpaperColors).first()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters and ranks colors from WallpaperColors.
|
||||||
|
*
|
||||||
|
* @param wallpaperColors Colors extracted from an image via quantization.
|
||||||
|
* @return List of ARGB ints, ordered from highest scoring to lowest.
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun getSeedColors(wallpaperColors: WallpaperColors): List<Int> {
|
||||||
|
val totalPopulation = wallpaperColors.allColors.values.reduce { a, b -> a + b }
|
||||||
|
.toDouble()
|
||||||
|
val totalPopulationMeaningless = (totalPopulation == 0.0)
|
||||||
|
if (totalPopulationMeaningless) {
|
||||||
|
// WallpaperColors with a population of 0 indicate the colors didn't come from
|
||||||
|
// quantization. Instead of scoring, trust the ordering of the provided primary
|
||||||
|
// secondary/tertiary colors.
|
||||||
|
//
|
||||||
|
// In this case, the colors are usually from a Live Wallpaper.
|
||||||
|
val distinctColors = wallpaperColors.mainColors.map {
|
||||||
|
it.toArgb()
|
||||||
|
}.distinct().filter {
|
||||||
|
val cam = Cam.fromInt(it)
|
||||||
|
val lstar = lstarFromInt(it)
|
||||||
|
cam.chroma >= MIN_CHROMA && lstar >= MIN_LSTAR
|
||||||
|
}.toList()
|
||||||
|
|
||||||
|
if (distinctColors.isEmpty()) {
|
||||||
|
return listOf(GOOGLE_BLUE)
|
||||||
|
}
|
||||||
|
return distinctColors
|
||||||
|
}
|
||||||
|
|
||||||
|
val intToProportion = wallpaperColors.allColors.mapValues {
|
||||||
|
it.value.toDouble() / totalPopulation
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
// Map each color to the percentage of the image with its hue.
|
||||||
|
val intToHueProportion = wallpaperColors.allColors.mapValues {
|
||||||
|
val cam = intToCam[it.key]!!
|
||||||
|
val hue = cam.hue.roundToInt()
|
||||||
|
var proportion = 0.0
|
||||||
|
for (i in hue - 15..hue + 15) {
|
||||||
|
proportion += hueProportions[wrapDegrees(i)]
|
||||||
|
}
|
||||||
|
proportion
|
||||||
|
}
|
||||||
|
// 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 cam = it.value
|
||||||
|
val lstar = lstarFromInt(it.key)
|
||||||
|
val proportion = intToHueProportion[it.key]!!
|
||||||
|
cam.chroma >= MIN_CHROMA && lstar >= MIN_LSTAR &&
|
||||||
|
(totalPopulationMeaningless || proportion > 0.01)
|
||||||
|
}
|
||||||
|
// Sort the colors by score, from high to low.
|
||||||
|
val seeds = mutableListOf<Int>()
|
||||||
|
val intToScoreIntermediate = filteredIntToCam.mapValues {
|
||||||
|
score(it.value, intToHueProportion[it.key]!!)
|
||||||
|
}
|
||||||
|
val intToScore = intToScoreIntermediate.entries.toMutableList()
|
||||||
|
intToScore.sortByDescending { it.value }
|
||||||
|
|
||||||
|
// Go through the colors, from high score to low score. If there isn't already a seed
|
||||||
|
// color with a hue close to color being examined, add the color being examined to the
|
||||||
|
// seed colors.
|
||||||
|
for (entry in intToScore) {
|
||||||
|
val int = entry.key
|
||||||
|
val existingSeedNearby = seeds.find {
|
||||||
|
val hueA = intToCam[int]!!.hue
|
||||||
|
val hueB = intToCam[it]!!.hue
|
||||||
|
hueDiff(hueA, hueB) < 15 } != null
|
||||||
|
if (existingSeedNearby) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seeds.add(int)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (seeds.isEmpty()) {
|
||||||
|
// Use gBlue 500 if there are 0 colors
|
||||||
|
seeds.add(GOOGLE_BLUE)
|
||||||
|
}
|
||||||
|
|
||||||
|
return seeds
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun wrapDegrees(degrees: Int): Int {
|
||||||
|
return when {
|
||||||
|
degrees < 0 -> {
|
||||||
|
(degrees % 360) + 360
|
||||||
|
}
|
||||||
|
degrees >= 360 -> {
|
||||||
|
degrees % 360
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
degrees
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hueDiff(a: Float, b: Float): Float {
|
||||||
|
return 180f - ((a - b).absoluteValue - 180f).absoluteValue
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun humanReadable(colors: List<Int>): String {
|
||||||
|
return colors.joinToString { "#" + Integer.toHexString(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun score(cam: Cam, proportion: Double): Double {
|
||||||
|
val proportionScore = 0.7 * 100.0 * proportion
|
||||||
|
val chromaScore = if (cam.chroma < ACCENT1_CHROMA) 0.1 * (cam.chroma - ACCENT1_CHROMA)
|
||||||
|
else 0.3 * (cam.chroma - ACCENT1_CHROMA)
|
||||||
|
return chromaScore + proportionScore
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun huePopulations(
|
||||||
|
camByColor: Map<Int, Cam>,
|
||||||
|
populationByColor: Map<Int, Double>
|
||||||
|
): List<Double> {
|
||||||
|
val huePopulation = List(size = 360, init = { 0.0 }).toMutableList()
|
||||||
|
|
||||||
|
for (entry in populationByColor.entries) {
|
||||||
|
val population = populationByColor[entry.key]!!
|
||||||
|
val cam = camByColor[entry.key]!!
|
||||||
|
val hue = cam.hue.roundToInt() % 360
|
||||||
|
huePopulation[hue] = huePopulation[hue] + population
|
||||||
|
}
|
||||||
|
|
||||||
|
return huePopulation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 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.
|
||||||
|
*/
|
||||||
|
package com.android.systemui.monet;
|
||||||
|
|
||||||
|
|
||||||
|
import androidx.annotation.ColorInt;
|
||||||
|
|
||||||
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
|
import com.android.internal.graphics.ColorUtils;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate sets of colors that are shades of the same color
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
public class Shades {
|
||||||
|
/**
|
||||||
|
* Combining the ability to convert between relative luminance and perceptual luminance with
|
||||||
|
* contrast leads to a design system that can be based on a linear value to determine contrast,
|
||||||
|
* rather than a ratio.
|
||||||
|
*
|
||||||
|
* This codebase implements a design system that has that property, and as a result, we can
|
||||||
|
* guarantee that any shades 5 steps from each other have a contrast ratio of at least 4.5.
|
||||||
|
* 4.5 is the requirement for smaller text contrast in WCAG 2.1 and earlier.
|
||||||
|
*
|
||||||
|
* However, lstar 50 does _not_ have a contrast ratio >= 4.5 with lstar 100.
|
||||||
|
* lstar 49.6 is the smallest lstar that will lead to a contrast ratio >= 4.5 with lstar 100,
|
||||||
|
* and it also contrasts >= 4.5 with lstar 100.
|
||||||
|
*/
|
||||||
|
public static final float MIDDLE_LSTAR = 49.6f;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate shades of a color. Ordered in lightness _descending_.
|
||||||
|
* <p>
|
||||||
|
* The first shade will be at 95% lightness, the next at 90, 80, etc. through 0.
|
||||||
|
*
|
||||||
|
* @param hue hue in CAM16 color space
|
||||||
|
* @param chroma chroma in CAM16 color space
|
||||||
|
* @return shades of a color, as argb integers. Ordered by lightness descending.
|
||||||
|
*/
|
||||||
|
public static @ColorInt int[] of(float hue, float chroma) {
|
||||||
|
int[] shades = new int[12];
|
||||||
|
shades[0] = ColorUtils.CAMToColor(hue, chroma, 99);
|
||||||
|
shades[1] = ColorUtils.CAMToColor(hue, chroma, 95);
|
||||||
|
for (int i = 2; i < 12; i++) {
|
||||||
|
float lStar = (i == 6) ? MIDDLE_LSTAR : 100 - 10 * (i - 1);
|
||||||
|
shades[i] = ColorUtils.CAMToColor(hue, chroma, lStar);
|
||||||
|
}
|
||||||
|
return shades;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
<bool name="flag_notification_pipeline2_rendering">false</bool>
|
<bool name="flag_notification_pipeline2_rendering">false</bool>
|
||||||
<bool name="flag_notif_updates">true</bool>
|
<bool name="flag_notif_updates">true</bool>
|
||||||
|
|
||||||
<bool name="flag_monet">false</bool>
|
<bool name="flag_monet">true</bool>
|
||||||
|
|
||||||
<!-- AOD/Lockscreen alternate layout -->
|
<!-- AOD/Lockscreen alternate layout -->
|
||||||
<bool name="flag_keyguard_layout">true</bool>
|
<bool name="flag_keyguard_layout">true</bool>
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import android.content.IntentFilter;
|
|||||||
import android.content.om.FabricatedOverlay;
|
import android.content.om.FabricatedOverlay;
|
||||||
import android.content.om.OverlayIdentifier;
|
import android.content.om.OverlayIdentifier;
|
||||||
import android.content.pm.UserInfo;
|
import android.content.pm.UserInfo;
|
||||||
|
import android.content.res.Configuration;
|
||||||
import android.database.ContentObserver;
|
import android.database.ContentObserver;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
@@ -47,9 +48,11 @@ import android.provider.Settings;
|
|||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.ArrayMap;
|
import android.util.ArrayMap;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.util.TypedValue;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.android.internal.graphics.ColorUtils;
|
||||||
import com.android.systemui.Dumpable;
|
import com.android.systemui.Dumpable;
|
||||||
import com.android.systemui.SystemUI;
|
import com.android.systemui.SystemUI;
|
||||||
import com.android.systemui.broadcast.BroadcastDispatcher;
|
import com.android.systemui.broadcast.BroadcastDispatcher;
|
||||||
@@ -59,6 +62,7 @@ import com.android.systemui.dagger.qualifiers.Main;
|
|||||||
import com.android.systemui.dump.DumpManager;
|
import com.android.systemui.dump.DumpManager;
|
||||||
import com.android.systemui.flags.FeatureFlags;
|
import com.android.systemui.flags.FeatureFlags;
|
||||||
import com.android.systemui.keyguard.WakefulnessLifecycle;
|
import com.android.systemui.keyguard.WakefulnessLifecycle;
|
||||||
|
import com.android.systemui.monet.ColorScheme;
|
||||||
import com.android.systemui.settings.UserTracker;
|
import com.android.systemui.settings.UserTracker;
|
||||||
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
|
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
|
||||||
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
|
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
|
||||||
@@ -71,6 +75,7 @@ import java.io.FileDescriptor;
|
|||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
@@ -107,6 +112,7 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
|
|||||||
private DeviceProvisionedController mDeviceProvisionedController;
|
private DeviceProvisionedController mDeviceProvisionedController;
|
||||||
private WallpaperColors mCurrentColors;
|
private WallpaperColors mCurrentColors;
|
||||||
private WallpaperManager mWallpaperManager;
|
private WallpaperManager mWallpaperManager;
|
||||||
|
private ColorScheme mColorScheme;
|
||||||
// If fabricated overlays were already created for the current theme.
|
// If fabricated overlays were already created for the current theme.
|
||||||
private boolean mNeedsOverlayCreation;
|
private boolean mNeedsOverlayCreation;
|
||||||
// Dominant color extracted from wallpaper, NOT the color used on the overlay
|
// Dominant color extracted from wallpaper, NOT the color used on the overlay
|
||||||
@@ -403,25 +409,47 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
|
|||||||
* Return the main theme color from a given {@link WallpaperColors} instance.
|
* Return the main theme color from a given {@link WallpaperColors} instance.
|
||||||
*/
|
*/
|
||||||
protected int getNeutralColor(@NonNull WallpaperColors wallpaperColors) {
|
protected int getNeutralColor(@NonNull WallpaperColors wallpaperColors) {
|
||||||
return wallpaperColors.getPrimaryColor().toArgb();
|
return ColorScheme.getSeedColor(wallpaperColors);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected int getAccentColor(@NonNull WallpaperColors wallpaperColors) {
|
protected int getAccentColor(@NonNull WallpaperColors wallpaperColors) {
|
||||||
Color accentCandidate = wallpaperColors.getSecondaryColor();
|
return ColorScheme.getSeedColor(wallpaperColors);
|
||||||
if (accentCandidate == null) {
|
|
||||||
accentCandidate = wallpaperColors.getTertiaryColor();
|
|
||||||
}
|
|
||||||
if (accentCandidate == null) {
|
|
||||||
accentCandidate = wallpaperColors.getPrimaryColor();
|
|
||||||
}
|
|
||||||
return accentCandidate.toArgb();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a color candidate, return an overlay definition.
|
* Given a color candidate, return an overlay definition.
|
||||||
*/
|
*/
|
||||||
protected @Nullable FabricatedOverlay getOverlay(int color, int type) {
|
protected @Nullable FabricatedOverlay getOverlay(int color, int type) {
|
||||||
return null;
|
boolean nightMode = (mContext.getResources().getConfiguration().uiMode
|
||||||
|
& Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
|
||||||
|
|
||||||
|
mColorScheme = new ColorScheme(color, nightMode);
|
||||||
|
List<Integer> colorShades = type == ACCENT
|
||||||
|
? mColorScheme.getAllAccentColors() : mColorScheme.getAllNeutralColors();
|
||||||
|
String name = type == ACCENT ? "accent" : "neutral";
|
||||||
|
int paletteSize = mColorScheme.getAccent1().size();
|
||||||
|
FabricatedOverlay.Builder overlay =
|
||||||
|
new FabricatedOverlay.Builder("com.android.systemui", name, "android");
|
||||||
|
for (int i = 0; i < colorShades.size(); i++) {
|
||||||
|
int luminosity = i % paletteSize;
|
||||||
|
int paletteIndex = i / paletteSize + 1;
|
||||||
|
String resourceName;
|
||||||
|
switch (luminosity) {
|
||||||
|
case 0:
|
||||||
|
resourceName = "android:color/system_" + name + paletteIndex + "_10";
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
resourceName = "android:color/system_" + name + paletteIndex + "_50";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
int l = luminosity - 1;
|
||||||
|
resourceName = "android:color/system_" + name + paletteIndex + "_" + l + "00";
|
||||||
|
}
|
||||||
|
overlay.setResourceValue(resourceName, TypedValue.TYPE_INT_COLOR_ARGB8,
|
||||||
|
ColorUtils.setAlphaComponent(colorShades.get(i), 0xFF));
|
||||||
|
}
|
||||||
|
|
||||||
|
return overlay.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateThemeOverlays() {
|
private void updateThemeOverlays() {
|
||||||
@@ -521,7 +549,7 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
|
|||||||
.map(key -> key + " -> " + categoryToPackage.get(key)).collect(
|
.map(key -> key + " -> " + categoryToPackage.get(key)).collect(
|
||||||
Collectors.joining(", ")));
|
Collectors.joining(", ")));
|
||||||
}
|
}
|
||||||
Runnable overlaysAppliedRunnable = () -> onOverlaysApplied();
|
Runnable overlaysAppliedRunnable = this::onOverlaysApplied;
|
||||||
if (mNeedsOverlayCreation) {
|
if (mNeedsOverlayCreation) {
|
||||||
mNeedsOverlayCreation = false;
|
mNeedsOverlayCreation = false;
|
||||||
mThemeManager.applyCurrentUserOverlays(categoryToPackage, new FabricatedOverlay[] {
|
mThemeManager.applyCurrentUserOverlays(categoryToPackage, new FabricatedOverlay[] {
|
||||||
@@ -544,6 +572,7 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
|
|||||||
pw.println("mSecondaryOverlay=" + mSecondaryOverlay);
|
pw.println("mSecondaryOverlay=" + mSecondaryOverlay);
|
||||||
pw.println("mNeutralOverlay=" + mNeutralOverlay);
|
pw.println("mNeutralOverlay=" + mNeutralOverlay);
|
||||||
pw.println("mIsMonetEnabled=" + mIsMonetEnabled);
|
pw.println("mIsMonetEnabled=" + mIsMonetEnabled);
|
||||||
|
pw.println("mColorScheme=" + mColorScheme);
|
||||||
pw.println("mNeedsOverlayCreation=" + mNeedsOverlayCreation);
|
pw.println("mNeedsOverlayCreation=" + mNeedsOverlayCreation);
|
||||||
pw.println("mAcceptColorEvents=" + mAcceptColorEvents);
|
pw.println("mAcceptColorEvents=" + mAcceptColorEvents);
|
||||||
pw.println("mDeferredThemeEvaluation=" + mDeferredThemeEvaluation);
|
pw.println("mDeferredThemeEvaluation=" + mDeferredThemeEvaluation);
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.systemui.monet;
|
||||||
|
|
||||||
|
import android.app.WallpaperColors;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.testing.AndroidTestingRunner;
|
||||||
|
|
||||||
|
import androidx.test.filters.SmallTest;
|
||||||
|
|
||||||
|
import com.android.systemui.SysuiTestCase;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@SmallTest
|
||||||
|
@RunWith(AndroidTestingRunner.class)
|
||||||
|
public class ColorSchemeTest extends SysuiTestCase {
|
||||||
|
@Test
|
||||||
|
public void testFilterTransparency() {
|
||||||
|
ColorScheme colorScheme = new ColorScheme(Color.TRANSPARENT, false /* darkTheme */);
|
||||||
|
Assert.assertEquals(colorScheme.getAllAccentColors(),
|
||||||
|
new ColorScheme(0xFF1b6ef3, false).getAllAccentColors());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDontFilterOpaque() {
|
||||||
|
ColorScheme colorScheme = new ColorScheme(0xFFFF0000, false /* darkTheme */);
|
||||||
|
Assert.assertNotEquals(colorScheme.getAllAccentColors(),
|
||||||
|
new ColorScheme(0xFF1b6ef3, false).getAllAccentColors());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUniqueColors() {
|
||||||
|
WallpaperColors wallpaperColors = new WallpaperColors(Color.valueOf(0xffaec00a),
|
||||||
|
Color.valueOf(0xffaec00a), Color.valueOf(0xffaec00a));
|
||||||
|
|
||||||
|
List<Integer> rankedSeedColors = ColorScheme.getSeedColors(wallpaperColors);
|
||||||
|
Assert.assertEquals(rankedSeedColors, List.of(0xffaec00a));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFiltersInvalidColors() {
|
||||||
|
WallpaperColors wallpaperColors = new WallpaperColors(Color.valueOf(0xff5e7ea2),
|
||||||
|
Color.valueOf(0xff5e7ea2), Color.valueOf(0xff000000));
|
||||||
|
|
||||||
|
List<Integer> rankedSeedColors = ColorScheme.getSeedColors(wallpaperColors);
|
||||||
|
Assert.assertEquals(rankedSeedColors, List.of(0xff5e7ea2));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInvalidColorBecomesGBlue() {
|
||||||
|
WallpaperColors wallpaperColors = new WallpaperColors(Color.valueOf(0xff000000), null,
|
||||||
|
null);
|
||||||
|
|
||||||
|
List<Integer> rankedSeedColors = ColorScheme.getSeedColors(wallpaperColors);
|
||||||
|
Assert.assertEquals(rankedSeedColors, List.of(0xFF1b6ef3));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDontFilterRRGGBB() {
|
||||||
|
ColorScheme colorScheme = new ColorScheme(0xFF0000, false /* darkTheme */);
|
||||||
|
Assert.assertEquals(colorScheme.getAllAccentColors(),
|
||||||
|
new ColorScheme(0xFFFF0000, false).getAllAccentColors());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoPopulationSignal() {
|
||||||
|
WallpaperColors wallpaperColors = new WallpaperColors(Color.valueOf(0xffaec00a),
|
||||||
|
Color.valueOf(0xffbe0000), Color.valueOf(0xffcc040f));
|
||||||
|
|
||||||
|
List<Integer> rankedSeedColors = ColorScheme.getSeedColors(wallpaperColors);
|
||||||
|
Assert.assertEquals(rankedSeedColors, List.of(0xffaec00a, 0xffbe0000, 0xffcc040f));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -167,7 +167,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase {
|
|||||||
assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE))
|
assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE))
|
||||||
.isEqualTo(new OverlayIdentifier("ffff0000"));
|
.isEqualTo(new OverlayIdentifier("ffff0000"));
|
||||||
assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_ACCENT_COLOR))
|
assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_ACCENT_COLOR))
|
||||||
.isEqualTo(new OverlayIdentifier("ff0000ff"));
|
.isEqualTo(new OverlayIdentifier("ffff0000"));
|
||||||
|
|
||||||
// Should not ask again if changed to same value
|
// Should not ask again if changed to same value
|
||||||
mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
|
mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
|
||||||
|
|||||||
Reference in New Issue
Block a user