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 17 package com.android.launcher3.model.data; 18 19 import static com.android.launcher3.icons.BitmapInfo.FLAG_THEMED; 20 21 import android.content.Context; 22 import android.content.Intent; 23 import android.os.Process; 24 25 import androidx.annotation.NonNull; 26 import androidx.annotation.Nullable; 27 28 import com.android.launcher3.Flags; 29 import com.android.launcher3.Utilities; 30 import com.android.launcher3.graphics.ThemeManager; 31 import com.android.launcher3.icons.BitmapInfo; 32 import com.android.launcher3.icons.BitmapInfo.DrawableCreationFlags; 33 import com.android.launcher3.icons.FastBitmapDrawable; 34 import com.android.launcher3.icons.cache.CacheLookupFlag; 35 import com.android.launcher3.logging.FileLog; 36 import com.android.launcher3.pm.PackageInstallInfo; 37 import com.android.launcher3.util.ApiWrapper; 38 39 /** 40 * Represents an ItemInfo which also holds an icon. 41 */ 42 public abstract class ItemInfoWithIcon extends ItemInfo { 43 44 public static final String TAG = "ItemInfoDebug"; 45 46 /** 47 * The bitmap for the application icon 48 */ 49 @NonNull 50 public BitmapInfo bitmap = BitmapInfo.LOW_RES_INFO; 51 52 /** 53 * Indicates that the icon is disabled due to safe mode restrictions. 54 */ 55 public static final int FLAG_DISABLED_SAFEMODE = 1 << 0; 56 57 /** 58 * Indicates that the icon is disabled as the app is not available. 59 */ 60 public static final int FLAG_DISABLED_NOT_AVAILABLE = 1 << 1; 61 62 /** 63 * Indicates that the icon is disabled as the app is suspended 64 */ 65 public static final int FLAG_DISABLED_SUSPENDED = 1 << 2; 66 67 /** 68 * Indicates that the icon is disabled as the user is in quiet mode. 69 */ 70 public static final int FLAG_DISABLED_QUIET_USER = 1 << 3; 71 72 /** 73 * Indicates that the icon is disabled as the publisher has disabled the actual shortcut. 74 */ 75 public static final int FLAG_DISABLED_BY_PUBLISHER = 1 << 4; 76 77 /** 78 * Indicates that the icon is disabled as the user partition is currently locked. 79 */ 80 public static final int FLAG_DISABLED_LOCKED_USER = 1 << 5; 81 82 /** 83 * The item points to a system app. 84 */ 85 public static final int FLAG_SYSTEM_YES = 1 << 6; 86 87 /** 88 * The item points to a non system app. 89 */ 90 public static final int FLAG_SYSTEM_NO = 1 << 7; 91 92 public static final int FLAG_SYSTEM_MASK = FLAG_SYSTEM_YES | FLAG_SYSTEM_NO; 93 94 /** 95 * The icon is being installed. If {@link WorkspaceItemInfo#FLAG_RESTORED_ICON} or 96 * {@link WorkspaceItemInfo#FLAG_AUTOINSTALL_ICON} is set, then the icon is either being 97 * installed or is in a broken state. 98 */ 99 public static final int FLAG_INSTALL_SESSION_ACTIVE = 1 << 10; 100 101 /** 102 * This icon is still being downloaded. 103 */ 104 public static final int FLAG_INCREMENTAL_DOWNLOAD_ACTIVE = 1 << 11; 105 106 public static final int FLAG_SHOW_DOWNLOAD_PROGRESS_MASK = FLAG_INSTALL_SESSION_ACTIVE 107 | FLAG_INCREMENTAL_DOWNLOAD_ACTIVE; 108 109 /** 110 * Indicates that the icon is a disabled shortcut and application updates are required. 111 */ 112 public static final int FLAG_DISABLED_VERSION_LOWER = 1 << 12; 113 114 public static final int FLAG_DISABLED_MASK = FLAG_DISABLED_SAFEMODE 115 | FLAG_DISABLED_NOT_AVAILABLE | FLAG_DISABLED_SUSPENDED 116 | FLAG_DISABLED_QUIET_USER | FLAG_DISABLED_BY_PUBLISHER | FLAG_DISABLED_LOCKED_USER 117 | FLAG_DISABLED_VERSION_LOWER; 118 119 /** 120 * Flag indicating this item can't be pinned to home screen. 121 */ 122 public static final int FLAG_NOT_PINNABLE = 1 << 13; 123 124 /** 125 * Flag indicating whether the package related to the item & user corresponds to that of 126 * archived app. 127 */ 128 public static final int FLAG_ARCHIVED = 1 << 14; 129 130 /** 131 * Flag indicating whether the package related to the item & user does not support resizing. 132 */ 133 public static final int FLAG_NOT_RESIZEABLE = 1 << 15; 134 135 /** 136 * Flag indicating whether the package related to the item & user supports multiple instances. 137 */ 138 public static final int FLAG_SUPPORTS_MULTI_INSTANCE = 1 << 16; 139 140 /** 141 * Status associated with the system state of the underlying item. This is calculated every 142 * time a new info is created and not persisted on the disk. 143 */ 144 public int runtimeStatusFlags = 0; 145 146 /** 147 * The download progress of the package that this shortcut represents. For legacy apps, this 148 * will always be the installation progress. For apps that support incremental downloads, this 149 * will only match be the installation progress until the app is installed, then this will the 150 * total download progress. 151 */ 152 private int mProgressLevel = 100; 153 ItemInfoWithIcon()154 protected ItemInfoWithIcon() { 155 } 156 ItemInfoWithIcon(ItemInfoWithIcon info)157 protected ItemInfoWithIcon(ItemInfoWithIcon info) { 158 super(info); 159 bitmap = info.bitmap; 160 mProgressLevel = info.mProgressLevel; 161 runtimeStatusFlags = info.runtimeStatusFlags; 162 user = info.user; 163 } 164 165 @Override isDisabled()166 public boolean isDisabled() { 167 return (runtimeStatusFlags & FLAG_DISABLED_MASK) != 0; 168 } 169 170 /** 171 * @return {@code true} if the app is pending download (0 progress) or if the app is archived 172 * and its install session is active 173 */ isPendingDownload()174 public boolean isPendingDownload() { 175 return getProgressLevel() == 0; 176 } 177 178 /** 179 * Returns true if the app corresponding to the item is archived. 180 */ isArchived()181 public boolean isArchived() { 182 if (!Flags.enableSupportForArchiving()) { 183 return false; 184 } 185 return (runtimeStatusFlags & FLAG_ARCHIVED) != 0; 186 } 187 188 /** Returns true if the app is archived and doesn't have an active install session. */ isInactiveArchive()189 public boolean isInactiveArchive() { 190 return isArchived() && (runtimeStatusFlags & FLAG_INSTALL_SESSION_ACTIVE) == 0; 191 } 192 193 /** 194 * Returns the lookup flag to match this current state of this info 195 */ getMatchingLookupFlag()196 public CacheLookupFlag getMatchingLookupFlag() { 197 return bitmap.getMatchingLookupFlag(); 198 } 199 200 /** 201 * Returns whether the app this shortcut represents is able to be started. For legacy apps, 202 * this returns whether it is fully installed. For apps that support incremental downloads, 203 * this returns whether the app is either fully downloaded or has installed and is downloading 204 * incrementally. 205 */ isAppStartable()206 public boolean isAppStartable() { 207 return ((runtimeStatusFlags & FLAG_INSTALL_SESSION_ACTIVE) == 0) 208 && (((runtimeStatusFlags & FLAG_INCREMENTAL_DOWNLOAD_ACTIVE) != 0) 209 || mProgressLevel == 100 || isArchived()); 210 } 211 212 /** 213 * Returns the download progress for the app this shortcut represents. If this app is not yet 214 * installed or does not support incremental downloads, this will return the installation 215 * progress. 216 */ getProgressLevel()217 public int getProgressLevel() { 218 if (((runtimeStatusFlags & FLAG_SHOW_DOWNLOAD_PROGRESS_MASK) != 0) 219 // This condition for archived apps is so that in case unarchival/update of 220 // archived app is cancelled, the state transitions back to 0% installed state. 221 || isArchived()) { 222 return mProgressLevel; 223 } 224 return 100; 225 } 226 227 /** 228 * Sets the download progress for the app this shortcut represents. If this app is not yet 229 * installed or does not support incremental downloads, this will set 230 * {@code FLAG_INSTALL_SESSION_ACTIVE}. If this app is downloading incrementally, this will 231 * set {@code FLAG_INCREMENTAL_DOWNLOAD_ACTIVE}. Otherwise, this will remove both flags. 232 */ setProgressLevel(PackageInstallInfo installInfo)233 public void setProgressLevel(PackageInstallInfo installInfo) { 234 setProgressLevel(installInfo.progress, installInfo.state); 235 236 if (installInfo.state == PackageInstallInfo.STATUS_FAILED) { 237 FileLog.d(TAG, 238 "Icon info: " + this + " marked broken with install info: " + installInfo, 239 new Exception()); 240 } 241 } 242 243 /** 244 * Sets the download progress for the app this shortcut represents. 245 */ setProgressLevel(int progress, int status)246 public void setProgressLevel(int progress, int status) { 247 if (status == PackageInstallInfo.STATUS_INSTALLING) { 248 mProgressLevel = progress; 249 runtimeStatusFlags = progress < 100 250 ? runtimeStatusFlags | FLAG_INSTALL_SESSION_ACTIVE 251 : runtimeStatusFlags & ~FLAG_INSTALL_SESSION_ACTIVE; 252 } else if (status == PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING) { 253 mProgressLevel = progress; 254 runtimeStatusFlags = runtimeStatusFlags & ~FLAG_INSTALL_SESSION_ACTIVE; 255 runtimeStatusFlags = progress < 100 256 ? runtimeStatusFlags | FLAG_INCREMENTAL_DOWNLOAD_ACTIVE 257 : runtimeStatusFlags & ~FLAG_INCREMENTAL_DOWNLOAD_ACTIVE; 258 } else { 259 mProgressLevel = status == PackageInstallInfo.STATUS_INSTALLED ? 100 : 0; 260 runtimeStatusFlags &= ~FLAG_INSTALL_SESSION_ACTIVE; 261 runtimeStatusFlags &= ~FLAG_INCREMENTAL_DOWNLOAD_ACTIVE; 262 } 263 } 264 265 /** 266 * Sets whether this app info supports multi-instance. 267 */ 268 protected void setSupportsMultiInstance(boolean supportsMultiInstance) { 269 if (supportsMultiInstance) { 270 runtimeStatusFlags |= FLAG_SUPPORTS_MULTI_INSTANCE; 271 } else { 272 runtimeStatusFlags &= ~FLAG_SUPPORTS_MULTI_INSTANCE; 273 } 274 } 275 276 /** 277 * Returns whether this app info supports multi-instance. 278 */ 279 public boolean supportsMultiInstance() { 280 return (runtimeStatusFlags & FLAG_SUPPORTS_MULTI_INSTANCE) != 0; 281 } 282 283 /** 284 * Sets whether this app info is non-resizeable. 285 */ 286 public void setNonResizeable(boolean nonResizeable) { 287 if (nonResizeable) { 288 runtimeStatusFlags |= FLAG_NOT_RESIZEABLE; 289 } else { 290 runtimeStatusFlags &= ~FLAG_NOT_RESIZEABLE; 291 } 292 } 293 294 /** 295 * Returns whether this app info is resizeable. 296 */ 297 public boolean isNonResizeable() { 298 return (runtimeStatusFlags & FLAG_NOT_RESIZEABLE) != 0; 299 } 300 301 /** Creates an intent to that launches the app store at this app's page. */ 302 @Nullable 303 public Intent getMarketIntent(Context context) { 304 String targetPackage = getTargetPackage(); 305 306 return targetPackage != null 307 ? ApiWrapper.INSTANCE.get(context).getAppMarketActivityIntent( 308 targetPackage, Process.myUserHandle()) 309 : null; 310 } 311 312 /** 313 * @return a copy of this 314 */ 315 public abstract ItemInfoWithIcon clone(); 316 317 318 /** 319 * Returns a FastBitmapDrawable with the icon. 320 */ 321 public FastBitmapDrawable newIcon(Context context) { 322 return newIcon(context, 0); 323 } 324 325 /** 326 * Returns a FastBitmapDrawable with the icon and context theme applied 327 */ 328 public FastBitmapDrawable newIcon(Context context, @DrawableCreationFlags int creationFlags) { 329 ThemeManager themeManager = ThemeManager.INSTANCE.get(context); 330 if (!themeManager.isIconThemeEnabled()) { 331 creationFlags &= ~FLAG_THEMED; 332 } 333 FastBitmapDrawable drawable = bitmap.newIcon( 334 context, creationFlags, Utilities.getIconShapeOrNull(context)); 335 drawable.setIsDisabled(isDisabled()); 336 return drawable; 337 } 338 339 @Override 340 protected String dumpProperties() { 341 return super.dumpProperties() 342 + " supportsMultiInstance=" + supportsMultiInstance() 343 + " nonResizeable=" + isNonResizeable(); 344 } 345 } 346