Extract color for the hotseat.
- Only considers the bottom fourth of the wallpaper - Is translucent black or white depending on how dark/light the wallpaper is - Hotseat extends behind the nav bar Bug: 27230217 Change-Id: Id4ea6ee91b4dd221b4c277d22d5041cab178801d
This commit is contained in:
+1
-1
@@ -3,7 +3,7 @@ buildscript {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:1.5.0'
|
||||
classpath 'com.android.tools.build:gradle:2.1.0-rc1'
|
||||
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.7.0'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,12 @@
|
||||
|
||||
package com.android.launcher3;
|
||||
|
||||
import android.animation.ArgbEvaluator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
@@ -27,12 +31,13 @@ import android.view.ViewDebug;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.launcher3.dynamicui.ExtractedColors;
|
||||
import com.android.launcher3.logging.UserEventDispatcher;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
|
||||
|
||||
public class Hotseat extends FrameLayout
|
||||
implements UserEventDispatcher.LaunchSourceProvider{
|
||||
implements UserEventDispatcher.LaunchSourceProvider, Insettable {
|
||||
|
||||
private CellLayout mContent;
|
||||
|
||||
@@ -44,6 +49,14 @@ public class Hotseat extends FrameLayout
|
||||
@ViewDebug.ExportedProperty(category = "launcher")
|
||||
private final boolean mHasVerticalHotseat;
|
||||
|
||||
@ViewDebug.ExportedProperty(category = "launcher")
|
||||
private Rect mInsets = new Rect();
|
||||
|
||||
@ViewDebug.ExportedProperty(category = "launcher")
|
||||
private int mBackgroundColor;
|
||||
@ViewDebug.ExportedProperty(category = "launcher")
|
||||
private ColorDrawable mBackground;
|
||||
|
||||
public Hotseat(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
@@ -56,6 +69,8 @@ public class Hotseat extends FrameLayout
|
||||
super(context, attrs, defStyle);
|
||||
mLauncher = (Launcher) context;
|
||||
mHasVerticalHotseat = mLauncher.getDeviceProfile().isVerticalBarLayout();
|
||||
mBackground = new ColorDrawable();
|
||||
setBackground(mBackground);
|
||||
}
|
||||
|
||||
public CellLayout getLayout() {
|
||||
@@ -166,4 +181,46 @@ public class Hotseat extends FrameLayout
|
||||
target.gridY = info.cellY;
|
||||
targetParent.containerType = LauncherLogProto.HOTSEAT;
|
||||
}
|
||||
|
||||
//Overridden so that the background color extends behind the navigation buttons.
|
||||
@Override
|
||||
public void setInsets(Rect insets) {
|
||||
int rightInset = insets.right - mInsets.right;
|
||||
int bottomInset = insets.bottom - mInsets.bottom;
|
||||
mInsets.set(insets);
|
||||
LayoutParams lp = (LayoutParams) getLayoutParams();
|
||||
if (mHasVerticalHotseat) {
|
||||
setPadding(getPaddingLeft(), getPaddingTop(),
|
||||
getPaddingRight() + rightInset, getPaddingBottom());
|
||||
if (lp.width != LayoutParams.MATCH_PARENT && lp.width != LayoutParams.WRAP_CONTENT) {
|
||||
lp.width += rightInset;
|
||||
}
|
||||
} else {
|
||||
setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(),
|
||||
getPaddingBottom() + bottomInset);
|
||||
if (lp.height != LayoutParams.MATCH_PARENT && lp.height != LayoutParams.WRAP_CONTENT) {
|
||||
lp.height += bottomInset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void updateColor(ExtractedColors extractedColors, boolean animate) {
|
||||
if (!mHasVerticalHotseat) {
|
||||
int color = extractedColors.getColor(ExtractedColors.HOTSEAT_INDEX, Color.TRANSPARENT);
|
||||
if (!animate) {
|
||||
setBackgroundColor(color);
|
||||
} else {
|
||||
ValueAnimator animator = ValueAnimator.ofInt(mBackgroundColor, color);
|
||||
animator.setEvaluator(new ArgbEvaluator());
|
||||
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
mBackground.setColor((Integer) animation.getAnimatedValue());
|
||||
}
|
||||
});
|
||||
animator.start();
|
||||
}
|
||||
mBackgroundColor = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -496,9 +496,10 @@ public class Launcher extends Activity
|
||||
}
|
||||
|
||||
private void loadExtractedColorsAndColorItems() {
|
||||
if (mExtractedColors != null) {
|
||||
// TODO: do this in pre-N as well, once the extraction part is complete.
|
||||
if (mExtractedColors != null && Utilities.isNycOrAbove()) {
|
||||
mExtractedColors.load(this);
|
||||
// TODO: pass mExtractedColors to interested items such as hotseat.
|
||||
mHotseat.updateColor(mExtractedColors, !mPaused);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,6 @@ import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.PaintDrawable;
|
||||
import android.os.Build;
|
||||
import android.os.Build.VERSION_CODES;
|
||||
import android.os.Bundle;
|
||||
import android.os.PowerManager;
|
||||
import android.text.Spannable;
|
||||
|
||||
@@ -32,6 +32,9 @@ import com.android.launcher3.LauncherSettings;
|
||||
*/
|
||||
public class ColorExtractionService extends IntentService {
|
||||
|
||||
/** The fraction of the wallpaper to extract colors for use on the hotseat. */
|
||||
private static final float HOTSEAT_FRACTION = 1f / 4;
|
||||
|
||||
public ColorExtractionService() {
|
||||
super("ColorExtractionService");
|
||||
}
|
||||
@@ -44,10 +47,21 @@ public class ColorExtractionService extends IntentService {
|
||||
if (wallpaperManager.getWallpaperInfo() != null) {
|
||||
// We can't extract colors from live wallpapers, so just use the default color always.
|
||||
extractedColors.updatePalette(null);
|
||||
extractedColors.updateHotseatPalette(null);
|
||||
} else {
|
||||
Bitmap wallpaper = ((BitmapDrawable) wallpaperManager.getDrawable()).getBitmap();
|
||||
Palette palette = Palette.from(wallpaper).generate();
|
||||
extractedColors.updatePalette(palette);
|
||||
// We extract colors for the hotseat separately,
|
||||
// since it only considers the lower part of the wallpaper.
|
||||
// TODO(twickham): update Palette library to 23.3.1 or higher,
|
||||
// which fixes a bug with using regions (b/28349435).
|
||||
Palette hotseatPalette = Palette.from(wallpaper)
|
||||
.setRegion(0, (int) (wallpaper.getHeight() * (1f - HOTSEAT_FRACTION)),
|
||||
wallpaper.getWidth(), wallpaper.getHeight())
|
||||
.clearFilters()
|
||||
.generate();
|
||||
extractedColors.updateHotseatPalette(hotseatPalette);
|
||||
}
|
||||
|
||||
// Save the extracted colors and wallpaper id to LauncherProvider.
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.launcher3.dynamicui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.support.v4.graphics.ColorUtils;
|
||||
import android.support.v7.graphics.Palette;
|
||||
import android.util.Log;
|
||||
|
||||
@@ -35,26 +36,30 @@ public class ExtractedColors {
|
||||
|
||||
// These color profile indices should NOT be changed, since they are used when saving and
|
||||
// loading extracted colors. New colors should always be added at the end.
|
||||
public static final int DEFAULT_INDEX = 0;
|
||||
public static final int VIBRANT_INDEX = 1;
|
||||
public static final int VIBRANT_DARK_INDEX = 2;
|
||||
public static final int VIBRANT_LIGHT_INDEX = 3;
|
||||
public static final int MUTED_INDEX = 4;
|
||||
public static final int MUTED_DARK_INDEX = 5;
|
||||
public static final int MUTED_LIGHT_INDEX = 6;
|
||||
public static final int VERSION_INDEX = 0;
|
||||
public static final int HOTSEAT_INDEX = 1;
|
||||
// public static final int VIBRANT_INDEX = 2;
|
||||
// public static final int VIBRANT_DARK_INDEX = 3;
|
||||
// public static final int VIBRANT_LIGHT_INDEX = 4;
|
||||
// public static final int MUTED_INDEX = 5;
|
||||
// public static final int MUTED_DARK_INDEX = 6;
|
||||
// public static final int MUTED_LIGHT_INDEX = 7;
|
||||
|
||||
public static final int NUM_COLOR_PROFILES = 7;
|
||||
public static final int NUM_COLOR_PROFILES = 1;
|
||||
private static final int VERSION = 1;
|
||||
|
||||
private static final String COLOR_SEPARATOR = ",";
|
||||
|
||||
private int[] mColors;
|
||||
|
||||
public ExtractedColors() {
|
||||
mColors = new int[NUM_COLOR_PROFILES];
|
||||
// The first entry is reserved for the version number.
|
||||
mColors = new int[NUM_COLOR_PROFILES + 1];
|
||||
mColors[VERSION_INDEX] = VERSION;
|
||||
}
|
||||
|
||||
public void setColorAtIndex(int index, int color) {
|
||||
if (index >= 0 && index < mColors.length) {
|
||||
if (index > VERSION_INDEX && index < mColors.length) {
|
||||
mColors[index] = color;
|
||||
} else {
|
||||
Log.e(TAG, "Attempted to set a color at an invalid index " + index);
|
||||
@@ -89,17 +94,21 @@ public class ExtractedColors {
|
||||
*/
|
||||
public void load(Context context) {
|
||||
String encodedString = Utilities.getPrefs(context).getString(
|
||||
ExtractionUtils.EXTRACTED_COLORS_PREFERENCE_KEY, DEFAULT_COLOR + "");
|
||||
ExtractionUtils.EXTRACTED_COLORS_PREFERENCE_KEY, VERSION + "");
|
||||
|
||||
decodeFromString(encodedString);
|
||||
|
||||
if (mColors[VERSION_INDEX] != VERSION) {
|
||||
ExtractionUtils.startColorExtractionService(context);
|
||||
}
|
||||
}
|
||||
|
||||
/** @param index must be one of the index values defined at the top of this class. */
|
||||
public int getColor(int index) {
|
||||
if (index >= 0 && index < mColors.length) {
|
||||
public int getColor(int index, int defaultColor) {
|
||||
if (index > VERSION_INDEX && index < mColors.length) {
|
||||
return mColors[index];
|
||||
}
|
||||
return DEFAULT_COLOR;
|
||||
return defaultColor;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,20 +121,39 @@ public class ExtractedColors {
|
||||
setColorAtIndex(i, ExtractedColors.DEFAULT_COLOR);
|
||||
}
|
||||
} else {
|
||||
setColorAtIndex(ExtractedColors.DEFAULT_INDEX,
|
||||
ExtractedColors.DEFAULT_COLOR);
|
||||
setColorAtIndex(ExtractedColors.VIBRANT_INDEX,
|
||||
palette.getVibrantColor(ExtractedColors.DEFAULT_COLOR));
|
||||
setColorAtIndex(ExtractedColors.VIBRANT_DARK_INDEX,
|
||||
palette.getDarkVibrantColor(ExtractedColors.DEFAULT_DARK));
|
||||
setColorAtIndex(ExtractedColors.VIBRANT_LIGHT_INDEX,
|
||||
palette.getLightVibrantColor(ExtractedColors.DEFAULT_LIGHT));
|
||||
setColorAtIndex(ExtractedColors.MUTED_INDEX,
|
||||
palette.getMutedColor(ExtractedColors.DEFAULT_COLOR));
|
||||
setColorAtIndex(ExtractedColors.MUTED_DARK_INDEX,
|
||||
palette.getDarkMutedColor(ExtractedColors.DEFAULT_DARK));
|
||||
setColorAtIndex(ExtractedColors.MUTED_LIGHT_INDEX,
|
||||
palette.getLightVibrantColor(ExtractedColors.DEFAULT_LIGHT));
|
||||
// We currently don't use any of the colors defined by the Palette API,
|
||||
// but this is how we would add them if we ever need them.
|
||||
|
||||
// setColorAtIndex(ExtractedColors.VIBRANT_INDEX,
|
||||
// palette.getVibrantColor(ExtractedColors.DEFAULT_COLOR));
|
||||
// setColorAtIndex(ExtractedColors.VIBRANT_DARK_INDEX,
|
||||
// palette.getDarkVibrantColor(ExtractedColors.DEFAULT_DARK));
|
||||
// setColorAtIndex(ExtractedColors.VIBRANT_LIGHT_INDEX,
|
||||
// palette.getLightVibrantColor(ExtractedColors.DEFAULT_LIGHT));
|
||||
// setColorAtIndex(ExtractedColors.MUTED_INDEX,
|
||||
// palette.getMutedColor(DEFAULT_COLOR));
|
||||
// setColorAtIndex(ExtractedColors.MUTED_DARK_INDEX,
|
||||
// palette.getDarkMutedColor(ExtractedColors.DEFAULT_DARK));
|
||||
// setColorAtIndex(ExtractedColors.MUTED_LIGHT_INDEX,
|
||||
// palette.getLightVibrantColor(ExtractedColors.DEFAULT_LIGHT));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The hotseat's color is defined as follows:
|
||||
* - 12% black for super light wallpaper
|
||||
* - 18% white for super dark
|
||||
* - 25% white otherwise
|
||||
*/
|
||||
public void updateHotseatPalette(Palette hotseatPalette) {
|
||||
int hotseatColor;
|
||||
if (hotseatPalette != null && ExtractionUtils.isSuperLight(hotseatPalette)) {
|
||||
hotseatColor = ColorUtils.setAlphaComponent(Color.BLACK, (int) (0.12f * 255));
|
||||
} else if (hotseatPalette != null && ExtractionUtils.isSuperDark(hotseatPalette)) {
|
||||
hotseatColor = ColorUtils.setAlphaComponent(Color.WHITE, (int) (0.18f * 255));
|
||||
} else {
|
||||
hotseatColor = ColorUtils.setAlphaComponent(Color.WHITE, (int) (0.25f * 255));
|
||||
}
|
||||
setColorAtIndex(HOTSEAT_INDEX, hotseatColor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,11 +20,15 @@ import android.app.WallpaperManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Color;
|
||||
import android.support.v4.graphics.ColorUtils;
|
||||
import android.support.v7.graphics.Palette;
|
||||
|
||||
import com.android.launcher3.Utilities;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Contains helper fields and methods related to extracting colors from the wallpaper.
|
||||
@@ -34,6 +38,7 @@ public class ExtractionUtils {
|
||||
public static final String WALLPAPER_ID_PREFERENCE_KEY = "pref_wallpaperId";
|
||||
|
||||
private static final int FLAG_SET_SYSTEM = 1 << 0; // TODO: use WallpaperManager.FLAG_SET_SYSTEM
|
||||
private static final float MIN_CONTRAST_RATIO = 2f;
|
||||
|
||||
/**
|
||||
* Extract colors in the :wallpaper-chooser process, if the wallpaper id has changed.
|
||||
@@ -46,12 +51,17 @@ public class ExtractionUtils {
|
||||
@Override
|
||||
public void run() {
|
||||
if (hasWallpaperIdChanged(context)) {
|
||||
context.startService(new Intent(context, ColorExtractionService.class));
|
||||
startColorExtractionService(context);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Starts the {@link ColorExtractionService} without checking the wallpaper id */
|
||||
public static void startColorExtractionService(Context context) {
|
||||
context.startService(new Intent(context, ColorExtractionService.class));
|
||||
}
|
||||
|
||||
private static boolean hasWallpaperIdChanged(Context context) {
|
||||
if (!Utilities.isNycOrAbove()) {
|
||||
// TODO: update an id in sharedprefs in onWallpaperChanged broadcast, and read it here.
|
||||
@@ -72,4 +82,36 @@ public class ExtractionUtils {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isSuperLight(Palette p) {
|
||||
return !isLegibleOnWallpaper(Color.WHITE, p.getSwatches());
|
||||
}
|
||||
|
||||
public static boolean isSuperDark(Palette p) {
|
||||
return !isLegibleOnWallpaper(Color.BLACK, p.getSwatches());
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a color, returns true if that color is legible on
|
||||
* the given wallpaper color swatches, else returns false.
|
||||
*/
|
||||
private static boolean isLegibleOnWallpaper(int color, List<Palette.Swatch> wallpaperSwatches) {
|
||||
int legiblePopulation = 0;
|
||||
int illegiblePopulation = 0;
|
||||
for (Palette.Swatch swatch : wallpaperSwatches) {
|
||||
if (isLegible(color, swatch.getRgb())) {
|
||||
legiblePopulation += swatch.getPopulation();
|
||||
} else {
|
||||
illegiblePopulation += swatch.getPopulation();
|
||||
}
|
||||
}
|
||||
return legiblePopulation > illegiblePopulation;
|
||||
}
|
||||
|
||||
/** @return Whether the foreground color is legible on the background color. */
|
||||
private static boolean isLegible(int foreground, int background) {
|
||||
background = ColorUtils.setAlphaComponent(background, 255);
|
||||
return ColorUtils.calculateContrast(foreground, background) >= MIN_CONTRAST_RATIO;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user