1 /* 2 * Copyright (C) 2008 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 17 package com.android.launcher3; 18 19 import android.annotation.TargetApi; 20 import android.content.ComponentName; 21 import android.content.ContentValues; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.graphics.Bitmap; 25 import android.graphics.drawable.Drawable; 26 import android.os.Build; 27 import android.text.TextUtils; 28 29 import com.android.launcher3.LauncherSettings.Favorites; 30 import com.android.launcher3.compat.LauncherActivityInfoCompat; 31 import com.android.launcher3.compat.UserHandleCompat; 32 import com.android.launcher3.compat.UserManagerCompat; 33 import com.android.launcher3.folder.FolderIcon; 34 import com.android.launcher3.shortcuts.ShortcutInfoCompat; 35 36 /** 37 * Represents a launchable icon on the workspaces and in folders. 38 */ 39 public class ShortcutInfo extends ItemInfo { 40 41 public static final int DEFAULT = 0; 42 43 /** 44 * The shortcut was restored from a backup and it not ready to be used. This is automatically 45 * set during backup/restore 46 */ 47 public static final int FLAG_RESTORED_ICON = 1; 48 49 /** 50 * The icon was added as an auto-install app, and is not ready to be used. This flag can't 51 * be present along with {@link #FLAG_RESTORED_ICON}, and is set during default layout 52 * parsing. 53 */ 54 public static final int FLAG_AUTOINTALL_ICON = 2; //0B10; 55 56 /** 57 * The icon is being installed. If {@link #FLAG_RESTORED_ICON} or {@link #FLAG_AUTOINTALL_ICON} 58 * is set, then the icon is either being installed or is in a broken state. 59 */ 60 public static final int FLAG_INSTALL_SESSION_ACTIVE = 4; // 0B100; 61 62 /** 63 * Indicates that the widget restore has started. 64 */ 65 public static final int FLAG_RESTORE_STARTED = 8; //0B1000; 66 67 /** 68 * Indicates if it represents a common type mentioned in {@link CommonAppTypeParser}. 69 * Upto 15 different types supported. 70 */ 71 public static final int FLAG_RESTORED_APP_TYPE = 0B0011110000; 72 73 /** 74 * The intent used to start the application. 75 */ 76 public Intent intent; 77 78 /** 79 * Indicates whether we're using the default fallback icon instead of something from the 80 * app. 81 */ 82 boolean usingFallbackIcon; 83 84 /** 85 * Indicates whether we're using a low res icon 86 */ 87 boolean usingLowResIcon; 88 89 /** 90 * If isShortcut=true and customIcon=false, this contains a reference to the 91 * shortcut icon as an application's resource. 92 */ 93 public Intent.ShortcutIconResource iconResource; 94 95 /** 96 * The application icon. 97 */ 98 private Bitmap mIcon; 99 100 /** 101 * Indicates that the icon is disabled due to safe mode restrictions. 102 */ 103 public static final int FLAG_DISABLED_SAFEMODE = 1 << 0; 104 105 /** 106 * Indicates that the icon is disabled as the app is not available. 107 */ 108 public static final int FLAG_DISABLED_NOT_AVAILABLE = 1 << 1; 109 110 /** 111 * Indicates that the icon is disabled as the app is suspended 112 */ 113 public static final int FLAG_DISABLED_SUSPENDED = 1 << 2; 114 115 /** 116 * Indicates that the icon is disabled as the user is in quiet mode. 117 */ 118 public static final int FLAG_DISABLED_QUIET_USER = 1 << 3; 119 120 /** 121 * Indicates that the icon is disabled as the publisher has disabled the actual shortcut. 122 */ 123 public static final int FLAG_DISABLED_BY_PUBLISHER = 1 << 4; 124 125 /** 126 * Indicates that the icon is disabled as the user partition is currently locked. 127 */ 128 public static final int FLAG_DISABLED_LOCKED_USER = 1 << 5; 129 130 /** 131 * Could be disabled, if the the app is installed but unavailable (eg. in safe mode or when 132 * sd-card is not available). 133 */ 134 int isDisabled = DEFAULT; 135 136 /** 137 * A message to display when the user tries to start a disabled shortcut. 138 * This is currently only used for deep shortcuts. 139 */ 140 CharSequence disabledMessage; 141 142 int status; 143 144 /** 145 * The installation progress [0-100] of the package that this shortcut represents. 146 */ 147 private int mInstallProgress; 148 149 /** 150 * TODO move this to {@link #status} 151 */ 152 int flags = 0; 153 154 /** 155 * If this shortcut is a placeholder, then intent will be a market intent for the package, and 156 * this will hold the original intent from the database. Otherwise, null. 157 * Refer {@link #FLAG_RESTORED_ICON}, {@link #FLAG_AUTOINTALL_ICON} 158 */ 159 Intent promisedIntent; 160 161 public ShortcutInfo() { 162 itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_SHORTCUT; 163 } 164 165 @Override 166 public Intent getIntent() { 167 return intent; 168 } 169 170 /** Returns {@link #promisedIntent}, or {@link #intent} if promisedIntent is null. */ 171 public Intent getPromisedIntent() { 172 return promisedIntent != null ? promisedIntent : intent; 173 } 174 175 ShortcutInfo(Intent intent, CharSequence title, CharSequence contentDescription, 176 Bitmap icon, UserHandleCompat user) { 177 this(); 178 this.intent = intent; 179 this.title = Utilities.trim(title); 180 this.contentDescription = contentDescription; 181 mIcon = icon; 182 this.user = user; 183 } 184 185 public ShortcutInfo(ShortcutInfo info) { 186 super(info); 187 title = info.title; 188 intent = new Intent(info.intent); 189 iconResource = info.iconResource; 190 mIcon = info.mIcon; // TODO: should make a copy here. maybe we don't need this ctor at all 191 flags = info.flags; 192 status = info.status; 193 mInstallProgress = info.mInstallProgress; 194 isDisabled = info.isDisabled; 195 usingFallbackIcon = info.usingFallbackIcon; 196 } 197 198 /** TODO: Remove this. It's only called by ApplicationInfo.makeShortcut. */ 199 public ShortcutInfo(AppInfo info) { 200 super(info); 201 title = Utilities.trim(info.title); 202 intent = new Intent(info.intent); 203 flags = info.flags; 204 isDisabled = info.isDisabled; 205 } 206 207 public ShortcutInfo(LauncherActivityInfoCompat info, Context context) { 208 user = info.getUser(); 209 title = Utilities.trim(info.getLabel()); 210 contentDescription = UserManagerCompat.getInstance(context) 211 .getBadgedLabelForUser(info.getLabel(), info.getUser()); 212 intent = AppInfo.makeLaunchIntent(context, info, info.getUser()); 213 itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; 214 flags = AppInfo.initFlags(info); 215 } 216 217 /** 218 * Creates a {@link ShortcutInfo} from a {@link ShortcutInfoCompat}. 219 */ 220 @TargetApi(Build.VERSION_CODES.N) 221 public ShortcutInfo(ShortcutInfoCompat shortcutInfo, Context context) { 222 user = shortcutInfo.getUserHandle(); 223 itemType = LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT; 224 flags = 0; 225 updateFromDeepShortcutInfo(shortcutInfo, context); 226 } 227 228 public void setIcon(Bitmap b) { 229 mIcon = b; 230 } 231 232 public Bitmap getIcon(IconCache iconCache) { 233 if (mIcon == null) { 234 updateIcon(iconCache); 235 } 236 return mIcon; 237 } 238 239 public void updateIcon(IconCache iconCache, boolean useLowRes) { 240 if (itemType == Favorites.ITEM_TYPE_APPLICATION) { 241 iconCache.getTitleAndIcon(this, promisedIntent != null ? promisedIntent : intent, user, 242 useLowRes); 243 } 244 } 245 246 public void updateIcon(IconCache iconCache) { 247 updateIcon(iconCache, shouldUseLowResIcon()); 248 } 249 250 @Override 251 void onAddToDatabase(Context context, ContentValues values) { 252 super.onAddToDatabase(context, values); 253 254 String titleStr = title != null ? title.toString() : null; 255 values.put(LauncherSettings.BaseLauncherColumns.TITLE, titleStr); 256 257 String uri = promisedIntent != null ? promisedIntent.toUri(0) 258 : (intent != null ? intent.toUri(0) : null); 259 values.put(LauncherSettings.BaseLauncherColumns.INTENT, uri); 260 values.put(LauncherSettings.Favorites.RESTORED, status); 261 262 if (!usingFallbackIcon && !usingLowResIcon) { 263 writeBitmap(values, mIcon); 264 } 265 if (iconResource != null) { 266 values.put(LauncherSettings.BaseLauncherColumns.ICON_PACKAGE, 267 iconResource.packageName); 268 values.put(LauncherSettings.BaseLauncherColumns.ICON_RESOURCE, 269 iconResource.resourceName); 270 } 271 } 272 273 public ComponentName getTargetComponent() { 274 return promisedIntent != null ? promisedIntent.getComponent() : intent.getComponent(); 275 } 276 277 public boolean hasStatusFlag(int flag) { 278 return (status & flag) != 0; 279 } 280 281 282 public final boolean isPromise() { 283 return hasStatusFlag(FLAG_RESTORED_ICON | FLAG_AUTOINTALL_ICON); 284 } 285 286 public int getInstallProgress() { 287 return mInstallProgress; 288 } 289 290 public void setInstallProgress(int progress) { 291 mInstallProgress = progress; 292 status |= FLAG_INSTALL_SESSION_ACTIVE; 293 } 294 295 public boolean shouldUseLowResIcon() { 296 return usingLowResIcon && container >= 0 && rank >= FolderIcon.NUM_ITEMS_IN_PREVIEW; 297 } 298 299 public void updateFromDeepShortcutInfo(ShortcutInfoCompat shortcutInfo, Context context) { 300 // {@link ShortcutInfoCompat#getActivity} can change during an update. Recreate the intent 301 intent = shortcutInfo.makeIntent(context); 302 title = shortcutInfo.getShortLabel(); 303 304 CharSequence label = shortcutInfo.getLongLabel(); 305 if (TextUtils.isEmpty(label)) { 306 label = shortcutInfo.getShortLabel(); 307 } 308 contentDescription = UserManagerCompat.getInstance(context) 309 .getBadgedLabelForUser(label, user); 310 if (shortcutInfo.isEnabled()) { 311 isDisabled &= ~FLAG_DISABLED_BY_PUBLISHER; 312 } else { 313 isDisabled |= FLAG_DISABLED_BY_PUBLISHER; 314 } 315 disabledMessage = shortcutInfo.getDisabledMessage(); 316 317 // TODO: Use cache for this 318 LauncherAppState launcherAppState = LauncherAppState.getInstance(); 319 Drawable unbadgedDrawable = launcherAppState.getShortcutManager() 320 .getShortcutIconDrawable(shortcutInfo, 321 launcherAppState.getInvariantDeviceProfile().fillResIconDpi); 322 323 IconCache cache = launcherAppState.getIconCache(); 324 Bitmap unbadgedBitmap = unbadgedDrawable == null 325 ? cache.getDefaultIcon(UserHandleCompat.myUserHandle()) 326 : Utilities.createScaledBitmapWithoutShadow(unbadgedDrawable, context); 327 setIcon(getBadgedIcon(unbadgedBitmap, shortcutInfo, cache, context)); 328 } 329 330 protected Bitmap getBadgedIcon(Bitmap unbadgedBitmap, ShortcutInfoCompat shortcutInfo, 331 IconCache cache, Context context) { 332 unbadgedBitmap = Utilities.addShadowToIcon(unbadgedBitmap); 333 // Get the app info for the source activity. 334 AppInfo appInfo = new AppInfo(); 335 appInfo.user = user; 336 appInfo.componentName = shortcutInfo.getActivity(); 337 try { 338 cache.getTitleAndIcon(appInfo, shortcutInfo.getActivityInfo(context), false); 339 } catch (NullPointerException e) { 340 // This may happen when we fail to load the activity info. Worst case ignore badging. 341 return Utilities.badgeIconForUser(unbadgedBitmap, user, context); 342 } 343 return Utilities.badgeWithBitmap(unbadgedBitmap, appInfo.iconBitmap, context); 344 } 345 346 /** Returns the ShortcutInfo id associated with the deep shortcut. */ 347 public String getDeepShortcutId() { 348 return itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT ? 349 getPromisedIntent().getStringExtra(ShortcutInfoCompat.EXTRA_SHORTCUT_ID) : null; 350 } 351 352 @Override 353 public boolean isDisabled() { 354 return isDisabled != 0; 355 } 356 } 357