Dominant color is part of icon cache
> Calculating extracted color during icon generation and storing it in model and DB > Removing unused logic avoid various types of badge rendering > Icons are badged with extracted colors, while folder is badged with theme color Bug: 35428783 Change-Id: I93e30c52fbded7515c3ae1778422e84672eafb56
This commit is contained in:
parent
8c3c9d2634
commit
179249d804
|
@ -32,8 +32,6 @@
|
||||||
|
|
||||||
<!-- Popup container -->
|
<!-- Popup container -->
|
||||||
<color name="notification_icon_default_color">#757575</color> <!-- Gray 600 -->
|
<color name="notification_icon_default_color">#757575</color> <!-- Gray 600 -->
|
||||||
<color name="badge_color">#1DE9B6</color> <!-- Teal A400 -->
|
|
||||||
<color name="folder_badge_color">#1DE9B6</color> <!-- Teal A400 -->
|
|
||||||
|
|
||||||
<color name="icon_background">#E0E0E0</color> <!-- Gray 300 -->
|
<color name="icon_background">#E0E0E0</color> <!-- Gray 300 -->
|
||||||
<color name="legacy_icon_background">#FFFFFF</color>
|
<color name="legacy_icon_background">#FFFFFF</color>
|
||||||
|
|
|
@ -212,10 +212,6 @@
|
||||||
<!-- (touch_size - icon_size) / 2 -->
|
<!-- (touch_size - icon_size) / 2 -->
|
||||||
<dimen name="system_shortcut_header_icon_padding">12dp</dimen>
|
<dimen name="system_shortcut_header_icon_padding">12dp</dimen>
|
||||||
|
|
||||||
<!-- Icon badges (with notification counts) -->
|
|
||||||
<dimen name="badge_small_padding">0dp</dimen>
|
|
||||||
<dimen name="badge_large_padding">3dp</dimen>
|
|
||||||
|
|
||||||
<!-- Notifications -->
|
<!-- Notifications -->
|
||||||
<dimen name="bg_round_rect_radius">12dp</dimen>
|
<dimen name="bg_round_rect_radius">12dp</dimen>
|
||||||
<dimen name="notification_padding_start">16dp</dimen>
|
<dimen name="notification_padding_start">16dp</dimen>
|
||||||
|
|
|
@ -29,6 +29,7 @@ import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.os.Build.VERSION;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
@ -438,7 +439,7 @@ public class AutoInstallsLayout {
|
||||||
// Auto installs should always support the current platform version.
|
// Auto installs should always support the current platform version.
|
||||||
mValues.put(LauncherSettings.Favorites.ICON, Utilities.flattenBitmap(
|
mValues.put(LauncherSettings.Favorites.ICON, Utilities.flattenBitmap(
|
||||||
LauncherIcons.createBadgedIconBitmap(
|
LauncherIcons.createBadgedIconBitmap(
|
||||||
icon, Process.myUserHandle(), mContext, Build.VERSION.SDK_INT)));
|
icon, Process.myUserHandle(), mContext, VERSION.SDK_INT).icon));
|
||||||
mValues.put(Favorites.ICON_PACKAGE, mIconRes.getResourcePackageName(iconId));
|
mValues.put(Favorites.ICON_PACKAGE, mIconRes.getResourcePackageName(iconId));
|
||||||
mValues.put(Favorites.ICON_RESOURCE, mIconRes.getResourceName(iconId));
|
mValues.put(Favorites.ICON_RESOURCE, mIconRes.getResourceName(iconId));
|
||||||
|
|
||||||
|
|
|
@ -88,7 +88,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||||
|
|
||||||
private BadgeInfo mBadgeInfo;
|
private BadgeInfo mBadgeInfo;
|
||||||
private BadgeRenderer mBadgeRenderer;
|
private BadgeRenderer mBadgeRenderer;
|
||||||
private IconPalette mBadgePalette;
|
private int mBadgeColor;
|
||||||
private float mBadgeScale;
|
private float mBadgeScale;
|
||||||
private boolean mForceHideBadge;
|
private boolean mForceHideBadge;
|
||||||
private Point mTempSpaceForBadgeOffset = new Point();
|
private Point mTempSpaceForBadgeOffset = new Point();
|
||||||
|
@ -183,7 +183,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||||
*/
|
*/
|
||||||
public void reset() {
|
public void reset() {
|
||||||
mBadgeInfo = null;
|
mBadgeInfo = null;
|
||||||
mBadgePalette = null;
|
mBadgeColor = Color.TRANSPARENT;
|
||||||
mBadgeScale = 0f;
|
mBadgeScale = 0f;
|
||||||
mForceHideBadge = false;
|
mForceHideBadge = false;
|
||||||
}
|
}
|
||||||
|
@ -193,7 +193,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||||
}
|
}
|
||||||
|
|
||||||
public void applyFromShortcutInfo(ShortcutInfo info, boolean promiseStateChanged) {
|
public void applyFromShortcutInfo(ShortcutInfo info, boolean promiseStateChanged) {
|
||||||
applyIconAndLabel(info.iconBitmap, info);
|
applyIconAndLabel(info);
|
||||||
setTag(info);
|
setTag(info);
|
||||||
if (promiseStateChanged || (info.hasPromiseIconUi())) {
|
if (promiseStateChanged || (info.hasPromiseIconUi())) {
|
||||||
applyPromiseState(promiseStateChanged);
|
applyPromiseState(promiseStateChanged);
|
||||||
|
@ -203,7 +203,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||||
}
|
}
|
||||||
|
|
||||||
public void applyFromApplicationInfo(AppInfo info) {
|
public void applyFromApplicationInfo(AppInfo info) {
|
||||||
applyIconAndLabel(info.iconBitmap, info);
|
applyIconAndLabel(info);
|
||||||
|
|
||||||
// We don't need to check the info since it's not a ShortcutInfo
|
// We don't need to check the info since it's not a ShortcutInfo
|
||||||
super.setTag(info);
|
super.setTag(info);
|
||||||
|
@ -219,7 +219,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||||
}
|
}
|
||||||
|
|
||||||
public void applyFromPackageItemInfo(PackageItemInfo info) {
|
public void applyFromPackageItemInfo(PackageItemInfo info) {
|
||||||
applyIconAndLabel(info.iconBitmap, info);
|
applyIconAndLabel(info);
|
||||||
// We don't need to check the info since it's not a ShortcutInfo
|
// We don't need to check the info since it's not a ShortcutInfo
|
||||||
super.setTag(info);
|
super.setTag(info);
|
||||||
|
|
||||||
|
@ -227,8 +227,10 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||||
verifyHighRes();
|
verifyHighRes();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyIconAndLabel(Bitmap icon, ItemInfo info) {
|
private void applyIconAndLabel(ItemInfoWithIcon info) {
|
||||||
FastBitmapDrawable iconDrawable = DrawableFactory.get(getContext()).newIcon(icon, info);
|
FastBitmapDrawable iconDrawable = DrawableFactory.get(getContext()).newIcon(info);
|
||||||
|
mBadgeColor = IconPalette.getMutedColor(info.iconColor, 0.54f);
|
||||||
|
|
||||||
iconDrawable.setIsDisabled(info.isDisabled());
|
iconDrawable.setIsDisabled(info.isDisabled());
|
||||||
setIcon(iconDrawable);
|
setIcon(iconDrawable);
|
||||||
setText(info.title);
|
setText(info.title);
|
||||||
|
@ -401,7 +403,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||||
final int scrollX = getScrollX();
|
final int scrollX = getScrollX();
|
||||||
final int scrollY = getScrollY();
|
final int scrollY = getScrollY();
|
||||||
canvas.translate(scrollX, scrollY);
|
canvas.translate(scrollX, scrollY);
|
||||||
mBadgeRenderer.draw(canvas, mBadgePalette, mBadgeInfo, mTempIconBounds, mBadgeScale,
|
mBadgeRenderer.draw(canvas, mBadgeColor, mTempIconBounds, mBadgeScale,
|
||||||
mTempSpaceForBadgeOffset);
|
mTempSpaceForBadgeOffset);
|
||||||
canvas.translate(-scrollX, -scrollY);
|
canvas.translate(-scrollX, -scrollY);
|
||||||
}
|
}
|
||||||
|
@ -532,7 +534,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||||
preloadDrawable.setLevel(progressLevel);
|
preloadDrawable.setLevel(progressLevel);
|
||||||
} else {
|
} else {
|
||||||
preloadDrawable = DrawableFactory.get(getContext())
|
preloadDrawable = DrawableFactory.get(getContext())
|
||||||
.newPendingIcon(info.iconBitmap, getContext());
|
.newPendingIcon(info, getContext());
|
||||||
preloadDrawable.setLevel(progressLevel);
|
preloadDrawable.setLevel(progressLevel);
|
||||||
setIcon(preloadDrawable);
|
setIcon(preloadDrawable);
|
||||||
}
|
}
|
||||||
|
@ -550,10 +552,6 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||||
float newBadgeScale = isBadged ? 1f : 0;
|
float newBadgeScale = isBadged ? 1f : 0;
|
||||||
mBadgeRenderer = mLauncher.getDeviceProfile().mBadgeRenderer;
|
mBadgeRenderer = mLauncher.getDeviceProfile().mBadgeRenderer;
|
||||||
if (wasBadged || isBadged) {
|
if (wasBadged || isBadged) {
|
||||||
mBadgePalette = IconPalette.getBadgePalette(getResources());
|
|
||||||
if (mBadgePalette == null) {
|
|
||||||
mBadgePalette = ((FastBitmapDrawable) mIcon).getIconPalette();
|
|
||||||
}
|
|
||||||
// Animate when a badge is first added or when it is removed.
|
// Animate when a badge is first added or when it is removed.
|
||||||
if (animate && (wasBadged ^ isBadged) && isShown()) {
|
if (animate && (wasBadged ^ isBadged) && isShown()) {
|
||||||
ObjectAnimator.ofFloat(this, BADGE_SCALE_PROPERTY, newBadgeScale).start();
|
ObjectAnimator.ofFloat(this, BADGE_SCALE_PROPERTY, newBadgeScale).start();
|
||||||
|
@ -565,10 +563,6 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IconPalette getBadgePalette() {
|
|
||||||
return mBadgePalette;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the icon for this view based on the layout direction.
|
* Sets the icon for this view based on the layout direction.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -248,7 +248,7 @@ public class DeviceProfile {
|
||||||
computeAllAppsButtonSize(context);
|
computeAllAppsButtonSize(context);
|
||||||
|
|
||||||
// This is done last, after iconSizePx is calculated above.
|
// This is done last, after iconSizePx is calculated above.
|
||||||
mBadgeRenderer = new BadgeRenderer(context, iconSizePx);
|
mBadgeRenderer = new BadgeRenderer(iconSizePx);
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceProfile getMultiWindowProfile(Context context, Point mwSize) {
|
DeviceProfile getMultiWindowProfile(Context context, Point mwSize) {
|
||||||
|
|
|
@ -32,7 +32,7 @@ import android.graphics.drawable.Drawable;
|
||||||
import android.util.Property;
|
import android.util.Property;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
|
|
||||||
import com.android.launcher3.graphics.IconPalette;
|
import com.android.launcher3.graphics.BitmapInfo;
|
||||||
|
|
||||||
public class FastBitmapDrawable extends Drawable {
|
public class FastBitmapDrawable extends Drawable {
|
||||||
|
|
||||||
|
@ -40,19 +40,9 @@ public class FastBitmapDrawable extends Drawable {
|
||||||
private static final float DISABLED_DESATURATION = 1f;
|
private static final float DISABLED_DESATURATION = 1f;
|
||||||
private static final float DISABLED_BRIGHTNESS = 0.5f;
|
private static final float DISABLED_BRIGHTNESS = 0.5f;
|
||||||
|
|
||||||
public static final TimeInterpolator CLICK_FEEDBACK_INTERPOLATOR = new TimeInterpolator() {
|
public static final TimeInterpolator CLICK_FEEDBACK_INTERPOLATOR = (input) ->
|
||||||
|
(input < 0.05f) ? (input / 0.05f) : ((input < 0.3f) ? 1 : (1 - input) / 0.7f);
|
||||||
|
|
||||||
@Override
|
|
||||||
public float getInterpolation(float input) {
|
|
||||||
if (input < 0.05f) {
|
|
||||||
return input / 0.05f;
|
|
||||||
} else if (input < 0.3f){
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
return (1 - input) / 0.7f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public static final int CLICK_FEEDBACK_DURATION = 2000;
|
public static final int CLICK_FEEDBACK_DURATION = 2000;
|
||||||
|
|
||||||
// Since we don't need 256^2 values for combinations of both the brightness and saturation, we
|
// Since we don't need 256^2 values for combinations of both the brightness and saturation, we
|
||||||
|
@ -69,12 +59,11 @@ public class FastBitmapDrawable extends Drawable {
|
||||||
|
|
||||||
protected final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG);
|
protected final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG);
|
||||||
private final Bitmap mBitmap;
|
private final Bitmap mBitmap;
|
||||||
|
protected final int mIconColor;
|
||||||
|
|
||||||
private boolean mIsPressed;
|
private boolean mIsPressed;
|
||||||
private boolean mIsDisabled;
|
private boolean mIsDisabled;
|
||||||
|
|
||||||
private IconPalette mIconPalette;
|
|
||||||
|
|
||||||
private static final Property<FastBitmapDrawable, Float> BRIGHTNESS
|
private static final Property<FastBitmapDrawable, Float> BRIGHTNESS
|
||||||
= new Property<FastBitmapDrawable, Float>(Float.TYPE, "brightness") {
|
= new Property<FastBitmapDrawable, Float>(Float.TYPE, "brightness") {
|
||||||
@Override
|
@Override
|
||||||
|
@ -99,7 +88,20 @@ public class FastBitmapDrawable extends Drawable {
|
||||||
private ObjectAnimator mBrightnessAnimator;
|
private ObjectAnimator mBrightnessAnimator;
|
||||||
|
|
||||||
public FastBitmapDrawable(Bitmap b) {
|
public FastBitmapDrawable(Bitmap b) {
|
||||||
|
this(b, Color.TRANSPARENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FastBitmapDrawable(BitmapInfo info) {
|
||||||
|
this(info.icon, info.color);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FastBitmapDrawable(ItemInfoWithIcon info) {
|
||||||
|
this(info.iconBitmap, info.iconColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected FastBitmapDrawable(Bitmap b, int iconColor) {
|
||||||
mBitmap = b;
|
mBitmap = b;
|
||||||
|
mIconColor = iconColor;
|
||||||
setFilterBitmap(true);
|
setFilterBitmap(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,14 +110,6 @@ public class FastBitmapDrawable extends Drawable {
|
||||||
canvas.drawBitmap(mBitmap, null, getBounds(), mPaint);
|
canvas.drawBitmap(mBitmap, null, getBounds(), mPaint);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IconPalette getIconPalette() {
|
|
||||||
if (mIconPalette == null) {
|
|
||||||
mIconPalette = IconPalette.fromDominantColor(Utilities
|
|
||||||
.findDominantColorByHue(mBitmap, 20), true /* desaturateBackground */);
|
|
||||||
}
|
|
||||||
return mIconPalette;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setColorFilter(ColorFilter cf) {
|
public void setColorFilter(ColorFilter cf) {
|
||||||
// No op
|
// No op
|
||||||
|
|
|
@ -33,18 +33,21 @@ import android.database.sqlite.SQLiteException;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.Build;
|
import android.os.Build.VERSION;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.v4.graphics.ColorUtils;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.android.launcher3.compat.LauncherAppsCompat;
|
import com.android.launcher3.compat.LauncherAppsCompat;
|
||||||
import com.android.launcher3.compat.UserManagerCompat;
|
import com.android.launcher3.compat.UserManagerCompat;
|
||||||
import com.android.launcher3.config.FeatureFlags;
|
import com.android.launcher3.config.FeatureFlags;
|
||||||
|
import com.android.launcher3.graphics.BitmapInfo;
|
||||||
|
import com.android.launcher3.graphics.ColorExtractor;
|
||||||
import com.android.launcher3.graphics.LauncherIcons;
|
import com.android.launcher3.graphics.LauncherIcons;
|
||||||
import com.android.launcher3.model.PackageItemInfo;
|
import com.android.launcher3.model.PackageItemInfo;
|
||||||
import com.android.launcher3.uioverrides.UiFactory;
|
import com.android.launcher3.uioverrides.UiFactory;
|
||||||
|
@ -81,14 +84,13 @@ public class IconCache {
|
||||||
|
|
||||||
@Thunk static final Object ICON_UPDATE_TOKEN = new Object();
|
@Thunk static final Object ICON_UPDATE_TOKEN = new Object();
|
||||||
|
|
||||||
public static class CacheEntry {
|
public static class CacheEntry extends BitmapInfo {
|
||||||
public Bitmap icon;
|
|
||||||
public CharSequence title = "";
|
public CharSequence title = "";
|
||||||
public CharSequence contentDescription = "";
|
public CharSequence contentDescription = "";
|
||||||
public boolean isLowResIcon;
|
public boolean isLowResIcon;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final HashMap<UserHandle, Bitmap> mDefaultIcons = new HashMap<>();
|
private final HashMap<UserHandle, BitmapInfo> mDefaultIcons = new HashMap<>();
|
||||||
@Thunk final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor();
|
@Thunk final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor();
|
||||||
|
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
|
@ -190,9 +192,9 @@ public class IconCache {
|
||||||
return mIconProvider.getIcon(info, mIconDpi, flattenDrawable);
|
return mIconProvider.getIcon(info, mIconDpi, flattenDrawable);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Bitmap makeDefaultIcon(UserHandle user) {
|
protected BitmapInfo makeDefaultIcon(UserHandle user) {
|
||||||
Drawable unbadged = getFullResDefaultActivityIcon();
|
Drawable unbadged = getFullResDefaultActivityIcon();
|
||||||
return LauncherIcons.createBadgedIconBitmap(unbadged, user, mContext, Build.VERSION_CODES.O);
|
return LauncherIcons.createBadgedIconBitmap(unbadged, user, mContext, VERSION.SDK_INT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -376,16 +378,16 @@ public class IconCache {
|
||||||
}
|
}
|
||||||
if (entry == null) {
|
if (entry == null) {
|
||||||
entry = new CacheEntry();
|
entry = new CacheEntry();
|
||||||
entry.icon = LauncherIcons.createBadgedIconBitmap(getFullResIcon(app), app.getUser(),
|
LauncherIcons.createBadgedIconBitmap(getFullResIcon(app), app.getUser(),
|
||||||
mContext, app.getApplicationInfo().targetSdkVersion);
|
mContext, app.getApplicationInfo().targetSdkVersion).applyTo(entry);
|
||||||
}
|
}
|
||||||
entry.title = app.getLabel();
|
entry.title = app.getLabel();
|
||||||
entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, app.getUser());
|
entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, app.getUser());
|
||||||
mCache.put(key, entry);
|
mCache.put(key, entry);
|
||||||
|
|
||||||
Bitmap lowResIcon = generateLowResIcon(entry.icon);
|
Bitmap lowResIcon = generateLowResIcon(entry.icon);
|
||||||
ContentValues values = newContentValues(entry.icon, lowResIcon, entry.title.toString(),
|
ContentValues values = newContentValues(entry.icon, lowResIcon, entry.color,
|
||||||
app.getApplicationInfo().packageName);
|
entry.title.toString(), app.getApplicationInfo().packageName);
|
||||||
addIconToDB(values, app.getComponentName(), info, userSerial);
|
addIconToDB(values, app.getComponentName(), info, userSerial);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -459,7 +461,7 @@ public class IconCache {
|
||||||
// null info means not installed, but if we have a component from the intent then
|
// null info means not installed, but if we have a component from the intent then
|
||||||
// we should still look in the cache for restored app icons.
|
// we should still look in the cache for restored app icons.
|
||||||
if (info.getTargetComponent() == null) {
|
if (info.getTargetComponent() == null) {
|
||||||
info.iconBitmap = getDefaultIcon(info.user);
|
getDefaultIcon(info.user).applyTo(info);
|
||||||
info.title = "";
|
info.title = "";
|
||||||
info.contentDescription = "";
|
info.contentDescription = "";
|
||||||
info.usingLowResIcon = false;
|
info.usingLowResIcon = false;
|
||||||
|
@ -494,11 +496,11 @@ public class IconCache {
|
||||||
private void applyCacheEntry(CacheEntry entry, ItemInfoWithIcon info) {
|
private void applyCacheEntry(CacheEntry entry, ItemInfoWithIcon info) {
|
||||||
info.title = Utilities.trim(entry.title);
|
info.title = Utilities.trim(entry.title);
|
||||||
info.contentDescription = entry.contentDescription;
|
info.contentDescription = entry.contentDescription;
|
||||||
info.iconBitmap = entry.icon == null ? getDefaultIcon(info.user) : entry.icon;
|
|
||||||
info.usingLowResIcon = entry.isLowResIcon;
|
info.usingLowResIcon = entry.isLowResIcon;
|
||||||
|
((entry.icon == null) ? getDefaultIcon(info.user) : entry).applyTo(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized Bitmap getDefaultIcon(UserHandle user) {
|
public synchronized BitmapInfo getDefaultIcon(UserHandle user) {
|
||||||
if (!mDefaultIcons.containsKey(user)) {
|
if (!mDefaultIcons.containsKey(user)) {
|
||||||
mDefaultIcons.put(user, makeDefaultIcon(user));
|
mDefaultIcons.put(user, makeDefaultIcon(user));
|
||||||
}
|
}
|
||||||
|
@ -506,7 +508,7 @@ public class IconCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isDefaultIcon(Bitmap icon, UserHandle user) {
|
public boolean isDefaultIcon(Bitmap icon, UserHandle user) {
|
||||||
return mDefaultIcons.get(user) == icon;
|
return getDefaultIcon(user).icon == icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -533,9 +535,9 @@ public class IconCache {
|
||||||
providerFetchedOnce = true;
|
providerFetchedOnce = true;
|
||||||
|
|
||||||
if (info != null) {
|
if (info != null) {
|
||||||
entry.icon = LauncherIcons.createBadgedIconBitmap(
|
LauncherIcons.createBadgedIconBitmap(
|
||||||
getFullResIcon(info), info.getUser(), mContext,
|
getFullResIcon(info), info.getUser(), mContext,
|
||||||
infoProvider.get().getApplicationInfo().targetSdkVersion);
|
info.getApplicationInfo().targetSdkVersion).applyTo(entry);
|
||||||
} else {
|
} else {
|
||||||
if (usePackageIcon) {
|
if (usePackageIcon) {
|
||||||
CacheEntry packageEntry = getEntryForPackageLocked(
|
CacheEntry packageEntry = getEntryForPackageLocked(
|
||||||
|
@ -543,7 +545,7 @@ public class IconCache {
|
||||||
if (packageEntry != null) {
|
if (packageEntry != null) {
|
||||||
if (DEBUG) Log.d(TAG, "using package default icon for " +
|
if (DEBUG) Log.d(TAG, "using package default icon for " +
|
||||||
componentName.toShortString());
|
componentName.toShortString());
|
||||||
entry.icon = packageEntry.icon;
|
packageEntry.applyTo(entry);
|
||||||
entry.title = packageEntry.title;
|
entry.title = packageEntry.title;
|
||||||
entry.contentDescription = packageEntry.contentDescription;
|
entry.contentDescription = packageEntry.contentDescription;
|
||||||
}
|
}
|
||||||
|
@ -551,7 +553,7 @@ public class IconCache {
|
||||||
if (entry.icon == null) {
|
if (entry.icon == null) {
|
||||||
if (DEBUG) Log.d(TAG, "using default icon for " +
|
if (DEBUG) Log.d(TAG, "using default icon for " +
|
||||||
componentName.toShortString());
|
componentName.toShortString());
|
||||||
entry.icon = getDefaultIcon(user);
|
getDefaultIcon(user).applyTo(entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -594,7 +596,7 @@ public class IconCache {
|
||||||
entry.title = title;
|
entry.title = title;
|
||||||
}
|
}
|
||||||
if (icon != null) {
|
if (icon != null) {
|
||||||
entry.icon = LauncherIcons.createIconBitmap(icon, mContext);
|
LauncherIcons.createIconBitmap(icon, mContext).applyTo(entry);
|
||||||
}
|
}
|
||||||
if (!TextUtils.isEmpty(title) && entry.icon != null) {
|
if (!TextUtils.isEmpty(title) && entry.icon != null) {
|
||||||
mCache.put(cacheKey, entry);
|
mCache.put(cacheKey, entry);
|
||||||
|
@ -633,22 +635,23 @@ public class IconCache {
|
||||||
|
|
||||||
// Load the full res icon for the application, but if useLowResIcon is set, then
|
// Load the full res icon for the application, but if useLowResIcon is set, then
|
||||||
// only keep the low resolution icon instead of the larger full-sized icon
|
// only keep the low resolution icon instead of the larger full-sized icon
|
||||||
Bitmap icon = LauncherIcons.createBadgedIconBitmap(
|
BitmapInfo iconInfo = LauncherIcons.createBadgedIconBitmap(
|
||||||
appInfo.loadIcon(mPackageManager), user, mContext, appInfo.targetSdkVersion);
|
appInfo.loadIcon(mPackageManager), user, mContext, appInfo.targetSdkVersion);
|
||||||
if (mInstantAppResolver.isInstantApp(appInfo)) {
|
if (mInstantAppResolver.isInstantApp(appInfo)) {
|
||||||
LauncherIcons.badgeWithDrawable(icon,
|
LauncherIcons.badgeWithDrawable(iconInfo.icon,
|
||||||
mContext.getDrawable(R.drawable.ic_instant_app_badge), mContext);
|
mContext.getDrawable(R.drawable.ic_instant_app_badge), mContext);
|
||||||
}
|
}
|
||||||
Bitmap lowResIcon = generateLowResIcon(icon);
|
Bitmap lowResIcon = generateLowResIcon(iconInfo.icon);
|
||||||
entry.title = appInfo.loadLabel(mPackageManager);
|
entry.title = appInfo.loadLabel(mPackageManager);
|
||||||
entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user);
|
entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user);
|
||||||
entry.icon = useLowResIcon ? lowResIcon : icon;
|
entry.icon = useLowResIcon ? lowResIcon : iconInfo.icon;
|
||||||
|
entry.color = iconInfo.color;
|
||||||
entry.isLowResIcon = useLowResIcon;
|
entry.isLowResIcon = useLowResIcon;
|
||||||
|
|
||||||
// Add the icon in the DB here, since these do not get written during
|
// Add the icon in the DB here, since these do not get written during
|
||||||
// package updates.
|
// package updates.
|
||||||
ContentValues values =
|
ContentValues values = newContentValues(iconInfo.icon, lowResIcon, entry.color,
|
||||||
newContentValues(icon, lowResIcon, entry.title.toString(), packageName);
|
entry.title.toString(), packageName);
|
||||||
addIconToDB(values, cacheKey.componentName, info,
|
addIconToDB(values, cacheKey.componentName, info,
|
||||||
mUserManager.getSerialNumberForUser(user));
|
mUserManager.getSerialNumberForUser(user));
|
||||||
|
|
||||||
|
@ -671,14 +674,16 @@ public class IconCache {
|
||||||
try {
|
try {
|
||||||
c = mIconDb.query(
|
c = mIconDb.query(
|
||||||
new String[]{lowRes ? IconDB.COLUMN_ICON_LOW_RES : IconDB.COLUMN_ICON,
|
new String[]{lowRes ? IconDB.COLUMN_ICON_LOW_RES : IconDB.COLUMN_ICON,
|
||||||
IconDB.COLUMN_LABEL},
|
IconDB.COLUMN_ICON_COLOR, IconDB.COLUMN_LABEL},
|
||||||
IconDB.COLUMN_COMPONENT + " = ? AND " + IconDB.COLUMN_USER + " = ?",
|
IconDB.COLUMN_COMPONENT + " = ? AND " + IconDB.COLUMN_USER + " = ?",
|
||||||
new String[]{cacheKey.componentName.flattenToString(),
|
new String[]{cacheKey.componentName.flattenToString(),
|
||||||
Long.toString(mUserManager.getSerialNumberForUser(cacheKey.user))});
|
Long.toString(mUserManager.getSerialNumberForUser(cacheKey.user))});
|
||||||
if (c.moveToNext()) {
|
if (c.moveToNext()) {
|
||||||
entry.icon = loadIconNoResize(c, 0, lowRes ? mLowResOptions : mHighResOptions);
|
entry.icon = loadIconNoResize(c, 0, lowRes ? mLowResOptions : mHighResOptions);
|
||||||
|
// Set the alpha to be 255, so that we never have a wrong color
|
||||||
|
entry.color = ColorUtils.setAlphaComponent(c.getInt(1), 255);
|
||||||
entry.isLowResIcon = lowRes;
|
entry.isLowResIcon = lowRes;
|
||||||
entry.title = c.getString(1);
|
entry.title = c.getString(2);
|
||||||
if (entry.title == null) {
|
if (entry.title == null) {
|
||||||
entry.title = "";
|
entry.title = "";
|
||||||
entry.contentDescription = "";
|
entry.contentDescription = "";
|
||||||
|
@ -771,7 +776,7 @@ public class IconCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class IconDB extends SQLiteCacheHelper {
|
private static final class IconDB extends SQLiteCacheHelper {
|
||||||
private final static int DB_VERSION = 17;
|
private final static int DB_VERSION = 18;
|
||||||
|
|
||||||
private final static int RELEASE_VERSION = DB_VERSION +
|
private final static int RELEASE_VERSION = DB_VERSION +
|
||||||
(FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION ? 0 : 1);
|
(FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION ? 0 : 1);
|
||||||
|
@ -784,6 +789,7 @@ public class IconCache {
|
||||||
private final static String COLUMN_VERSION = "version";
|
private final static String COLUMN_VERSION = "version";
|
||||||
private final static String COLUMN_ICON = "icon";
|
private final static String COLUMN_ICON = "icon";
|
||||||
private final static String COLUMN_ICON_LOW_RES = "icon_low_res";
|
private final static String COLUMN_ICON_LOW_RES = "icon_low_res";
|
||||||
|
private final static String COLUMN_ICON_COLOR = "icon_color";
|
||||||
private final static String COLUMN_LABEL = "label";
|
private final static String COLUMN_LABEL = "label";
|
||||||
private final static String COLUMN_SYSTEM_STATE = "system_state";
|
private final static String COLUMN_SYSTEM_STATE = "system_state";
|
||||||
|
|
||||||
|
@ -802,6 +808,7 @@ public class IconCache {
|
||||||
COLUMN_VERSION + " INTEGER NOT NULL DEFAULT 0, " +
|
COLUMN_VERSION + " INTEGER NOT NULL DEFAULT 0, " +
|
||||||
COLUMN_ICON + " BLOB, " +
|
COLUMN_ICON + " BLOB, " +
|
||||||
COLUMN_ICON_LOW_RES + " BLOB, " +
|
COLUMN_ICON_LOW_RES + " BLOB, " +
|
||||||
|
COLUMN_ICON_COLOR + " INTEGER NOT NULL DEFAULT 0, " +
|
||||||
COLUMN_LABEL + " TEXT, " +
|
COLUMN_LABEL + " TEXT, " +
|
||||||
COLUMN_SYSTEM_STATE + " TEXT, " +
|
COLUMN_SYSTEM_STATE + " TEXT, " +
|
||||||
"PRIMARY KEY (" + COLUMN_COMPONENT + ", " + COLUMN_USER + ") " +
|
"PRIMARY KEY (" + COLUMN_COMPONENT + ", " + COLUMN_USER + ") " +
|
||||||
|
@ -809,11 +816,12 @@ public class IconCache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ContentValues newContentValues(Bitmap icon, Bitmap lowResIcon, String label,
|
private ContentValues newContentValues(Bitmap icon, Bitmap lowResIcon, int iconColor,
|
||||||
String packageName) {
|
String label, String packageName) {
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
values.put(IconDB.COLUMN_ICON, Utilities.flattenBitmap(icon));
|
values.put(IconDB.COLUMN_ICON, Utilities.flattenBitmap(icon));
|
||||||
values.put(IconDB.COLUMN_ICON_LOW_RES, Utilities.flattenBitmap(lowResIcon));
|
values.put(IconDB.COLUMN_ICON_LOW_RES, Utilities.flattenBitmap(lowResIcon));
|
||||||
|
values.put(IconDB.COLUMN_ICON_COLOR, iconColor);
|
||||||
|
|
||||||
values.put(IconDB.COLUMN_LABEL, label);
|
values.put(IconDB.COLUMN_LABEL, label);
|
||||||
values.put(IconDB.COLUMN_SYSTEM_STATE, mIconProvider.getIconSystemState(packageName));
|
values.put(IconDB.COLUMN_SYSTEM_STATE, mIconProvider.getIconSystemState(packageName));
|
||||||
|
|
|
@ -40,6 +40,7 @@ import android.util.Pair;
|
||||||
|
|
||||||
import com.android.launcher3.compat.LauncherAppsCompat;
|
import com.android.launcher3.compat.LauncherAppsCompat;
|
||||||
import com.android.launcher3.compat.UserManagerCompat;
|
import com.android.launcher3.compat.UserManagerCompat;
|
||||||
|
import com.android.launcher3.graphics.BitmapInfo;
|
||||||
import com.android.launcher3.graphics.LauncherIcons;
|
import com.android.launcher3.graphics.LauncherIcons;
|
||||||
import com.android.launcher3.shortcuts.DeepShortcutManager;
|
import com.android.launcher3.shortcuts.DeepShortcutManager;
|
||||||
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
|
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
|
||||||
|
@ -480,7 +481,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
|
||||||
final LauncherAppState app = LauncherAppState.getInstance(mContext);
|
final LauncherAppState app = LauncherAppState.getInstance(mContext);
|
||||||
// Set default values until proper values is loaded.
|
// Set default values until proper values is loaded.
|
||||||
appInfo.title = "";
|
appInfo.title = "";
|
||||||
appInfo.iconBitmap = app.getIconCache().getDefaultIcon(user);
|
app.getIconCache().getDefaultIcon(user).applyTo(appInfo);
|
||||||
final ShortcutInfo si = appInfo.makeShortcut();
|
final ShortcutInfo si = appInfo.makeShortcut();
|
||||||
if (Looper.myLooper() == LauncherModel.getWorkerLooper()) {
|
if (Looper.myLooper() == LauncherModel.getWorkerLooper()) {
|
||||||
app.getIconCache().getTitleAndIcon(si, activityInfo, false /* useLowResIcon */);
|
app.getIconCache().getTitleAndIcon(si, activityInfo, false /* useLowResIcon */);
|
||||||
|
@ -497,7 +498,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
|
||||||
return Pair.create((ItemInfo) si, (Object) activityInfo);
|
return Pair.create((ItemInfo) si, (Object) activityInfo);
|
||||||
} else if (shortcutInfo != null) {
|
} else if (shortcutInfo != null) {
|
||||||
ShortcutInfo si = new ShortcutInfo(shortcutInfo, mContext);
|
ShortcutInfo si = new ShortcutInfo(shortcutInfo, mContext);
|
||||||
si.iconBitmap = LauncherIcons.createShortcutIcon(shortcutInfo, mContext);
|
LauncherIcons.createShortcutIcon(shortcutInfo, mContext).applyTo(si);
|
||||||
return Pair.create((ItemInfo) si, (Object) shortcutInfo);
|
return Pair.create((ItemInfo) si, (Object) shortcutInfo);
|
||||||
} else if (providerInfo != null) {
|
} else if (providerInfo != null) {
|
||||||
LauncherAppWidgetProviderInfo info = LauncherAppWidgetProviderInfo
|
LauncherAppWidgetProviderInfo info = LauncherAppWidgetProviderInfo
|
||||||
|
@ -641,18 +642,20 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
|
||||||
// users wouldn't get here without intent forwarding anyway.
|
// users wouldn't get here without intent forwarding anyway.
|
||||||
info.user = Process.myUserHandle();
|
info.user = Process.myUserHandle();
|
||||||
|
|
||||||
|
BitmapInfo iconInfo = null;
|
||||||
if (bitmap instanceof Bitmap) {
|
if (bitmap instanceof Bitmap) {
|
||||||
info.iconBitmap = LauncherIcons.createIconBitmap((Bitmap) bitmap, app.getContext());
|
iconInfo = LauncherIcons.createIconBitmap((Bitmap) bitmap, app.getContext());
|
||||||
} else {
|
} else {
|
||||||
Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
|
Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
|
||||||
if (extra instanceof Intent.ShortcutIconResource) {
|
if (extra instanceof Intent.ShortcutIconResource) {
|
||||||
info.iconResource = (Intent.ShortcutIconResource) extra;
|
info.iconResource = (Intent.ShortcutIconResource) extra;
|
||||||
info.iconBitmap = LauncherIcons.createIconBitmap(info.iconResource, app.getContext());
|
iconInfo = LauncherIcons.createIconBitmap(info.iconResource, app.getContext());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (info.iconBitmap == null) {
|
if (iconInfo == null) {
|
||||||
info.iconBitmap = app.getIconCache().getDefaultIcon(info.user);
|
iconInfo = app.getIconCache().getDefaultIcon(info.user);
|
||||||
}
|
}
|
||||||
|
iconInfo.applyTo(info);
|
||||||
|
|
||||||
info.title = Utilities.trim(name);
|
info.title = Utilities.trim(name);
|
||||||
info.contentDescription = UserManagerCompat.getInstance(app.getContext())
|
info.contentDescription = UserManagerCompat.getInstance(app.getContext())
|
||||||
|
|
|
@ -28,6 +28,11 @@ public abstract class ItemInfoWithIcon extends ItemInfo {
|
||||||
*/
|
*/
|
||||||
public Bitmap iconBitmap;
|
public Bitmap iconBitmap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dominant color in the {@link #iconBitmap}.
|
||||||
|
*/
|
||||||
|
public int iconColor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates whether we're using a low res icon
|
* Indicates whether we're using a low res icon
|
||||||
*/
|
*/
|
||||||
|
@ -96,6 +101,7 @@ public abstract class ItemInfoWithIcon extends ItemInfo {
|
||||||
protected ItemInfoWithIcon(ItemInfoWithIcon info) {
|
protected ItemInfoWithIcon(ItemInfoWithIcon info) {
|
||||||
super(info);
|
super(info);
|
||||||
iconBitmap = info.iconBitmap;
|
iconBitmap = info.iconBitmap;
|
||||||
|
iconColor = info.iconColor;
|
||||||
usingLowResIcon = info.usingLowResIcon;
|
usingLowResIcon = info.usingLowResIcon;
|
||||||
runtimeStatusFlags = info.runtimeStatusFlags;
|
runtimeStatusFlags = info.runtimeStatusFlags;
|
||||||
}
|
}
|
||||||
|
|
|
@ -620,7 +620,7 @@ public class LauncherModel extends BroadcastReceiver
|
||||||
@Override
|
@Override
|
||||||
public ShortcutInfo get() {
|
public ShortcutInfo get() {
|
||||||
si.updateFromDeepShortcutInfo(info, mApp.getContext());
|
si.updateFromDeepShortcutInfo(info, mApp.getContext());
|
||||||
si.iconBitmap = LauncherIcons.createShortcutIcon(info, mApp.getContext());
|
LauncherIcons.createShortcutIcon(info, mApp.getContext()).applyTo(si);
|
||||||
return si;
|
return si;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -28,7 +28,6 @@ import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
import android.content.pm.ResolveInfo;
|
import android.content.pm.ResolveInfo;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Color;
|
|
||||||
import android.graphics.Matrix;
|
import android.graphics.Matrix;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
|
@ -44,7 +43,6 @@ import android.text.style.TtsSpan;
|
||||||
import android.util.DisplayMetrics;
|
import android.util.DisplayMetrics;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
import android.util.SparseArray;
|
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
|
@ -285,89 +283,6 @@ public final class Utilities {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This picks a dominant color, looking for high-saturation, high-value, repeated hues.
|
|
||||||
* @param bitmap The bitmap to scan
|
|
||||||
* @param samples The approximate max number of samples to use.
|
|
||||||
*/
|
|
||||||
public static int findDominantColorByHue(Bitmap bitmap, int samples) {
|
|
||||||
final int height = bitmap.getHeight();
|
|
||||||
final int width = bitmap.getWidth();
|
|
||||||
int sampleStride = (int) Math.sqrt((height * width) / samples);
|
|
||||||
if (sampleStride < 1) {
|
|
||||||
sampleStride = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is an out-param, for getting the hsv values for an rgb
|
|
||||||
float[] hsv = new float[3];
|
|
||||||
|
|
||||||
// First get the best hue, by creating a histogram over 360 hue buckets,
|
|
||||||
// where each pixel contributes a score weighted by saturation, value, and alpha.
|
|
||||||
float[] hueScoreHistogram = new float[360];
|
|
||||||
float highScore = -1;
|
|
||||||
int bestHue = -1;
|
|
||||||
|
|
||||||
int[] pixels = new int[samples];
|
|
||||||
int pixelCount = 0;
|
|
||||||
|
|
||||||
for (int y = 0; y < height; y += sampleStride) {
|
|
||||||
for (int x = 0; x < width; x += sampleStride) {
|
|
||||||
int argb = bitmap.getPixel(x, y);
|
|
||||||
int alpha = 0xFF & (argb >> 24);
|
|
||||||
if (alpha < 0x80) {
|
|
||||||
// Drop mostly-transparent pixels.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Remove the alpha channel.
|
|
||||||
int rgb = argb | 0xFF000000;
|
|
||||||
Color.colorToHSV(rgb, hsv);
|
|
||||||
// Bucket colors by the 360 integer hues.
|
|
||||||
int hue = (int) hsv[0];
|
|
||||||
if (hue < 0 || hue >= hueScoreHistogram.length) {
|
|
||||||
// Defensively avoid array bounds violations.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (pixelCount < samples) {
|
|
||||||
pixels[pixelCount++] = rgb;
|
|
||||||
}
|
|
||||||
float score = hsv[1] * hsv[2];
|
|
||||||
hueScoreHistogram[hue] += score;
|
|
||||||
if (hueScoreHistogram[hue] > highScore) {
|
|
||||||
highScore = hueScoreHistogram[hue];
|
|
||||||
bestHue = hue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SparseArray<Float> rgbScores = new SparseArray<>();
|
|
||||||
int bestColor = 0xff000000;
|
|
||||||
highScore = -1;
|
|
||||||
// Go back over the RGB colors that match the winning hue,
|
|
||||||
// creating a histogram of weighted s*v scores, for up to 100*100 [s,v] buckets.
|
|
||||||
// The highest-scoring RGB color wins.
|
|
||||||
for (int i = 0; i < pixelCount; i++) {
|
|
||||||
int rgb = pixels[i];
|
|
||||||
Color.colorToHSV(rgb, hsv);
|
|
||||||
int hue = (int) hsv[0];
|
|
||||||
if (hue == bestHue) {
|
|
||||||
float s = hsv[1];
|
|
||||||
float v = hsv[2];
|
|
||||||
int bucket = (int) (s * 100) + (int) (v * 10000);
|
|
||||||
// Score by cumulative saturation * value.
|
|
||||||
float score = s * v;
|
|
||||||
Float oldTotal = rgbScores.get(bucket);
|
|
||||||
float newTotal = oldTotal == null ? score : oldTotal + score;
|
|
||||||
rgbScores.put(bucket, newTotal);
|
|
||||||
if (newTotal > highScore) {
|
|
||||||
highScore = newTotal;
|
|
||||||
// All the colors in the winning bucket are very similar. Last in wins.
|
|
||||||
bestColor = rgb;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bestColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Finds a system apk which had a broadcast receiver listening to a particular action.
|
* Finds a system apk which had a broadcast receiver listening to a particular action.
|
||||||
* @param action intent action used to find the apk
|
* @param action intent action used to find the apk
|
||||||
|
|
|
@ -16,12 +16,6 @@
|
||||||
|
|
||||||
package com.android.launcher3.badge;
|
package com.android.launcher3.badge;
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.graphics.BitmapShader;
|
|
||||||
import android.graphics.Canvas;
|
|
||||||
import android.graphics.Shader;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
import com.android.launcher3.notification.NotificationInfo;
|
import com.android.launcher3.notification.NotificationInfo;
|
||||||
|
@ -56,12 +50,6 @@ public class BadgeInfo {
|
||||||
/** This will only be initialized if the badge should display the notification icon. */
|
/** This will only be initialized if the badge should display the notification icon. */
|
||||||
private NotificationInfo mNotificationInfo;
|
private NotificationInfo mNotificationInfo;
|
||||||
|
|
||||||
/**
|
|
||||||
* When retrieving the notification icon, we draw it into this shader, which can be clipped
|
|
||||||
* as necessary when drawn in a badge.
|
|
||||||
*/
|
|
||||||
private Shader mNotificationIcon;
|
|
||||||
|
|
||||||
public BadgeInfo(PackageUserKey packageUserKey) {
|
public BadgeInfo(PackageUserKey packageUserKey) {
|
||||||
mPackageUserKey = packageUserKey;
|
mPackageUserKey = packageUserKey;
|
||||||
mNotificationKeys = new ArrayList<>();
|
mNotificationKeys = new ArrayList<>();
|
||||||
|
@ -112,42 +100,12 @@ public class BadgeInfo {
|
||||||
|
|
||||||
public void setNotificationToShow(@Nullable NotificationInfo notificationInfo) {
|
public void setNotificationToShow(@Nullable NotificationInfo notificationInfo) {
|
||||||
mNotificationInfo = notificationInfo;
|
mNotificationInfo = notificationInfo;
|
||||||
mNotificationIcon = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasNotificationToShow() {
|
public boolean hasNotificationToShow() {
|
||||||
return mNotificationInfo != null;
|
return mNotificationInfo != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a shader to set on a Paint that will draw the notification icon in a badge.
|
|
||||||
*
|
|
||||||
* The shader is cached until {@link #setNotificationToShow(NotificationInfo)} is called.
|
|
||||||
*/
|
|
||||||
public @Nullable Shader getNotificationIconForBadge(Context context, int badgeColor,
|
|
||||||
int badgeSize, int badgePadding) {
|
|
||||||
if (mNotificationInfo == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (mNotificationIcon == null) {
|
|
||||||
Drawable icon = mNotificationInfo.getIconForBackground(context, badgeColor)
|
|
||||||
.getConstantState().newDrawable();
|
|
||||||
int iconSize = badgeSize - badgePadding * 2;
|
|
||||||
icon.setBounds(0, 0, iconSize, iconSize);
|
|
||||||
Bitmap iconBitmap = Bitmap.createBitmap(badgeSize, badgeSize, Bitmap.Config.ARGB_8888);
|
|
||||||
Canvas canvas = new Canvas(iconBitmap);
|
|
||||||
canvas.translate(badgePadding, badgePadding);
|
|
||||||
icon.draw(canvas);
|
|
||||||
mNotificationIcon = new BitmapShader(iconBitmap, Shader.TileMode.CLAMP,
|
|
||||||
Shader.TileMode.CLAMP);
|
|
||||||
}
|
|
||||||
return mNotificationIcon;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isIconLarge() {
|
|
||||||
return mNotificationInfo != null && mNotificationInfo.isIconLarge();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether newBadge represents the same PackageUserKey as this badge, and icons with
|
* Whether newBadge represents the same PackageUserKey as this badge, and icons with
|
||||||
* this badge should be invalidated. So, for instance, if a badge has 3 notifications
|
* this badge should be invalidated. So, for instance, if a badge has 3 notifications
|
||||||
|
|
|
@ -16,21 +16,17 @@
|
||||||
|
|
||||||
package com.android.launcher3.badge;
|
package com.android.launcher3.badge;
|
||||||
|
|
||||||
import android.content.Context;
|
import static android.graphics.Paint.ANTI_ALIAS_FLAG;
|
||||||
import android.content.res.Resources;
|
import static android.graphics.Paint.FILTER_BITMAP_FLAG;
|
||||||
|
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.graphics.Point;
|
import android.graphics.Point;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.graphics.Shader;
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.SparseArray;
|
|
||||||
|
|
||||||
import com.android.launcher3.R;
|
|
||||||
import com.android.launcher3.graphics.IconPalette;
|
|
||||||
import com.android.launcher3.graphics.ShadowGenerator;
|
import com.android.launcher3.graphics.ShadowGenerator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -41,147 +37,58 @@ public class BadgeRenderer {
|
||||||
|
|
||||||
private static final String TAG = "BadgeRenderer";
|
private static final String TAG = "BadgeRenderer";
|
||||||
|
|
||||||
private static final boolean DOTS_ONLY = true;
|
|
||||||
|
|
||||||
// The badge sizes are defined as percentages of the app icon size.
|
// The badge sizes are defined as percentages of the app icon size.
|
||||||
private static final float SIZE_PERCENTAGE = 0.38f;
|
private static final float SIZE_PERCENTAGE = 0.23f;
|
||||||
// Used to expand the width of the badge for each additional digit.
|
// Used to expand the width of the badge for each additional digit.
|
||||||
private static final float CHAR_SIZE_PERCENTAGE = 0.12f;
|
|
||||||
private static final float TEXT_SIZE_PERCENTAGE = 0.26f;
|
|
||||||
private static final float OFFSET_PERCENTAGE = 0.02f;
|
private static final float OFFSET_PERCENTAGE = 0.02f;
|
||||||
private static final float STACK_OFFSET_PERCENTAGE_X = 0.05f;
|
|
||||||
private static final float STACK_OFFSET_PERCENTAGE_Y = 0.06f;
|
|
||||||
private static final float DOT_SCALE = 0.6f;
|
|
||||||
|
|
||||||
private final Context mContext;
|
|
||||||
private final int mSize;
|
private final int mSize;
|
||||||
private final int mCharSize;
|
|
||||||
private final int mTextHeight;
|
|
||||||
private final int mOffset;
|
private final int mOffset;
|
||||||
private final int mStackOffsetX;
|
private final float mCircleRadius;
|
||||||
private final int mStackOffsetY;
|
private final Paint mCirclePaint = new Paint(ANTI_ALIAS_FLAG | FILTER_BITMAP_FLAG);
|
||||||
private final IconDrawer mLargeIconDrawer;
|
|
||||||
private final IconDrawer mSmallIconDrawer;
|
|
||||||
private final Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
|
||||||
private final Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG
|
|
||||||
| Paint.FILTER_BITMAP_FLAG);
|
|
||||||
private final SparseArray<Bitmap> mBackgroundsWithShadow;
|
|
||||||
|
|
||||||
public BadgeRenderer(Context context, int iconSizePx) {
|
private final Bitmap mBackgroundWithShadow;
|
||||||
mContext = context;
|
private final int mBitmapOffset;
|
||||||
Resources res = context.getResources();
|
|
||||||
|
public BadgeRenderer(int iconSizePx) {
|
||||||
mSize = (int) (SIZE_PERCENTAGE * iconSizePx);
|
mSize = (int) (SIZE_PERCENTAGE * iconSizePx);
|
||||||
mCharSize = (int) (CHAR_SIZE_PERCENTAGE * iconSizePx);
|
|
||||||
mOffset = (int) (OFFSET_PERCENTAGE * iconSizePx);
|
mOffset = (int) (OFFSET_PERCENTAGE * iconSizePx);
|
||||||
mStackOffsetX = (int) (STACK_OFFSET_PERCENTAGE_X * iconSizePx);
|
|
||||||
mStackOffsetY = (int) (STACK_OFFSET_PERCENTAGE_Y * iconSizePx);
|
|
||||||
mTextPaint.setTextSize(iconSizePx * TEXT_SIZE_PERCENTAGE);
|
|
||||||
mTextPaint.setTextAlign(Paint.Align.CENTER);
|
|
||||||
mLargeIconDrawer = new IconDrawer(res.getDimensionPixelSize(R.dimen.badge_small_padding));
|
|
||||||
mSmallIconDrawer = new IconDrawer(res.getDimensionPixelSize(R.dimen.badge_large_padding));
|
|
||||||
// Measure the text height.
|
|
||||||
Rect tempTextHeight = new Rect();
|
|
||||||
mTextPaint.getTextBounds("0", 0, 1, tempTextHeight);
|
|
||||||
mTextHeight = tempTextHeight.height();
|
|
||||||
|
|
||||||
mBackgroundsWithShadow = new SparseArray<>(3);
|
ShadowGenerator.Builder builder = new ShadowGenerator.Builder(Color.TRANSPARENT);
|
||||||
|
mBackgroundWithShadow = builder.setupBlurForSize(mSize).createPill(mSize, mSize);
|
||||||
|
mCircleRadius = builder.radius;
|
||||||
|
|
||||||
|
mBitmapOffset = -mBackgroundWithShadow.getHeight() / 2; // Same as width.
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draw a circle in the top right corner of the given bounds, and draw
|
* Draw a circle in the top right corner of the given bounds, and draw
|
||||||
* {@link BadgeInfo#getNotificationCount()} on top of the circle.
|
* {@link BadgeInfo#getNotificationCount()} on top of the circle.
|
||||||
* @param palette The colors (based on the icon) to use for the badge.
|
* @param color The color (based on the icon) to use for the badge.
|
||||||
* @param badgeInfo Contains data to draw on the badge. Could be null if we are animating out.
|
|
||||||
* @param iconBounds The bounds of the icon being badged.
|
* @param iconBounds The bounds of the icon being badged.
|
||||||
* @param badgeScale The progress of the animation, from 0 to 1.
|
* @param badgeScale The progress of the animation, from 0 to 1.
|
||||||
* @param spaceForOffset How much space is available to offset the badge up and to the right.
|
* @param spaceForOffset How much space is available to offset the badge up and to the right.
|
||||||
*/
|
*/
|
||||||
public void draw(Canvas canvas, IconPalette palette, @Nullable BadgeInfo badgeInfo,
|
public void draw(
|
||||||
Rect iconBounds, float badgeScale, Point spaceForOffset) {
|
Canvas canvas, int color, Rect iconBounds, float badgeScale, Point spaceForOffset) {
|
||||||
if (palette == null || iconBounds == null || spaceForOffset == null) {
|
if (iconBounds == null || spaceForOffset == null) {
|
||||||
Log.e(TAG, "Invalid null argument(s) passed in call to draw.");
|
Log.e(TAG, "Invalid null argument(s) passed in call to draw.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mTextPaint.setColor(palette.textColor);
|
|
||||||
IconDrawer iconDrawer = badgeInfo != null && badgeInfo.isIconLarge()
|
|
||||||
? mLargeIconDrawer : mSmallIconDrawer;
|
|
||||||
Shader icon = badgeInfo == null ? null : badgeInfo.getNotificationIconForBadge(
|
|
||||||
mContext, palette.backgroundColor, mSize, iconDrawer.mPadding);
|
|
||||||
String notificationCount = badgeInfo == null ? "0"
|
|
||||||
: String.valueOf(badgeInfo.getNotificationCount());
|
|
||||||
int numChars = notificationCount.length();
|
|
||||||
int width = DOTS_ONLY ? mSize : mSize + mCharSize * (numChars - 1);
|
|
||||||
// Lazily load the background with shadow.
|
|
||||||
Bitmap backgroundWithShadow = mBackgroundsWithShadow.get(numChars);
|
|
||||||
if (backgroundWithShadow == null) {
|
|
||||||
backgroundWithShadow = new ShadowGenerator.Builder(Color.WHITE)
|
|
||||||
.setupBlurForSize(mSize).createPill(width, mSize);
|
|
||||||
mBackgroundsWithShadow.put(numChars, backgroundWithShadow);
|
|
||||||
}
|
|
||||||
canvas.save(Canvas.MATRIX_SAVE_FLAG);
|
canvas.save(Canvas.MATRIX_SAVE_FLAG);
|
||||||
// We draw the badge relative to its center.
|
// We draw the badge relative to its center.
|
||||||
int badgeCenterX = iconBounds.right - width / 2;
|
int badgeCenterX = iconBounds.right - mSize / 2;
|
||||||
int badgeCenterY = iconBounds.top + mSize / 2;
|
int badgeCenterY = iconBounds.top + mSize / 2;
|
||||||
boolean isText = !DOTS_ONLY && badgeInfo != null && badgeInfo.getNotificationCount() != 0;
|
|
||||||
boolean isIcon = !DOTS_ONLY && icon != null;
|
|
||||||
boolean isDot = !(isText || isIcon);
|
|
||||||
if (isDot) {
|
|
||||||
badgeScale *= DOT_SCALE;
|
|
||||||
}
|
|
||||||
int offsetX = Math.min(mOffset, spaceForOffset.x);
|
int offsetX = Math.min(mOffset, spaceForOffset.x);
|
||||||
int offsetY = Math.min(mOffset, spaceForOffset.y);
|
int offsetY = Math.min(mOffset, spaceForOffset.y);
|
||||||
canvas.translate(badgeCenterX + offsetX, badgeCenterY - offsetY);
|
canvas.translate(badgeCenterX + offsetX, badgeCenterY - offsetY);
|
||||||
canvas.scale(badgeScale, badgeScale);
|
canvas.scale(badgeScale, badgeScale);
|
||||||
// Prepare the background and shadow and possible stacking effect.
|
|
||||||
mBackgroundPaint.setColorFilter(palette.backgroundColorMatrixFilter);
|
|
||||||
int backgroundWithShadowSize = backgroundWithShadow.getHeight(); // Same as width.
|
|
||||||
boolean shouldStack = !isDot && badgeInfo != null
|
|
||||||
&& badgeInfo.getNotificationKeys().size() > 1;
|
|
||||||
if (shouldStack) {
|
|
||||||
int offsetDiffX = mStackOffsetX - mOffset;
|
|
||||||
int offsetDiffY = mStackOffsetY - mOffset;
|
|
||||||
canvas.translate(offsetDiffX, offsetDiffY);
|
|
||||||
canvas.drawBitmap(backgroundWithShadow, -backgroundWithShadowSize / 2,
|
|
||||||
-backgroundWithShadowSize / 2, mBackgroundPaint);
|
|
||||||
canvas.translate(-offsetDiffX, -offsetDiffY);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isText) {
|
mCirclePaint.setColor(Color.BLACK);
|
||||||
canvas.drawBitmap(backgroundWithShadow, -backgroundWithShadowSize / 2,
|
canvas.drawBitmap(mBackgroundWithShadow, mBitmapOffset, mBitmapOffset, mCirclePaint);
|
||||||
-backgroundWithShadowSize / 2, mBackgroundPaint);
|
mCirclePaint.setColor(color);
|
||||||
canvas.drawText(notificationCount, 0, mTextHeight / 2, mTextPaint);
|
canvas.drawCircle(0, 0, mCircleRadius, mCirclePaint);
|
||||||
} else if (isIcon) {
|
|
||||||
canvas.drawBitmap(backgroundWithShadow, -backgroundWithShadowSize / 2,
|
|
||||||
-backgroundWithShadowSize / 2, mBackgroundPaint);
|
|
||||||
iconDrawer.drawIcon(icon, canvas);
|
|
||||||
} else if (isDot) {
|
|
||||||
mBackgroundPaint.setColorFilter(palette.saturatedBackgroundColorMatrixFilter);
|
|
||||||
canvas.drawBitmap(backgroundWithShadow, -backgroundWithShadowSize / 2,
|
|
||||||
-backgroundWithShadowSize / 2, mBackgroundPaint);
|
|
||||||
}
|
|
||||||
canvas.restore();
|
canvas.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Draws the notification icon with padding of a given size. */
|
|
||||||
private class IconDrawer {
|
|
||||||
|
|
||||||
private final int mPadding;
|
|
||||||
private final Bitmap mCircleClipBitmap;
|
|
||||||
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG |
|
|
||||||
Paint.FILTER_BITMAP_FLAG);
|
|
||||||
|
|
||||||
public IconDrawer(int padding) {
|
|
||||||
mPadding = padding;
|
|
||||||
mCircleClipBitmap = Bitmap.createBitmap(mSize, mSize, Bitmap.Config.ALPHA_8);
|
|
||||||
Canvas canvas = new Canvas();
|
|
||||||
canvas.setBitmap(mCircleClipBitmap);
|
|
||||||
canvas.drawCircle(mSize / 2, mSize / 2, mSize / 2 - padding, mPaint);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void drawIcon(Shader icon, Canvas canvas) {
|
|
||||||
mPaint.setShader(icon);
|
|
||||||
canvas.drawBitmap(mCircleClipBitmap, -mSize / 2, -mSize / 2, mPaint);
|
|
||||||
mPaint.setShader(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -137,8 +137,7 @@ public class LauncherAppsCompatVO extends LauncherAppsCompatVL {
|
||||||
ShortcutInfoCompat compat = new ShortcutInfoCompat(request.getShortcutInfo());
|
ShortcutInfoCompat compat = new ShortcutInfoCompat(request.getShortcutInfo());
|
||||||
ShortcutInfo info = new ShortcutInfo(compat, context);
|
ShortcutInfo info = new ShortcutInfo(compat, context);
|
||||||
// Apply the unbadged icon and fetch the actual icon asynchronously.
|
// Apply the unbadged icon and fetch the actual icon asynchronously.
|
||||||
info.iconBitmap = LauncherIcons
|
LauncherIcons.createShortcutIcon(compat, context, false /* badged */).applyTo(info);
|
||||||
.createShortcutIcon(compat, context, false /* badged */);
|
|
||||||
LauncherAppState.getInstance(context).getModel()
|
LauncherAppState.getInstance(context).getModel()
|
||||||
.updateAndBindShortcutInfo(info, compat);
|
.updateAndBindShortcutInfo(info, compat);
|
||||||
return info;
|
return info;
|
||||||
|
|
|
@ -18,6 +18,7 @@ package com.android.launcher3.compat;
|
||||||
import static android.app.WallpaperManager.FLAG_SYSTEM;
|
import static android.app.WallpaperManager.FLAG_SYSTEM;
|
||||||
|
|
||||||
import static com.android.launcher3.Utilities.getDevicePrefs;
|
import static com.android.launcher3.Utilities.getDevicePrefs;
|
||||||
|
import static com.android.launcher3.graphics.ColorExtractor.findDominantColorByHue;
|
||||||
|
|
||||||
import android.app.WallpaperInfo;
|
import android.app.WallpaperInfo;
|
||||||
import android.app.WallpaperManager;
|
import android.app.WallpaperManager;
|
||||||
|
@ -257,7 +258,7 @@ public class WallpaperManagerCompatVL extends WallpaperManagerCompat {
|
||||||
String value = VERSION_PREFIX + wallpaperId;
|
String value = VERSION_PREFIX + wallpaperId;
|
||||||
|
|
||||||
if (bitmap != null) {
|
if (bitmap != null) {
|
||||||
int color = Utilities.findDominantColorByHue(bitmap, MAX_WALLPAPER_EXTRACTION_AREA);
|
int color = findDominantColorByHue(bitmap, MAX_WALLPAPER_EXTRACTION_AREA);
|
||||||
value += "," + color;
|
value += "," + color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,12 +18,14 @@ package com.android.launcher3.discovery;
|
||||||
|
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.graphics.Color;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
import com.android.launcher3.AppInfo;
|
import com.android.launcher3.AppInfo;
|
||||||
import com.android.launcher3.LauncherSettings;
|
import com.android.launcher3.LauncherSettings;
|
||||||
import com.android.launcher3.ShortcutInfo;
|
import com.android.launcher3.ShortcutInfo;
|
||||||
|
import com.android.launcher3.graphics.ColorExtractor;
|
||||||
|
|
||||||
public class AppDiscoveryAppInfo extends AppInfo {
|
public class AppDiscoveryAppInfo extends AppInfo {
|
||||||
|
|
||||||
|
@ -41,6 +43,8 @@ public class AppDiscoveryAppInfo extends AppInfo {
|
||||||
this.intent = item.isInstantApp ? item.launchIntent : item.installIntent;
|
this.intent = item.isInstantApp ? item.launchIntent : item.installIntent;
|
||||||
this.title = item.title;
|
this.title = item.title;
|
||||||
this.iconBitmap = item.bitmap;
|
this.iconBitmap = item.bitmap;
|
||||||
|
this.iconColor = iconBitmap == null ? Color.TRANSPARENT :
|
||||||
|
ColorExtractor.findDominantColorByHue(item.bitmap);
|
||||||
this.usingLowResIcon = false;
|
this.usingLowResIcon = false;
|
||||||
this.isInstantApp = item.isInstantApp;
|
this.isInstantApp = item.isInstantApp;
|
||||||
this.isRecent = item.isRecent;
|
this.isRecent = item.isRecent;
|
||||||
|
|
|
@ -265,7 +265,7 @@ public class DragView extends View {
|
||||||
mDrawBitmap = !(dr instanceof FolderAdaptiveIcon);
|
mDrawBitmap = !(dr instanceof FolderAdaptiveIcon);
|
||||||
|
|
||||||
if (info.isDisabled()) {
|
if (info.isDisabled()) {
|
||||||
FastBitmapDrawable d = new FastBitmapDrawable(null);
|
FastBitmapDrawable d = new FastBitmapDrawable((Bitmap) null);
|
||||||
d.setIsDisabled(true);
|
d.setIsDisabled(true);
|
||||||
mBaseFilter = (ColorMatrixColorFilter) d.getColorFilter();
|
mBaseFilter = (ColorMatrixColorFilter) d.getColorFilter();
|
||||||
}
|
}
|
||||||
|
@ -367,7 +367,8 @@ public class DragView extends View {
|
||||||
return new FixedSizeEmptyDrawable(iconSize);
|
return new FixedSizeEmptyDrawable(iconSize);
|
||||||
}
|
}
|
||||||
ShortcutInfoCompat si = (ShortcutInfoCompat) obj;
|
ShortcutInfoCompat si = (ShortcutInfoCompat) obj;
|
||||||
Bitmap badge = LauncherIcons.getShortcutInfoBadge(si, appState.getIconCache());
|
Bitmap badge = LauncherIcons.getShortcutInfoBadge(si, appState.getIconCache())
|
||||||
|
.iconBitmap;
|
||||||
|
|
||||||
float badgeSize = mLauncher.getResources().getDimension(R.dimen.profile_badge_size);
|
float badgeSize = mLauncher.getResources().getDimension(R.dimen.profile_badge_size);
|
||||||
float insetFraction = (iconSize - badgeSize) / iconSize;
|
float insetFraction = (iconSize - badgeSize) / iconSize;
|
||||||
|
|
|
@ -63,7 +63,6 @@ import com.android.launcher3.badge.FolderBadgeInfo;
|
||||||
import com.android.launcher3.dragndrop.BaseItemDragListener;
|
import com.android.launcher3.dragndrop.BaseItemDragListener;
|
||||||
import com.android.launcher3.dragndrop.DragLayer;
|
import com.android.launcher3.dragndrop.DragLayer;
|
||||||
import com.android.launcher3.dragndrop.DragView;
|
import com.android.launcher3.dragndrop.DragView;
|
||||||
import com.android.launcher3.graphics.IconPalette;
|
|
||||||
import com.android.launcher3.util.Thunk;
|
import com.android.launcher3.util.Thunk;
|
||||||
import com.android.launcher3.widget.PendingAddShortcutInfo;
|
import com.android.launcher3.widget.PendingAddShortcutInfo;
|
||||||
|
|
||||||
|
@ -499,8 +498,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
|
||||||
// If we are animating to the accepting state, animate the badge out.
|
// If we are animating to the accepting state, animate the badge out.
|
||||||
float badgeScale = Math.max(0, mBadgeScale - mBackground.getScaleProgress());
|
float badgeScale = Math.max(0, mBadgeScale - mBackground.getScaleProgress());
|
||||||
mTempSpaceForBadgeOffset.set(getWidth() - mTempBounds.right, mTempBounds.top);
|
mTempSpaceForBadgeOffset.set(getWidth() - mTempBounds.right, mTempBounds.top);
|
||||||
IconPalette badgePalette = IconPalette.getFolderBadgePalette(getResources());
|
mBadgeRenderer.draw(canvas, mBackground.getBadgeColor(), mTempBounds,
|
||||||
mBadgeRenderer.draw(canvas, badgePalette, mBadgeInfo, mTempBounds,
|
|
||||||
badgeScale, mTempSpaceForBadgeOffset);
|
badgeScale, mTempSpaceForBadgeOffset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -200,6 +200,10 @@ public class PreviewBackground {
|
||||||
return ColorUtils.setAlphaComponent(mBgColor, alpha);
|
return ColorUtils.setAlphaComponent(mBgColor, alpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getBadgeColor() {
|
||||||
|
return mBgColor;
|
||||||
|
}
|
||||||
|
|
||||||
public void drawBackground(Canvas canvas) {
|
public void drawBackground(Canvas canvas) {
|
||||||
mPaint.setStyle(Paint.Style.FILL);
|
mPaint.setStyle(Paint.Style.FILL);
|
||||||
mPaint.setColor(getBgColor());
|
mPaint.setColor(getBgColor());
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017 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.launcher3.graphics;
|
||||||
|
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
|
||||||
|
import com.android.launcher3.ItemInfoWithIcon;
|
||||||
|
|
||||||
|
public class BitmapInfo {
|
||||||
|
|
||||||
|
public Bitmap icon;
|
||||||
|
public int color;
|
||||||
|
|
||||||
|
public void applyTo(ItemInfoWithIcon info) {
|
||||||
|
info.iconBitmap = icon;
|
||||||
|
info.iconColor = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void applyTo(BitmapInfo info) {
|
||||||
|
info.icon = icon;
|
||||||
|
info.color = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BitmapInfo fromBitmap(Bitmap bitmap) {
|
||||||
|
BitmapInfo info = new BitmapInfo();
|
||||||
|
info.icon = bitmap;
|
||||||
|
info.color = ColorExtractor.findDominantColorByHue(bitmap);
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,113 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017 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.launcher3.graphics;
|
||||||
|
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.util.SparseArray;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class for extracting colors from a bitmap.
|
||||||
|
*/
|
||||||
|
public class ColorExtractor {
|
||||||
|
|
||||||
|
public static int findDominantColorByHue(Bitmap bitmap) {
|
||||||
|
return findDominantColorByHue(bitmap, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This picks a dominant color, looking for high-saturation, high-value, repeated hues.
|
||||||
|
* @param bitmap The bitmap to scan
|
||||||
|
* @param samples The approximate max number of samples to use.
|
||||||
|
*/
|
||||||
|
public static int findDominantColorByHue(Bitmap bitmap, int samples) {
|
||||||
|
final int height = bitmap.getHeight();
|
||||||
|
final int width = bitmap.getWidth();
|
||||||
|
int sampleStride = (int) Math.sqrt((height * width) / samples);
|
||||||
|
if (sampleStride < 1) {
|
||||||
|
sampleStride = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is an out-param, for getting the hsv values for an rgb
|
||||||
|
float[] hsv = new float[3];
|
||||||
|
|
||||||
|
// First get the best hue, by creating a histogram over 360 hue buckets,
|
||||||
|
// where each pixel contributes a score weighted by saturation, value, and alpha.
|
||||||
|
float[] hueScoreHistogram = new float[360];
|
||||||
|
float highScore = -1;
|
||||||
|
int bestHue = -1;
|
||||||
|
|
||||||
|
int[] pixels = new int[samples];
|
||||||
|
int pixelCount = 0;
|
||||||
|
|
||||||
|
for (int y = 0; y < height; y += sampleStride) {
|
||||||
|
for (int x = 0; x < width; x += sampleStride) {
|
||||||
|
int argb = bitmap.getPixel(x, y);
|
||||||
|
int alpha = 0xFF & (argb >> 24);
|
||||||
|
if (alpha < 0x80) {
|
||||||
|
// Drop mostly-transparent pixels.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Remove the alpha channel.
|
||||||
|
int rgb = argb | 0xFF000000;
|
||||||
|
Color.colorToHSV(rgb, hsv);
|
||||||
|
// Bucket colors by the 360 integer hues.
|
||||||
|
int hue = (int) hsv[0];
|
||||||
|
if (hue < 0 || hue >= hueScoreHistogram.length) {
|
||||||
|
// Defensively avoid array bounds violations.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (pixelCount < samples) {
|
||||||
|
pixels[pixelCount++] = rgb;
|
||||||
|
}
|
||||||
|
float score = hsv[1] * hsv[2];
|
||||||
|
hueScoreHistogram[hue] += score;
|
||||||
|
if (hueScoreHistogram[hue] > highScore) {
|
||||||
|
highScore = hueScoreHistogram[hue];
|
||||||
|
bestHue = hue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SparseArray<Float> rgbScores = new SparseArray<>();
|
||||||
|
int bestColor = 0xff000000;
|
||||||
|
highScore = -1;
|
||||||
|
// Go back over the RGB colors that match the winning hue,
|
||||||
|
// creating a histogram of weighted s*v scores, for up to 100*100 [s,v] buckets.
|
||||||
|
// The highest-scoring RGB color wins.
|
||||||
|
for (int i = 0; i < pixelCount; i++) {
|
||||||
|
int rgb = pixels[i];
|
||||||
|
Color.colorToHSV(rgb, hsv);
|
||||||
|
int hue = (int) hsv[0];
|
||||||
|
if (hue == bestHue) {
|
||||||
|
float s = hsv[1];
|
||||||
|
float v = hsv[2];
|
||||||
|
int bucket = (int) (s * 100) + (int) (v * 10000);
|
||||||
|
// Score by cumulative saturation * value.
|
||||||
|
float score = s * v;
|
||||||
|
Float oldTotal = rgbScores.get(bucket);
|
||||||
|
float newTotal = oldTotal == null ? score : oldTotal + score;
|
||||||
|
rgbScores.put(bucket, newTotal);
|
||||||
|
if (newTotal > highScore) {
|
||||||
|
highScore = newTotal;
|
||||||
|
// All the colors in the winning bucket are very similar. Last in wins.
|
||||||
|
bestColor = rgb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bestColor;
|
||||||
|
}
|
||||||
|
}
|
|
@ -31,7 +31,7 @@ import android.support.annotation.UiThread;
|
||||||
import android.util.ArrayMap;
|
import android.util.ArrayMap;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import com.android.launcher3.FastBitmapDrawable;
|
import com.android.launcher3.FastBitmapDrawable;
|
||||||
import com.android.launcher3.ItemInfo;
|
import com.android.launcher3.ItemInfoWithIcon;
|
||||||
import com.android.launcher3.R;
|
import com.android.launcher3.R;
|
||||||
import com.android.launcher3.Utilities;
|
import com.android.launcher3.Utilities;
|
||||||
import com.android.launcher3.allapps.AllAppsBackgroundDrawable;
|
import com.android.launcher3.allapps.AllAppsBackgroundDrawable;
|
||||||
|
@ -64,18 +64,18 @@ public class DrawableFactory {
|
||||||
/**
|
/**
|
||||||
* Returns a FastBitmapDrawable with the icon.
|
* Returns a FastBitmapDrawable with the icon.
|
||||||
*/
|
*/
|
||||||
public FastBitmapDrawable newIcon(Bitmap icon, ItemInfo info) {
|
public FastBitmapDrawable newIcon(ItemInfoWithIcon info) {
|
||||||
return new FastBitmapDrawable(icon);
|
return new FastBitmapDrawable(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a FastBitmapDrawable with the icon.
|
* Returns a FastBitmapDrawable with the icon.
|
||||||
*/
|
*/
|
||||||
public PreloadIconDrawable newPendingIcon(Bitmap icon, Context context) {
|
public PreloadIconDrawable newPendingIcon(ItemInfoWithIcon info, Context context) {
|
||||||
if (mPreloadProgressPath == null) {
|
if (mPreloadProgressPath == null) {
|
||||||
mPreloadProgressPath = getPreloadProgressPath(context);
|
mPreloadProgressPath = getPreloadProgressPath(context);
|
||||||
}
|
}
|
||||||
return new PreloadIconDrawable(icon, mPreloadProgressPath, context);
|
return new PreloadIconDrawable(info, mPreloadProgressPath, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -18,12 +18,7 @@ package com.android.launcher3.graphics;
|
||||||
|
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.ColorMatrix;
|
|
||||||
import android.graphics.ColorMatrixColorFilter;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.support.v4.graphics.ColorUtils;
|
import android.support.v4.graphics.ColorUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
@ -41,37 +36,10 @@ public class IconPalette {
|
||||||
private static final float MIN_PRELOAD_COLOR_SATURATION = 0.2f;
|
private static final float MIN_PRELOAD_COLOR_SATURATION = 0.2f;
|
||||||
private static final float MIN_PRELOAD_COLOR_LIGHTNESS = 0.6f;
|
private static final float MIN_PRELOAD_COLOR_LIGHTNESS = 0.6f;
|
||||||
|
|
||||||
private static IconPalette sBadgePalette;
|
|
||||||
private static IconPalette sFolderBadgePalette;
|
|
||||||
|
|
||||||
public final int dominantColor;
|
|
||||||
public final int backgroundColor;
|
|
||||||
public final ColorMatrixColorFilter backgroundColorMatrixFilter;
|
|
||||||
public final ColorMatrixColorFilter saturatedBackgroundColorMatrixFilter;
|
|
||||||
public final int textColor;
|
|
||||||
public final int secondaryColor;
|
|
||||||
|
|
||||||
private IconPalette(int color, boolean desaturateBackground) {
|
|
||||||
dominantColor = color;
|
|
||||||
backgroundColor = desaturateBackground ? getMutedColor(dominantColor, 0.87f) : dominantColor;
|
|
||||||
ColorMatrix backgroundColorMatrix = new ColorMatrix();
|
|
||||||
Themes.setColorScaleOnMatrix(backgroundColor, backgroundColorMatrix);
|
|
||||||
backgroundColorMatrixFilter = new ColorMatrixColorFilter(backgroundColorMatrix);
|
|
||||||
if (!desaturateBackground) {
|
|
||||||
saturatedBackgroundColorMatrixFilter = backgroundColorMatrixFilter;
|
|
||||||
} else {
|
|
||||||
// Get slightly more saturated background color.
|
|
||||||
Themes.setColorScaleOnMatrix(getMutedColor(dominantColor, 0.54f), backgroundColorMatrix);
|
|
||||||
saturatedBackgroundColorMatrixFilter = new ColorMatrixColorFilter(backgroundColorMatrix);
|
|
||||||
}
|
|
||||||
textColor = getTextColorForBackground(backgroundColor);
|
|
||||||
secondaryColor = getLowContrastColor(backgroundColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a color suitable for the progress bar color of preload icon.
|
* Returns a color suitable for the progress bar color of preload icon.
|
||||||
*/
|
*/
|
||||||
public int getPreloadProgressColor(Context context) {
|
public static int getPreloadProgressColor(Context context, int dominantColor) {
|
||||||
int result = dominantColor;
|
int result = dominantColor;
|
||||||
|
|
||||||
// Make sure that the dominant color has enough saturation to be visible properly.
|
// Make sure that the dominant color has enough saturation to be visible properly.
|
||||||
|
@ -86,37 +54,6 @@ public class IconPalette {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IconPalette fromDominantColor(int dominantColor, boolean desaturateBackground) {
|
|
||||||
return new IconPalette(dominantColor, desaturateBackground);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an IconPalette based on the badge_color in colors.xml.
|
|
||||||
* If that color is Color.TRANSPARENT, then returns null instead.
|
|
||||||
*/
|
|
||||||
public static @Nullable IconPalette getBadgePalette(Resources resources) {
|
|
||||||
int badgeColor = resources.getColor(R.color.badge_color);
|
|
||||||
if (badgeColor == Color.TRANSPARENT) {
|
|
||||||
// Colors will be extracted per app icon, so a static palette won't work.
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (sBadgePalette == null) {
|
|
||||||
sBadgePalette = fromDominantColor(badgeColor, false);
|
|
||||||
}
|
|
||||||
return sBadgePalette;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an IconPalette based on the folder_badge_color in colors.xml.
|
|
||||||
*/
|
|
||||||
public static @NonNull IconPalette getFolderBadgePalette(Resources resources) {
|
|
||||||
if (sFolderBadgePalette == null) {
|
|
||||||
int badgeColor = resources.getColor(R.color.folder_badge_color);
|
|
||||||
sFolderBadgePalette = fromDominantColor(badgeColor, false);
|
|
||||||
}
|
|
||||||
return sFolderBadgePalette;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves a color such that it has enough contrast to be used as the
|
* Resolves a color such that it has enough contrast to be used as the
|
||||||
* color of an icon or text on the given background color.
|
* color of an icon or text on the given background color.
|
||||||
|
@ -208,30 +145,8 @@ public class IconPalette {
|
||||||
return ColorUtils.LABToColor(low, a, b);
|
return ColorUtils.LABToColor(low, a, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getMutedColor(int color, float whiteScrimAlpha) {
|
public static int getMutedColor(int color, float whiteScrimAlpha) {
|
||||||
int whiteScrim = ColorUtils.setAlphaComponent(Color.WHITE, (int) (255 * whiteScrimAlpha));
|
int whiteScrim = ColorUtils.setAlphaComponent(Color.WHITE, (int) (255 * whiteScrimAlpha));
|
||||||
return ColorUtils.compositeColors(whiteScrim, color);
|
return ColorUtils.compositeColors(whiteScrim, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getTextColorForBackground(int backgroundColor) {
|
|
||||||
return getLighterOrDarkerVersionOfColor(backgroundColor, 4.5f);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int getLowContrastColor(int color) {
|
|
||||||
return getLighterOrDarkerVersionOfColor(color, 1.5f);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int getLighterOrDarkerVersionOfColor(int color, float contrastRatio) {
|
|
||||||
int whiteMinAlpha = ColorUtils.calculateMinimumAlpha(Color.WHITE, color, contrastRatio);
|
|
||||||
int blackMinAlpha = ColorUtils.calculateMinimumAlpha(Color.BLACK, color, contrastRatio);
|
|
||||||
int translucentWhiteOrBlack;
|
|
||||||
if (whiteMinAlpha >= 0) {
|
|
||||||
translucentWhiteOrBlack = ColorUtils.setAlphaComponent(Color.WHITE, whiteMinAlpha);
|
|
||||||
} else if (blackMinAlpha >= 0) {
|
|
||||||
translucentWhiteOrBlack = ColorUtils.setAlphaComponent(Color.BLACK, blackMinAlpha);
|
|
||||||
} else {
|
|
||||||
translucentWhiteOrBlack = Color.WHITE;
|
|
||||||
}
|
|
||||||
return ColorUtils.compositeColors(translucentWhiteOrBlack, color);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,6 @@ import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.Intent.ShortcutIconResource;
|
import android.content.Intent.ShortcutIconResource;
|
||||||
import android.content.pm.ApplicationInfo;
|
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
|
@ -42,6 +41,7 @@ import android.support.annotation.Nullable;
|
||||||
import com.android.launcher3.AppInfo;
|
import com.android.launcher3.AppInfo;
|
||||||
import com.android.launcher3.FastBitmapDrawable;
|
import com.android.launcher3.FastBitmapDrawable;
|
||||||
import com.android.launcher3.IconCache;
|
import com.android.launcher3.IconCache;
|
||||||
|
import com.android.launcher3.ItemInfoWithIcon;
|
||||||
import com.android.launcher3.LauncherAppState;
|
import com.android.launcher3.LauncherAppState;
|
||||||
import com.android.launcher3.R;
|
import com.android.launcher3.R;
|
||||||
import com.android.launcher3.Utilities;
|
import com.android.launcher3.Utilities;
|
||||||
|
@ -51,6 +51,7 @@ import com.android.launcher3.shortcuts.DeepShortcutManager;
|
||||||
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
|
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
|
||||||
import com.android.launcher3.uioverrides.UiFactory;
|
import com.android.launcher3.uioverrides.UiFactory;
|
||||||
import com.android.launcher3.util.Provider;
|
import com.android.launcher3.util.Provider;
|
||||||
|
import com.android.launcher3.util.Themes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper methods for generating various launcher icons
|
* Helper methods for generating various launcher icons
|
||||||
|
@ -69,7 +70,7 @@ public class LauncherIcons {
|
||||||
* Returns a bitmap suitable for the all apps view. If the package or the resource do not
|
* Returns a bitmap suitable for the all apps view. If the package or the resource do not
|
||||||
* exist, it returns null.
|
* exist, it returns null.
|
||||||
*/
|
*/
|
||||||
public static Bitmap createIconBitmap(ShortcutIconResource iconRes, Context context) {
|
public static BitmapInfo createIconBitmap(ShortcutIconResource iconRes, Context context) {
|
||||||
PackageManager packageManager = context.getPackageManager();
|
PackageManager packageManager = context.getPackageManager();
|
||||||
// the resource
|
// the resource
|
||||||
try {
|
try {
|
||||||
|
@ -92,12 +93,13 @@ public class LauncherIcons {
|
||||||
/**
|
/**
|
||||||
* Returns a bitmap which is of the appropriate size to be displayed as an icon
|
* Returns a bitmap which is of the appropriate size to be displayed as an icon
|
||||||
*/
|
*/
|
||||||
public static Bitmap createIconBitmap(Bitmap icon, Context context) {
|
public static BitmapInfo createIconBitmap(Bitmap icon, Context context) {
|
||||||
final int iconBitmapSize = LauncherAppState.getIDP(context).iconBitmapSize;
|
final int iconBitmapSize = LauncherAppState.getIDP(context).iconBitmapSize;
|
||||||
if (iconBitmapSize == icon.getWidth() && iconBitmapSize == icon.getHeight()) {
|
if (iconBitmapSize == icon.getWidth() && iconBitmapSize == icon.getHeight()) {
|
||||||
return icon;
|
return BitmapInfo.fromBitmap(icon);
|
||||||
}
|
}
|
||||||
return createIconBitmap(new BitmapDrawable(context.getResources(), icon), context, 1f);
|
return BitmapInfo.fromBitmap(
|
||||||
|
createIconBitmap(new BitmapDrawable(context.getResources(), icon), context, 1f));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -105,7 +107,7 @@ public class LauncherIcons {
|
||||||
* view or workspace. The icon is badged for {@param user}.
|
* view or workspace. The icon is badged for {@param user}.
|
||||||
* The bitmap is also visually normalized with other icons.
|
* The bitmap is also visually normalized with other icons.
|
||||||
*/
|
*/
|
||||||
public static Bitmap createBadgedIconBitmap(
|
public static BitmapInfo createBadgedIconBitmap(
|
||||||
Drawable icon, UserHandle user, Context context, int iconAppTargetSdk) {
|
Drawable icon, UserHandle user, Context context, int iconAppTargetSdk) {
|
||||||
|
|
||||||
IconNormalizer normalizer;
|
IconNormalizer normalizer;
|
||||||
|
@ -141,18 +143,20 @@ public class LauncherIcons {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final Bitmap result;
|
||||||
if (user != null && !Process.myUserHandle().equals(user)) {
|
if (user != null && !Process.myUserHandle().equals(user)) {
|
||||||
BitmapDrawable drawable = new FixedSizeBitmapDrawable(bitmap);
|
BitmapDrawable drawable = new FixedSizeBitmapDrawable(bitmap);
|
||||||
Drawable badged = context.getPackageManager().getUserBadgedIcon(
|
Drawable badged = context.getPackageManager().getUserBadgedIcon(
|
||||||
drawable, user);
|
drawable, user);
|
||||||
if (badged instanceof BitmapDrawable) {
|
if (badged instanceof BitmapDrawable) {
|
||||||
return ((BitmapDrawable) badged).getBitmap();
|
result = ((BitmapDrawable) badged).getBitmap();
|
||||||
} else {
|
} else {
|
||||||
return createIconBitmap(badged, context, 1f);
|
result = createIconBitmap(badged, context, 1f);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return bitmap;
|
result = bitmap;
|
||||||
}
|
}
|
||||||
|
return BitmapInfo.fromBitmap(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -302,29 +306,23 @@ public class LauncherIcons {
|
||||||
return drawable;
|
return drawable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Bitmap createShortcutIcon(ShortcutInfoCompat shortcutInfo, Context context) {
|
public static BitmapInfo createShortcutIcon(ShortcutInfoCompat shortcutInfo, Context context) {
|
||||||
return createShortcutIcon(shortcutInfo, context, true /* badged */);
|
return createShortcutIcon(shortcutInfo, context, true /* badged */);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Bitmap createShortcutIcon(ShortcutInfoCompat shortcutInfo, Context context,
|
public static BitmapInfo createShortcutIcon(ShortcutInfoCompat shortcutInfo, Context context,
|
||||||
boolean badged) {
|
boolean badged) {
|
||||||
return createShortcutIcon(shortcutInfo, context, badged, null);
|
return createShortcutIcon(shortcutInfo, context, badged, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Bitmap createShortcutIcon(ShortcutInfoCompat shortcutInfo, Context context,
|
public static BitmapInfo createShortcutIcon(ShortcutInfoCompat shortcutInfo, Context context,
|
||||||
final Bitmap fallbackIcon) {
|
|
||||||
// If the shortcut is pinned but no longer has an icon in the system,
|
|
||||||
// keep the current icon instead of reverting to the default icon.
|
|
||||||
return createShortcutIcon(shortcutInfo, context, true, Provider.of(fallbackIcon));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Bitmap createShortcutIcon(ShortcutInfoCompat shortcutInfo, Context context,
|
|
||||||
boolean badged, @Nullable Provider<Bitmap> fallbackIconProvider) {
|
boolean badged, @Nullable Provider<Bitmap> fallbackIconProvider) {
|
||||||
LauncherAppState app = LauncherAppState.getInstance(context);
|
LauncherAppState app = LauncherAppState.getInstance(context);
|
||||||
Drawable unbadgedDrawable = DeepShortcutManager.getInstance(context)
|
Drawable unbadgedDrawable = DeepShortcutManager.getInstance(context)
|
||||||
.getShortcutIconDrawable(shortcutInfo,
|
.getShortcutIconDrawable(shortcutInfo,
|
||||||
app.getInvariantDeviceProfile().fillResIconDpi);
|
app.getInvariantDeviceProfile().fillResIconDpi);
|
||||||
IconCache cache = app.getIconCache();
|
IconCache cache = app.getIconCache();
|
||||||
|
|
||||||
Bitmap unbadgedBitmap = null;
|
Bitmap unbadgedBitmap = null;
|
||||||
if (unbadgedDrawable != null) {
|
if (unbadgedDrawable != null) {
|
||||||
unbadgedBitmap = LauncherIcons.createScaledBitmapWithoutShadow(
|
unbadgedBitmap = LauncherIcons.createScaledBitmapWithoutShadow(
|
||||||
|
@ -334,27 +332,32 @@ public class LauncherIcons {
|
||||||
unbadgedBitmap = fallbackIconProvider.get();
|
unbadgedBitmap = fallbackIconProvider.get();
|
||||||
}
|
}
|
||||||
if (unbadgedBitmap == null) {
|
if (unbadgedBitmap == null) {
|
||||||
unbadgedBitmap = cache.getDefaultIcon(Process.myUserHandle());
|
unbadgedBitmap = cache.getDefaultIcon(Process.myUserHandle()).icon;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BitmapInfo result = new BitmapInfo();
|
||||||
if (!badged) {
|
if (!badged) {
|
||||||
return unbadgedBitmap;
|
result.color = Themes.getColorAccent(context);
|
||||||
|
result.icon = unbadgedBitmap;
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int size = app.getInvariantDeviceProfile().iconBitmapSize;
|
int size = app.getInvariantDeviceProfile().iconBitmapSize;
|
||||||
|
|
||||||
final Bitmap unbadgedfinal = unbadgedBitmap;
|
final Bitmap unbadgedfinal = unbadgedBitmap;
|
||||||
final Bitmap badge = getShortcutInfoBadge(shortcutInfo, cache);
|
final ItemInfoWithIcon badge = getShortcutInfoBadge(shortcutInfo, cache);
|
||||||
|
|
||||||
return UiFactory.createFromRenderer(size, size, false, (c) -> {
|
result.color = badge.iconColor;
|
||||||
|
result.icon = UiFactory.createFromRenderer(size, size, false, (c) -> {
|
||||||
ShadowGenerator.getInstance(context).recreateIcon(unbadgedfinal, c);
|
ShadowGenerator.getInstance(context).recreateIcon(unbadgedfinal, c);
|
||||||
badgeWithDrawable(c, new FastBitmapDrawable(badge), context);
|
badgeWithDrawable(c, new FastBitmapDrawable(badge), context);
|
||||||
});
|
});
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Bitmap getShortcutInfoBadge(ShortcutInfoCompat shortcutInfo, IconCache cache) {
|
public static ItemInfoWithIcon getShortcutInfoBadge(
|
||||||
final Bitmap badgeBitmap;
|
ShortcutInfoCompat shortcutInfo, IconCache cache) {
|
||||||
ComponentName cn = shortcutInfo.getActivity();
|
ComponentName cn = shortcutInfo.getActivity();
|
||||||
if (cn != null) {
|
if (cn != null) {
|
||||||
// Get the app info for the source activity.
|
// Get the app info for the source activity.
|
||||||
|
@ -365,13 +368,12 @@ public class LauncherIcons {
|
||||||
.addCategory(Intent.CATEGORY_LAUNCHER)
|
.addCategory(Intent.CATEGORY_LAUNCHER)
|
||||||
.setComponent(cn);
|
.setComponent(cn);
|
||||||
cache.getTitleAndIcon(appInfo, false);
|
cache.getTitleAndIcon(appInfo, false);
|
||||||
badgeBitmap = appInfo.iconBitmap;
|
return appInfo;
|
||||||
} else {
|
} else {
|
||||||
PackageItemInfo pkgInfo = new PackageItemInfo(shortcutInfo.getPackage());
|
PackageItemInfo pkgInfo = new PackageItemInfo(shortcutInfo.getPackage());
|
||||||
cache.getTitleAndIconForApp(pkgInfo, false);
|
cache.getTitleAndIconForApp(pkgInfo, false);
|
||||||
badgeBitmap = pkgInfo.iconBitmap;
|
return pkgInfo;
|
||||||
}
|
}
|
||||||
return badgeBitmap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -32,6 +32,7 @@ import android.util.Property;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
|
|
||||||
import com.android.launcher3.FastBitmapDrawable;
|
import com.android.launcher3.FastBitmapDrawable;
|
||||||
|
import com.android.launcher3.ItemInfoWithIcon;
|
||||||
import com.android.launcher3.anim.Interpolators;
|
import com.android.launcher3.anim.Interpolators;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
|
@ -86,7 +87,7 @@ public class PreloadIconDrawable extends FastBitmapDrawable {
|
||||||
private final Paint mProgressPaint;
|
private final Paint mProgressPaint;
|
||||||
|
|
||||||
private Bitmap mShadowBitmap;
|
private Bitmap mShadowBitmap;
|
||||||
private int mIndicatorColor = 0;
|
private final int mIndicatorColor;
|
||||||
|
|
||||||
private int mTrackAlpha;
|
private int mTrackAlpha;
|
||||||
private float mTrackLength;
|
private float mTrackLength;
|
||||||
|
@ -103,8 +104,8 @@ public class PreloadIconDrawable extends FastBitmapDrawable {
|
||||||
/**
|
/**
|
||||||
* @param progressPath fixed path in the bounds [0, 0, 100, 100] representing a progress bar.
|
* @param progressPath fixed path in the bounds [0, 0, 100, 100] representing a progress bar.
|
||||||
*/
|
*/
|
||||||
public PreloadIconDrawable(Bitmap b, Path progressPath, Context context) {
|
public PreloadIconDrawable(ItemInfoWithIcon info, Path progressPath, Context context) {
|
||||||
super(b);
|
super(info);
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mProgressPath = progressPath;
|
mProgressPath = progressPath;
|
||||||
mScaledTrackPath = new Path();
|
mScaledTrackPath = new Path();
|
||||||
|
@ -113,6 +114,7 @@ public class PreloadIconDrawable extends FastBitmapDrawable {
|
||||||
mProgressPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
|
mProgressPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
|
||||||
mProgressPaint.setStyle(Paint.Style.STROKE);
|
mProgressPaint.setStyle(Paint.Style.STROKE);
|
||||||
mProgressPaint.setStrokeCap(Paint.Cap.ROUND);
|
mProgressPaint.setStrokeCap(Paint.Cap.ROUND);
|
||||||
|
mIndicatorColor = IconPalette.getPreloadProgressColor(context, mIconColor);
|
||||||
|
|
||||||
setInternalProgress(0);
|
setInternalProgress(0);
|
||||||
}
|
}
|
||||||
|
@ -266,9 +268,6 @@ public class PreloadIconDrawable extends FastBitmapDrawable {
|
||||||
mScaledTrackPath.reset();
|
mScaledTrackPath.reset();
|
||||||
mTrackAlpha = MAX_PAINT_ALPHA;
|
mTrackAlpha = MAX_PAINT_ALPHA;
|
||||||
setIsDisabled(true);
|
setIsDisabled(true);
|
||||||
} else if (mIndicatorColor == 0) {
|
|
||||||
// Update the indicator color
|
|
||||||
mIndicatorColor = getIconPalette().getPreloadProgressColor(mContext);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (progress < 1 && progress > 0) {
|
if (progress < 1 && progress > 0) {
|
||||||
|
|
|
@ -44,6 +44,7 @@ import com.android.launcher3.Workspace;
|
||||||
import com.android.launcher3.compat.LauncherAppsCompat;
|
import com.android.launcher3.compat.LauncherAppsCompat;
|
||||||
import com.android.launcher3.compat.UserManagerCompat;
|
import com.android.launcher3.compat.UserManagerCompat;
|
||||||
import com.android.launcher3.config.FeatureFlags;
|
import com.android.launcher3.config.FeatureFlags;
|
||||||
|
import com.android.launcher3.graphics.BitmapInfo;
|
||||||
import com.android.launcher3.graphics.LauncherIcons;
|
import com.android.launcher3.graphics.LauncherIcons;
|
||||||
import com.android.launcher3.logging.FileLog;
|
import com.android.launcher3.logging.FileLog;
|
||||||
import com.android.launcher3.util.ContentWriter;
|
import com.android.launcher3.util.ContentWriter;
|
||||||
|
@ -151,10 +152,9 @@ public class LoaderCursor extends CursorWrapper {
|
||||||
info.user = user;
|
info.user = user;
|
||||||
info.itemType = itemType;
|
info.itemType = itemType;
|
||||||
info.title = getTitle();
|
info.title = getTitle();
|
||||||
info.iconBitmap = loadIcon(info);
|
|
||||||
// the fallback icon
|
// the fallback icon
|
||||||
if (info.iconBitmap == null) {
|
if (!loadIcon(info)) {
|
||||||
info.iconBitmap = mIconCache.getDefaultIcon(info.user);
|
mIconCache.getDefaultIcon(info.user).applyTo(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: If there's an explicit component and we can't install that, delete it.
|
// TODO: If there's an explicit component and we can't install that, delete it.
|
||||||
|
@ -165,8 +165,7 @@ public class LoaderCursor extends CursorWrapper {
|
||||||
/**
|
/**
|
||||||
* Loads the icon from the cursor and updates the {@param info} if the icon is an app resource.
|
* Loads the icon from the cursor and updates the {@param info} if the icon is an app resource.
|
||||||
*/
|
*/
|
||||||
protected Bitmap loadIcon(ShortcutInfo info) {
|
protected boolean loadIcon(ShortcutInfo info) {
|
||||||
Bitmap icon = null;
|
|
||||||
if (itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
|
if (itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
|
||||||
String packageName = getString(iconPackageIndex);
|
String packageName = getString(iconPackageIndex);
|
||||||
String resourceName = getString(iconResourceIndex);
|
String resourceName = getString(iconResourceIndex);
|
||||||
|
@ -174,24 +173,24 @@ public class LoaderCursor extends CursorWrapper {
|
||||||
info.iconResource = new ShortcutIconResource();
|
info.iconResource = new ShortcutIconResource();
|
||||||
info.iconResource.packageName = packageName;
|
info.iconResource.packageName = packageName;
|
||||||
info.iconResource.resourceName = resourceName;
|
info.iconResource.resourceName = resourceName;
|
||||||
icon = LauncherIcons.createIconBitmap(info.iconResource, mContext);
|
BitmapInfo iconInfo = LauncherIcons.createIconBitmap(info.iconResource, mContext);
|
||||||
|
if (iconInfo != null) {
|
||||||
|
iconInfo.applyTo(info);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (icon == null) {
|
|
||||||
// Failed to load from resource, try loading from DB.
|
// Failed to load from resource, try loading from DB.
|
||||||
byte[] data = getBlob(iconIndex);
|
byte[] data = getBlob(iconIndex);
|
||||||
try {
|
try {
|
||||||
icon = LauncherIcons.createIconBitmap(
|
LauncherIcons.createIconBitmap(BitmapFactory.decodeByteArray(data, 0, data.length),
|
||||||
BitmapFactory.decodeByteArray(data, 0, data.length), mContext);
|
mContext).applyTo(info);
|
||||||
} catch (Exception e) {
|
return true;
|
||||||
Log.e(TAG, "Failed to load icon for info " + info, e);
|
} catch (Exception e) {
|
||||||
return null;
|
Log.e(TAG, "Failed to load icon for info " + info, e);
|
||||||
}
|
return false;
|
||||||
}
|
}
|
||||||
if (icon == null) {
|
|
||||||
Log.e(TAG, "Failed to load icon for info " + info);
|
|
||||||
}
|
|
||||||
return icon;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -211,9 +210,8 @@ public class LoaderCursor extends CursorWrapper {
|
||||||
info.user = user;
|
info.user = user;
|
||||||
info.intent = intent;
|
info.intent = intent;
|
||||||
|
|
||||||
info.iconBitmap = loadIcon(info);
|
|
||||||
// the fallback icon
|
// the fallback icon
|
||||||
if (info.iconBitmap == null) {
|
if (!loadIcon(info)) {
|
||||||
mIconCache.getTitleAndIcon(info, false /* useLowResIcon */);
|
mIconCache.getTitleAndIcon(info, false /* useLowResIcon */);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,8 +267,7 @@ public class LoaderCursor extends CursorWrapper {
|
||||||
|
|
||||||
mIconCache.getTitleAndIcon(info, lai, useLowResIcon);
|
mIconCache.getTitleAndIcon(info, lai, useLowResIcon);
|
||||||
if (mIconCache.isDefaultIcon(info.iconBitmap, user)) {
|
if (mIconCache.isDefaultIcon(info.iconBitmap, user)) {
|
||||||
Bitmap icon = loadIcon(info);
|
loadIcon(info);
|
||||||
info.iconBitmap = icon != null ? icon : info.iconBitmap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lai != null) {
|
if (lai != null) {
|
||||||
|
|
|
@ -472,12 +472,12 @@ public class LoaderTask implements Runnable {
|
||||||
public Bitmap get() {
|
public Bitmap get() {
|
||||||
// If the pinned deep shortcut is no longer published,
|
// If the pinned deep shortcut is no longer published,
|
||||||
// use the last saved icon instead of the default.
|
// use the last saved icon instead of the default.
|
||||||
return c.loadIcon(finalInfo);
|
return c.loadIcon(finalInfo)
|
||||||
|
? finalInfo.iconBitmap : null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
info.iconBitmap = LauncherIcons
|
LauncherIcons.createShortcutIcon(pinnedShortcut, context,
|
||||||
.createShortcutIcon(pinnedShortcut, context,
|
true /* badged */, fallbackIconProvider).applyTo(info);
|
||||||
true /* badged */, fallbackIconProvider);
|
|
||||||
if (pmHelper.isAppSuspended(
|
if (pmHelper.isAppSuspended(
|
||||||
pinnedShortcut.getPackage(), info.user)) {
|
pinnedShortcut.getPackage(), info.user)) {
|
||||||
info.runtimeStatusFlags |= FLAG_DISABLED_SUSPENDED;
|
info.runtimeStatusFlags |= FLAG_DISABLED_SUSPENDED;
|
||||||
|
|
|
@ -40,6 +40,7 @@ import com.android.launcher3.Utilities;
|
||||||
import com.android.launcher3.compat.LauncherAppsCompat;
|
import com.android.launcher3.compat.LauncherAppsCompat;
|
||||||
import com.android.launcher3.compat.UserManagerCompat;
|
import com.android.launcher3.compat.UserManagerCompat;
|
||||||
import com.android.launcher3.config.FeatureFlags;
|
import com.android.launcher3.config.FeatureFlags;
|
||||||
|
import com.android.launcher3.graphics.BitmapInfo;
|
||||||
import com.android.launcher3.graphics.LauncherIcons;
|
import com.android.launcher3.graphics.LauncherIcons;
|
||||||
import com.android.launcher3.util.FlagOp;
|
import com.android.launcher3.util.FlagOp;
|
||||||
import com.android.launcher3.util.ItemInfoMatcher;
|
import com.android.launcher3.util.ItemInfoMatcher;
|
||||||
|
@ -191,9 +192,10 @@ public class PackageUpdatedTask extends BaseModelUpdateTask {
|
||||||
// Update shortcuts which use iconResource.
|
// Update shortcuts which use iconResource.
|
||||||
if ((si.iconResource != null)
|
if ((si.iconResource != null)
|
||||||
&& packageSet.contains(si.iconResource.packageName)) {
|
&& packageSet.contains(si.iconResource.packageName)) {
|
||||||
Bitmap icon = LauncherIcons.createIconBitmap(si.iconResource, context);
|
BitmapInfo iconInfo =
|
||||||
if (icon != null) {
|
LauncherIcons.createIconBitmap(si.iconResource, context);
|
||||||
si.iconBitmap = icon;
|
if (iconInfo != null) {
|
||||||
|
iconInfo.applyTo(si);
|
||||||
infoUpdated = true;
|
infoUpdated = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ import com.android.launcher3.shortcuts.ShortcutInfoCompat;
|
||||||
import com.android.launcher3.shortcuts.ShortcutKey;
|
import com.android.launcher3.shortcuts.ShortcutKey;
|
||||||
import com.android.launcher3.util.ItemInfoMatcher;
|
import com.android.launcher3.util.ItemInfoMatcher;
|
||||||
import com.android.launcher3.util.MultiHashMap;
|
import com.android.launcher3.util.MultiHashMap;
|
||||||
|
import com.android.launcher3.util.Provider;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
@ -92,8 +93,10 @@ public class ShortcutsChangedTask extends BaseModelUpdateTask {
|
||||||
}
|
}
|
||||||
for (final ShortcutInfo shortcutInfo : shortcutInfos) {
|
for (final ShortcutInfo shortcutInfo : shortcutInfos) {
|
||||||
shortcutInfo.updateFromDeepShortcutInfo(fullDetails, context);
|
shortcutInfo.updateFromDeepShortcutInfo(fullDetails, context);
|
||||||
shortcutInfo.iconBitmap = LauncherIcons.createShortcutIcon(fullDetails, context,
|
// If the shortcut is pinned but no longer has an icon in the system,
|
||||||
shortcutInfo.iconBitmap);
|
// keep the current icon instead of reverting to the default icon.
|
||||||
|
LauncherIcons.createShortcutIcon(fullDetails, context, true,
|
||||||
|
Provider.of(shortcutInfo.iconBitmap)).applyTo(shortcutInfo);
|
||||||
updatedShortcutInfos.add(shortcutInfo);
|
updatedShortcutInfos.add(shortcutInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ import com.android.launcher3.shortcuts.ShortcutInfoCompat;
|
||||||
import com.android.launcher3.shortcuts.ShortcutKey;
|
import com.android.launcher3.shortcuts.ShortcutKey;
|
||||||
import com.android.launcher3.util.ComponentKey;
|
import com.android.launcher3.util.ComponentKey;
|
||||||
import com.android.launcher3.util.ItemInfoMatcher;
|
import com.android.launcher3.util.ItemInfoMatcher;
|
||||||
|
import com.android.launcher3.util.Provider;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -91,8 +92,10 @@ public class UserLockStateChangedTask extends BaseModelUpdateTask {
|
||||||
}
|
}
|
||||||
si.runtimeStatusFlags &= ~FLAG_DISABLED_LOCKED_USER;
|
si.runtimeStatusFlags &= ~FLAG_DISABLED_LOCKED_USER;
|
||||||
si.updateFromDeepShortcutInfo(shortcut, context);
|
si.updateFromDeepShortcutInfo(shortcut, context);
|
||||||
si.iconBitmap = LauncherIcons.createShortcutIcon(shortcut, context,
|
// If the shortcut is pinned but no longer has an icon in the system,
|
||||||
si.iconBitmap);
|
// keep the current icon instead of reverting to the default icon.
|
||||||
|
LauncherIcons.createShortcutIcon(shortcut, context, true,
|
||||||
|
Provider.of(si.iconBitmap)).applyTo(si);
|
||||||
} else {
|
} else {
|
||||||
si.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER;
|
si.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER;
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,7 +83,7 @@ public class NotificationInfo implements View.OnClickListener {
|
||||||
if (mIconDrawable == null) {
|
if (mIconDrawable == null) {
|
||||||
mIconDrawable = new BitmapDrawable(context.getResources(), LauncherAppState
|
mIconDrawable = new BitmapDrawable(context.getResources(), LauncherAppState
|
||||||
.getInstance(context).getIconCache()
|
.getInstance(context).getIconCache()
|
||||||
.getDefaultIcon(statusBarNotification.getUser()));
|
.getDefaultIcon(statusBarNotification.getUser()).icon);
|
||||||
mBadgeIcon = Notification.BADGE_ICON_NONE;
|
mBadgeIcon = Notification.BADGE_ICON_NONE;
|
||||||
}
|
}
|
||||||
intent = notification.contentIntent;
|
intent = notification.contentIntent;
|
||||||
|
|
|
@ -18,6 +18,7 @@ package com.android.launcher3.notification;
|
||||||
|
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.graphics.Color;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
|
@ -114,12 +115,12 @@ public class NotificationItemView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateHeader(int notificationCount, @Nullable IconPalette palette) {
|
public void updateHeader(int notificationCount, int iconColor) {
|
||||||
mHeaderCount.setText(notificationCount <= 1 ? "" : String.valueOf(notificationCount));
|
mHeaderCount.setText(notificationCount <= 1 ? "" : String.valueOf(notificationCount));
|
||||||
if (palette != null) {
|
if (Color.alpha(iconColor) > 0) {
|
||||||
if (mNotificationHeaderTextColor == Notification.COLOR_DEFAULT) {
|
if (mNotificationHeaderTextColor == Notification.COLOR_DEFAULT) {
|
||||||
mNotificationHeaderTextColor =
|
mNotificationHeaderTextColor =
|
||||||
IconPalette.resolveContrastColor(mContext, palette.dominantColor,
|
IconPalette.resolveContrastColor(mContext, iconColor,
|
||||||
Themes.getAttrColor(mContext, R.attr.popupColorPrimary));
|
Themes.getAttrColor(mContext, R.attr.popupColorPrimary));
|
||||||
}
|
}
|
||||||
mHeaderText.setTextColor(mNotificationHeaderTextColor);
|
mHeaderText.setTextColor(mNotificationHeaderTextColor);
|
||||||
|
|
|
@ -54,6 +54,7 @@ import com.android.launcher3.DragSource;
|
||||||
import com.android.launcher3.DropTarget;
|
import com.android.launcher3.DropTarget;
|
||||||
import com.android.launcher3.DropTarget.DragObject;
|
import com.android.launcher3.DropTarget.DragObject;
|
||||||
import com.android.launcher3.ItemInfo;
|
import com.android.launcher3.ItemInfo;
|
||||||
|
import com.android.launcher3.ItemInfoWithIcon;
|
||||||
import com.android.launcher3.Launcher;
|
import com.android.launcher3.Launcher;
|
||||||
import com.android.launcher3.LauncherAnimUtils;
|
import com.android.launcher3.LauncherAnimUtils;
|
||||||
import com.android.launcher3.LauncherModel;
|
import com.android.launcher3.LauncherModel;
|
||||||
|
@ -742,11 +743,11 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateNotificationHeader() {
|
private void updateNotificationHeader() {
|
||||||
ItemInfo itemInfo = (ItemInfo) mOriginalIcon.getTag();
|
ItemInfoWithIcon itemInfo = (ItemInfoWithIcon) mOriginalIcon.getTag();
|
||||||
BadgeInfo badgeInfo = mLauncher.getPopupDataProvider().getBadgeInfoForItem(itemInfo);
|
BadgeInfo badgeInfo = mLauncher.getPopupDataProvider().getBadgeInfoForItem(itemInfo);
|
||||||
if (mNotificationItemView != null && badgeInfo != null) {
|
if (mNotificationItemView != null && badgeInfo != null) {
|
||||||
IconPalette palette = mOriginalIcon.getBadgePalette();
|
mNotificationItemView.updateHeader(
|
||||||
mNotificationItemView.updateHeader(badgeInfo.getNotificationCount(), palette);
|
badgeInfo.getNotificationCount(), itemInfo.iconColor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -148,8 +148,8 @@ public class PopupPopulator {
|
||||||
final ShortcutInfoCompat shortcut = shortcuts.get(i);
|
final ShortcutInfoCompat shortcut = shortcuts.get(i);
|
||||||
final ShortcutInfo si = new ShortcutInfo(shortcut, launcher);
|
final ShortcutInfo si = new ShortcutInfo(shortcut, launcher);
|
||||||
// Use unbadged icon for the menu.
|
// Use unbadged icon for the menu.
|
||||||
si.iconBitmap = LauncherIcons.createShortcutIcon(
|
LauncherIcons.createShortcutIcon(shortcut, launcher, false /* badged */)
|
||||||
shortcut, launcher, false /* badged */);
|
.applyTo(si);
|
||||||
si.rank = i;
|
si.rank = i;
|
||||||
|
|
||||||
final DeepShortcutView view = shortcutViews.get(i);
|
final DeepShortcutView view = shortcutViews.get(i);
|
||||||
|
|
|
@ -58,8 +58,6 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView
|
||||||
private final int mStartState;
|
private final int mStartState;
|
||||||
private final boolean mDisabledForSafeMode;
|
private final boolean mDisabledForSafeMode;
|
||||||
|
|
||||||
private Bitmap mIcon;
|
|
||||||
|
|
||||||
private Drawable mCenterDrawable;
|
private Drawable mCenterDrawable;
|
||||||
private Drawable mSettingIconDrawable;
|
private Drawable mSettingIconDrawable;
|
||||||
|
|
||||||
|
@ -129,53 +127,44 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reapplyItemInfo(ItemInfoWithIcon info) {
|
public void reapplyItemInfo(ItemInfoWithIcon info) {
|
||||||
Bitmap icon = info.iconBitmap;
|
|
||||||
if (mIcon == icon) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mIcon = icon;
|
|
||||||
if (mCenterDrawable != null) {
|
if (mCenterDrawable != null) {
|
||||||
mCenterDrawable.setCallback(null);
|
mCenterDrawable.setCallback(null);
|
||||||
mCenterDrawable = null;
|
mCenterDrawable = null;
|
||||||
}
|
}
|
||||||
if (mIcon != null) {
|
if (info.iconBitmap != null) {
|
||||||
// The view displays three modes,
|
// The view displays three modes,
|
||||||
// 1) App icon in the center
|
// 1) App icon in the center
|
||||||
// 2) Preload icon in the center
|
// 2) Preload icon in the center
|
||||||
// 3) Setup icon in the center and app icon in the top right corner.
|
// 3) Setup icon in the center and app icon in the top right corner.
|
||||||
DrawableFactory drawableFactory = DrawableFactory.get(getContext());
|
DrawableFactory drawableFactory = DrawableFactory.get(getContext());
|
||||||
if (mDisabledForSafeMode) {
|
if (mDisabledForSafeMode) {
|
||||||
FastBitmapDrawable disabledIcon = drawableFactory.newIcon(mIcon, mInfo);
|
FastBitmapDrawable disabledIcon = drawableFactory.newIcon(info);
|
||||||
disabledIcon.setIsDisabled(true);
|
disabledIcon.setIsDisabled(true);
|
||||||
mCenterDrawable = disabledIcon;
|
mCenterDrawable = disabledIcon;
|
||||||
mSettingIconDrawable = null;
|
mSettingIconDrawable = null;
|
||||||
} else if (isReadyForClickSetup()) {
|
} else if (isReadyForClickSetup()) {
|
||||||
mCenterDrawable = drawableFactory.newIcon(mIcon, mInfo);
|
mCenterDrawable = drawableFactory.newIcon(info);
|
||||||
mSettingIconDrawable = getResources().getDrawable(R.drawable.ic_setting).mutate();
|
mSettingIconDrawable = getResources().getDrawable(R.drawable.ic_setting).mutate();
|
||||||
|
updateSettingColor(info.iconColor);
|
||||||
updateSettingColor();
|
|
||||||
} else {
|
} else {
|
||||||
mCenterDrawable = DrawableFactory.get(getContext())
|
mCenterDrawable = DrawableFactory.get(getContext())
|
||||||
.newPendingIcon(mIcon, getContext());
|
.newPendingIcon(info, getContext());
|
||||||
mCenterDrawable.setCallback(this);
|
|
||||||
mSettingIconDrawable = null;
|
mSettingIconDrawable = null;
|
||||||
applyState();
|
applyState();
|
||||||
}
|
}
|
||||||
|
mCenterDrawable.setCallback(this);
|
||||||
mDrawableSizeChanged = true;
|
mDrawableSizeChanged = true;
|
||||||
}
|
}
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateSettingColor() {
|
private void updateSettingColor(int dominantColor) {
|
||||||
int color = Utilities.findDominantColorByHue(mIcon, 20);
|
|
||||||
// Make the dominant color bright.
|
// Make the dominant color bright.
|
||||||
float[] hsv = new float[3];
|
float[] hsv = new float[3];
|
||||||
Color.colorToHSV(color, hsv);
|
Color.colorToHSV(dominantColor, hsv);
|
||||||
hsv[1] = Math.min(hsv[1], MIN_SATUNATION);
|
hsv[1] = Math.min(hsv[1], MIN_SATUNATION);
|
||||||
hsv[2] = 1;
|
hsv[2] = 1;
|
||||||
color = Color.HSVToColor(hsv);
|
mSettingIconDrawable.setColorFilter(Color.HSVToColor(hsv), PorterDuff.Mode.SRC_IN);
|
||||||
|
|
||||||
mSettingIconDrawable.setColorFilter(color, PorterDuff.Mode.SRC_IN);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -9,12 +9,12 @@ import android.content.pm.LauncherActivityInfo;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Bitmap.Config;
|
import android.graphics.Bitmap.Config;
|
||||||
|
import android.graphics.Color;
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.test.InstrumentationRegistry;
|
import android.support.test.InstrumentationRegistry;
|
||||||
import android.support.test.rule.provider.ProviderTestRule;
|
import android.support.test.rule.provider.ProviderTestRule;
|
||||||
import android.support.test.runner.AndroidJUnit4;
|
|
||||||
|
|
||||||
import com.android.launcher3.AllAppsList;
|
import com.android.launcher3.AllAppsList;
|
||||||
import com.android.launcher3.AppFilter;
|
import com.android.launcher3.AppFilter;
|
||||||
|
@ -27,6 +27,7 @@ import com.android.launcher3.LauncherModel;
|
||||||
import com.android.launcher3.LauncherModel.ModelUpdateTask;
|
import com.android.launcher3.LauncherModel.ModelUpdateTask;
|
||||||
import com.android.launcher3.LauncherModel.Callbacks;
|
import com.android.launcher3.LauncherModel.Callbacks;
|
||||||
import com.android.launcher3.LauncherProvider;
|
import com.android.launcher3.LauncherProvider;
|
||||||
|
import com.android.launcher3.graphics.BitmapInfo;
|
||||||
import com.android.launcher3.util.ComponentKey;
|
import com.android.launcher3.util.ComponentKey;
|
||||||
import com.android.launcher3.util.Provider;
|
import com.android.launcher3.util.Provider;
|
||||||
import com.android.launcher3.util.TestLauncherProvider;
|
import com.android.launcher3.util.TestLauncherProvider;
|
||||||
|
@ -208,7 +209,7 @@ public class BaseModelUpdateTaskTestCase {
|
||||||
CacheEntry entry = mCache.get(new ComponentKey(componentName, user));
|
CacheEntry entry = mCache.get(new ComponentKey(componentName, user));
|
||||||
if (entry == null) {
|
if (entry == null) {
|
||||||
entry = new CacheEntry();
|
entry = new CacheEntry();
|
||||||
entry.icon = getDefaultIcon(user);
|
getDefaultIcon(user).applyTo(entry);
|
||||||
}
|
}
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
@ -216,6 +217,7 @@ public class BaseModelUpdateTaskTestCase {
|
||||||
public void addCache(ComponentName key, String title) {
|
public void addCache(ComponentName key, String title) {
|
||||||
CacheEntry entry = new CacheEntry();
|
CacheEntry entry = new CacheEntry();
|
||||||
entry.icon = newIcon();
|
entry.icon = newIcon();
|
||||||
|
entry.color = Color.RED;
|
||||||
entry.title = title;
|
entry.title = title;
|
||||||
mCache.put(new ComponentKey(key, Process.myUserHandle()), entry);
|
mCache.put(new ComponentKey(key, Process.myUserHandle()), entry);
|
||||||
}
|
}
|
||||||
|
@ -225,8 +227,8 @@ public class BaseModelUpdateTaskTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Bitmap makeDefaultIcon(UserHandle user) {
|
protected BitmapInfo makeDefaultIcon(UserHandle user) {
|
||||||
return newIcon();
|
return BitmapInfo.fromBitmap(newIcon());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ import com.android.launcher3.LauncherAppState;
|
||||||
import com.android.launcher3.ShortcutInfo;
|
import com.android.launcher3.ShortcutInfo;
|
||||||
import com.android.launcher3.Utilities;
|
import com.android.launcher3.Utilities;
|
||||||
import com.android.launcher3.compat.LauncherAppsCompat;
|
import com.android.launcher3.compat.LauncherAppsCompat;
|
||||||
|
import com.android.launcher3.graphics.BitmapInfo;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -138,7 +139,8 @@ public class LoaderCursorTest {
|
||||||
assertTrue(mLoaderCursor.moveToNext());
|
assertTrue(mLoaderCursor.moveToNext());
|
||||||
|
|
||||||
Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8);
|
Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8);
|
||||||
when(mMockIconCache.getDefaultIcon(eq(mLoaderCursor.user))).thenReturn(icon);
|
when(mMockIconCache.getDefaultIcon(eq(mLoaderCursor.user)))
|
||||||
|
.thenReturn(BitmapInfo.fromBitmap(icon));
|
||||||
ShortcutInfo info = mLoaderCursor.loadSimpleShortcut();
|
ShortcutInfo info = mLoaderCursor.loadSimpleShortcut();
|
||||||
assertEquals(icon, info.iconBitmap);
|
assertEquals(icon, info.iconBitmap);
|
||||||
assertEquals("my-shortcut", info.title);
|
assertEquals("my-shortcut", info.title);
|
||||||
|
|
Loading…
Reference in New Issue