1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.launcher3.icons; 17 18 import static com.android.launcher3.icons.cache.CacheLookupFlag.DEFAULT_LOOKUP_FLAG; 19 20 import android.content.Context; 21 import android.graphics.Bitmap; 22 import android.graphics.Bitmap.Config; 23 import android.graphics.Canvas; 24 import android.graphics.Path; 25 import android.graphics.drawable.Drawable; 26 27 import androidx.annotation.IntDef; 28 import androidx.annotation.NonNull; 29 import androidx.annotation.Nullable; 30 31 import com.android.launcher3.icons.cache.CacheLookupFlag; 32 import com.android.launcher3.util.FlagOp; 33 34 public class BitmapInfo { 35 36 public static final int FLAG_WORK = 1 << 0; 37 public static final int FLAG_INSTANT = 1 << 1; 38 public static final int FLAG_CLONE = 1 << 2; 39 public static final int FLAG_PRIVATE = 1 << 3; 40 @IntDef(flag = true, value = { 41 FLAG_WORK, 42 FLAG_INSTANT, 43 FLAG_CLONE, 44 FLAG_PRIVATE 45 }) 46 @interface BitmapInfoFlags {} 47 48 public static final int FLAG_THEMED = 1 << 0; 49 public static final int FLAG_NO_BADGE = 1 << 1; 50 public static final int FLAG_SKIP_USER_BADGE = 1 << 2; 51 @IntDef(flag = true, value = { 52 FLAG_THEMED, 53 FLAG_NO_BADGE, 54 FLAG_SKIP_USER_BADGE, 55 }) 56 public @interface DrawableCreationFlags {} 57 58 public static final Bitmap LOW_RES_ICON = Bitmap.createBitmap(1, 1, Config.ALPHA_8); 59 public static final BitmapInfo LOW_RES_INFO = fromBitmap(LOW_RES_ICON); 60 61 public static final String TAG = "BitmapInfo"; 62 63 @NonNull 64 public final Bitmap icon; 65 public final int color; 66 67 @Nullable 68 private ThemedBitmap mThemedBitmap; 69 70 public @BitmapInfoFlags int flags; 71 72 // b/377618519: These are saved to debug why work badges sometimes don't show up on work apps 73 public @DrawableCreationFlags int creationFlags; 74 75 private BitmapInfo badgeInfo; 76 BitmapInfo(@onNull Bitmap icon, int color)77 public BitmapInfo(@NonNull Bitmap icon, int color) { 78 this.icon = icon; 79 this.color = color; 80 } 81 withBadgeInfo(BitmapInfo badgeInfo)82 public BitmapInfo withBadgeInfo(BitmapInfo badgeInfo) { 83 BitmapInfo result = clone(); 84 result.badgeInfo = badgeInfo; 85 return result; 86 } 87 88 /** 89 * Returns a bitmapInfo with the flagOP applied 90 */ withFlags(@onNull FlagOp op)91 public BitmapInfo withFlags(@NonNull FlagOp op) { 92 if (op == FlagOp.NO_OP) { 93 return this; 94 } 95 BitmapInfo result = clone(); 96 result.flags = op.apply(result.flags); 97 return result; 98 } 99 copyInternalsTo(BitmapInfo target)100 protected BitmapInfo copyInternalsTo(BitmapInfo target) { 101 target.mThemedBitmap = mThemedBitmap; 102 target.flags = flags; 103 target.badgeInfo = badgeInfo; 104 return target; 105 } 106 107 @Override clone()108 public BitmapInfo clone() { 109 return copyInternalsTo(new BitmapInfo(icon, color)); 110 } 111 setThemedBitmap(@ullable ThemedBitmap themedBitmap)112 public void setThemedBitmap(@Nullable ThemedBitmap themedBitmap) { 113 mThemedBitmap = themedBitmap; 114 } 115 116 @Nullable getThemedBitmap()117 public ThemedBitmap getThemedBitmap() { 118 return mThemedBitmap; 119 } 120 121 /** 122 * Ideally icon should not be null, except in cases when generating hardware bitmap failed 123 */ isNullOrLowRes()124 public final boolean isNullOrLowRes() { 125 return icon == null || icon == LOW_RES_ICON; 126 } 127 isLowRes()128 public final boolean isLowRes() { 129 return LOW_RES_ICON == icon; 130 } 131 132 /** 133 * Returns the lookup flag to match this current state of this info 134 */ getMatchingLookupFlag()135 public CacheLookupFlag getMatchingLookupFlag() { 136 return DEFAULT_LOOKUP_FLAG.withUseLowRes(isLowRes()); 137 } 138 139 /** 140 * BitmapInfo can be stored on disk or other persistent storage 141 */ canPersist()142 public boolean canPersist() { 143 return !isNullOrLowRes(); 144 } 145 146 /** 147 * Creates a drawable for the provided BitmapInfo 148 */ newIcon(Context context)149 public FastBitmapDrawable newIcon(Context context) { 150 return newIcon(context, 0); 151 } 152 153 /** 154 * Creates a drawable for the provided BitmapInfo 155 */ newIcon(Context context, @DrawableCreationFlags int creationFlags)156 public FastBitmapDrawable newIcon(Context context, @DrawableCreationFlags int creationFlags) { 157 return newIcon(context, creationFlags, null); 158 } 159 160 /** 161 * Creates a drawable for the provided BitmapInfo 162 * 163 * @param context Context 164 * @param creationFlags Flags for creating the FastBitmapDrawable 165 * @param badgeShape Optional Path for masking icon badges to a shape. Should be 100x100. 166 * @return FastBitmapDrawable 167 */ newIcon(Context context, @DrawableCreationFlags int creationFlags, @Nullable Path badgeShape)168 public FastBitmapDrawable newIcon(Context context, @DrawableCreationFlags int creationFlags, 169 @Nullable Path badgeShape) { 170 FastBitmapDrawable drawable; 171 if (isLowRes()) { 172 drawable = new PlaceHolderIconDrawable(this, context); 173 } else if ((creationFlags & FLAG_THEMED) != 0 && mThemedBitmap != null) { 174 drawable = mThemedBitmap.newDrawable(this, context); 175 } else { 176 drawable = new FastBitmapDrawable(this); 177 } 178 applyFlags(context, drawable, creationFlags, badgeShape); 179 return drawable; 180 } 181 applyFlags(Context context, FastBitmapDrawable drawable, @DrawableCreationFlags int creationFlags, @Nullable Path badgeShape)182 protected void applyFlags(Context context, FastBitmapDrawable drawable, 183 @DrawableCreationFlags int creationFlags, @Nullable Path badgeShape) { 184 this.creationFlags = creationFlags; 185 drawable.mDisabledAlpha = GraphicsUtils.getFloat(context, R.attr.disabledIconAlpha, 1f); 186 drawable.mCreationFlags = creationFlags; 187 if ((creationFlags & FLAG_NO_BADGE) == 0) { 188 Drawable badge = getBadgeDrawable(context, (creationFlags & FLAG_THEMED) != 0, 189 (creationFlags & FLAG_SKIP_USER_BADGE) != 0, badgeShape); 190 if (badge != null) { 191 drawable.setBadge(badge); 192 } 193 } 194 } 195 196 /** 197 * Gets Badge drawable based on current flags 198 * @param context Context 199 * @param isThemed If Drawable is themed. 200 * @param badgeShape Optional Path to mask badges to a shape. Should be 100x100. 201 * @return Drawable for the badge. 202 */ getBadgeDrawable(Context context, boolean isThemed, @Nullable Path badgeShape)203 public Drawable getBadgeDrawable(Context context, boolean isThemed, @Nullable Path badgeShape) { 204 return getBadgeDrawable(context, isThemed, false, badgeShape); 205 } 206 207 208 /** 209 * Creates a Drawable for an icon badge for this BitmapInfo 210 * @param context Context 211 * @param isThemed If the drawable is themed. 212 * @param skipUserBadge If should skip User Profile badging. 213 * @param badgeShape Optional Path to mask badge Drawable to a shape. Should be 100x100. 214 * @return Drawable for an icon Badge. 215 */ 216 @Nullable getBadgeDrawable(Context context, boolean isThemed, boolean skipUserBadge, @Nullable Path badgeShape)217 private Drawable getBadgeDrawable(Context context, boolean isThemed, boolean skipUserBadge, 218 @Nullable Path badgeShape) { 219 if (badgeInfo != null) { 220 int creationFlag = isThemed ? FLAG_THEMED : 0; 221 if (skipUserBadge) { 222 creationFlag |= FLAG_SKIP_USER_BADGE; 223 } 224 return badgeInfo.newIcon(context, creationFlag, badgeShape); 225 } 226 if (skipUserBadge) { 227 return null; 228 } else if ((flags & FLAG_INSTANT) != 0) { 229 return new UserBadgeDrawable(context, R.drawable.ic_instant_app_badge, 230 R.color.badge_tint_instant, isThemed, badgeShape); 231 } else if ((flags & FLAG_WORK) != 0) { 232 return new UserBadgeDrawable(context, R.drawable.ic_work_app_badge, 233 R.color.badge_tint_work, isThemed, badgeShape); 234 } else if ((flags & FLAG_CLONE) != 0) { 235 return new UserBadgeDrawable(context, R.drawable.ic_clone_app_badge, 236 R.color.badge_tint_clone, isThemed, badgeShape); 237 } else if ((flags & FLAG_PRIVATE) != 0) { 238 return new UserBadgeDrawable(context, R.drawable.ic_private_profile_app_badge, 239 R.color.badge_tint_private, isThemed, badgeShape); 240 } 241 return null; 242 } 243 fromBitmap(@onNull Bitmap bitmap)244 public static BitmapInfo fromBitmap(@NonNull Bitmap bitmap) { 245 return of(bitmap, 0); 246 } 247 of(@onNull Bitmap bitmap, int color)248 public static BitmapInfo of(@NonNull Bitmap bitmap, int color) { 249 return new BitmapInfo(bitmap, color); 250 } 251 252 /** 253 * Interface to be implemented by drawables to provide a custom BitmapInfo 254 */ 255 public interface Extender { 256 257 /** 258 * Called for creating a custom BitmapInfo 259 */ getExtendedInfo(Bitmap bitmap, int color, BaseIconFactory iconFactory, float normalizationScale)260 BitmapInfo getExtendedInfo(Bitmap bitmap, int color, 261 BaseIconFactory iconFactory, float normalizationScale); 262 263 /** 264 * Called to draw the UI independent of any runtime configurations like time or theme 265 */ drawForPersistence(Canvas canvas)266 void drawForPersistence(Canvas canvas); 267 } 268 } 269