diff --git a/src/com/android/launcher3/AllAppsList.java b/src/com/android/launcher3/AllAppsList.java index 3d60605459..733f29540f 100644 --- a/src/com/android/launcher3/AllAppsList.java +++ b/src/com/android/launcher3/AllAppsList.java @@ -26,6 +26,7 @@ import android.util.Log; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.PackageInstallerCompat; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.util.FlagOp; import com.android.launcher3.util.ItemInfoMatcher; diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 5d401433bf..8e6703cc5a 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -40,8 +40,8 @@ import android.view.ViewConfiguration; import android.view.ViewDebug; import android.widget.TextView; -import com.android.launcher3.IconCache.IconLoadRequest; -import com.android.launcher3.IconCache.ItemInfoUpdateReceiver; +import com.android.launcher3.icons.IconCache.IconLoadRequest; +import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver; import com.android.launcher3.Launcher.OnResumeCallback; import com.android.launcher3.badge.BadgeInfo; import com.android.launcher3.badge.BadgeRenderer; diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 151c76183c..bbe5b78f4c 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -87,6 +87,7 @@ import com.android.launcher3.dragndrop.DragView; import com.android.launcher3.folder.Folder; import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.folder.FolderIconPreviewVerifier; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.keyboard.CustomActionsPopup; import com.android.launcher3.keyboard.ViewGroupFocusHelper; import com.android.launcher3.logging.FileLog; diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index 5159de17af..b02182c571 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -29,6 +29,7 @@ import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.PackageInstallerCompat; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.notification.NotificationListener; import com.android.launcher3.util.MainThreadInitializedObject; import com.android.launcher3.util.Preconditions; diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 7a90a55d45..df1c693382 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -39,6 +39,7 @@ import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.graphics.LauncherIcons; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.model.AddWorkspaceItemsTask; import com.android.launcher3.model.BaseModelUpdateTask; import com.android.launcher3.model.BgDataModel; diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java index a84bfd54ec..7717d91883 100644 --- a/src/com/android/launcher3/ShortcutInfo.java +++ b/src/com/android/launcher3/ShortcutInfo.java @@ -25,6 +25,7 @@ import android.text.TextUtils; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.compat.UserManagerCompat; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.shortcuts.ShortcutInfoCompat; import com.android.launcher3.util.ContentWriter; diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java index 84bad08583..cf497fd876 100644 --- a/src/com/android/launcher3/WidgetPreviewLoader.java +++ b/src/com/android/launcher3/WidgetPreviewLoader.java @@ -33,6 +33,7 @@ import com.android.launcher3.compat.ShortcutConfigActivityInfo; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.graphics.LauncherIcons; import com.android.launcher3.graphics.ShadowGenerator; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.model.WidgetItem; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.PackageUserKey; diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java index dd17916f0d..fe7b4e5d0d 100644 --- a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java +++ b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java @@ -27,7 +27,7 @@ import android.os.UserHandle; import android.text.TextUtils; import android.util.SparseArray; -import com.android.launcher3.IconCache; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherModel; import com.android.launcher3.config.FeatureFlags; diff --git a/src/com/android/launcher3/compat/ShortcutConfigActivityInfo.java b/src/com/android/launcher3/compat/ShortcutConfigActivityInfo.java index 31c0087cb0..d260e24e7e 100644 --- a/src/com/android/launcher3/compat/ShortcutConfigActivityInfo.java +++ b/src/com/android/launcher3/compat/ShortcutConfigActivityInfo.java @@ -32,7 +32,7 @@ import android.os.UserHandle; import android.util.Log; import android.widget.Toast; -import com.android.launcher3.IconCache; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.LauncherSettings; import com.android.launcher3.R; import com.android.launcher3.ShortcutInfo; diff --git a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java index 52167bb8bf..bd919bcbcb 100644 --- a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java +++ b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java @@ -28,7 +28,7 @@ import android.os.Build; import android.os.Process; import com.android.launcher3.FastBitmapDrawable; -import com.android.launcher3.IconCache; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.LauncherAnimUtils; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherSettings; diff --git a/src/com/android/launcher3/graphics/LauncherIcons.java b/src/com/android/launcher3/graphics/LauncherIcons.java index 087362cd42..7db67c9828 100644 --- a/src/com/android/launcher3/graphics/LauncherIcons.java +++ b/src/com/android/launcher3/graphics/LauncherIcons.java @@ -44,7 +44,7 @@ import android.os.UserHandle; import com.android.launcher3.AppInfo; import com.android.launcher3.FastBitmapDrawable; -import com.android.launcher3.IconCache; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.ItemInfoWithIcon; import com.android.launcher3.LauncherAppState; diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/icons/IconCache.java similarity index 74% rename from src/com/android/launcher3/IconCache.java rename to src/com/android/launcher3/icons/IconCache.java index 61fb3e38bf..2035d0e57d 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/icons/IconCache.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.launcher3; +package com.android.launcher3.icons; import static com.android.launcher3.graphics.BitmapInfo.LOW_RES_ICON; @@ -38,11 +38,19 @@ import android.graphics.drawable.Drawable; import android.os.Build.VERSION; import android.os.Handler; import android.os.Process; -import android.os.SystemClock; import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; +import com.android.launcher3.AppInfo; +import com.android.launcher3.IconProvider; +import com.android.launcher3.InvariantDeviceProfile; +import com.android.launcher3.ItemInfoWithIcon; +import com.android.launcher3.LauncherFiles; +import com.android.launcher3.LauncherModel; +import com.android.launcher3.MainThreadExecutor; +import com.android.launcher3.ShortcutInfo; +import com.android.launcher3.Utilities; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.graphics.BitmapInfo; @@ -54,14 +62,9 @@ import com.android.launcher3.util.InstantAppResolver; import com.android.launcher3.util.Preconditions; import com.android.launcher3.util.Provider; import com.android.launcher3.util.SQLiteCacheHelper; -import com.android.launcher3.util.Thunk; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.Stack; import androidx.annotation.NonNull; import androidx.core.graphics.ColorUtils; @@ -81,8 +84,6 @@ public class IconCache { private static final boolean DEBUG = false; private static final boolean DEBUG_IGNORE_CACHE = false; - @Thunk static final Object ICON_UPDATE_TOKEN = new Object(); - public static class CacheEntry extends BitmapInfo { public CharSequence title = ""; public CharSequence contentDescription = ""; @@ -93,20 +94,21 @@ public class IconCache { } private final HashMap mDefaultIcons = new HashMap<>(); - @Thunk final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor(); - private final Context mContext; - private final PackageManager mPackageManager; - private final IconProvider mIconProvider; - @Thunk final UserManagerCompat mUserManager; - private final LauncherAppsCompat mLauncherApps; + final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor(); + final Context mContext; + final PackageManager mPackageManager; + final IconProvider mIconProvider; + final UserManagerCompat mUserManager; + final LauncherAppsCompat mLauncherApps; + private final HashMap mCache = new HashMap<>(INITIAL_ICON_CACHE_CAPACITY); private final InstantAppResolver mInstantAppResolver; private final int mIconDpi; - @Thunk final IconDB mIconDb; - @Thunk final Handler mWorkerHandler; + final IconDB mIconDb; + final Handler mWorkerHandler; private final BitmapFactory.Options mDecodeOptions; @@ -247,115 +249,9 @@ public class IconCache { new String[]{packageName + "/%", Long.toString(userSerial)}); } - public void updateDbIcons(Set ignorePackagesForMainUser) { - // Remove all active icon update tasks. - mWorkerHandler.removeCallbacksAndMessages(ICON_UPDATE_TOKEN); - + public IconCacheUpdateHandler getUpdateHandler() { mIconProvider.updateSystemStateString(mContext); - for (UserHandle user : mUserManager.getUserProfiles()) { - // Query for the set of apps - final List apps = mLauncherApps.getActivityList(null, user); - // Fail if we don't have any apps - // TODO: Fix this. Only fail for the current user. - if (apps == null || apps.isEmpty()) { - return; - } - - // Update icon cache. This happens in segments and {@link #onPackageIconsUpdated} - // is called by the icon cache when the job is complete. - updateDBIcons(user, apps, Process.myUserHandle().equals(user) - ? ignorePackagesForMainUser : Collections.emptySet()); - } - } - - /** - * Updates the persistent DB, such that only entries corresponding to {@param apps} remain in - * the DB and are updated. - * @return The set of packages for which icons have updated. - */ - private void updateDBIcons(UserHandle user, List apps, - Set ignorePackages) { - long userSerial = mUserManager.getSerialNumberForUser(user); - PackageManager pm = mContext.getPackageManager(); - HashMap pkgInfoMap = new HashMap<>(); - for (PackageInfo info : pm.getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES)) { - pkgInfoMap.put(info.packageName, info); - } - - HashMap componentMap = new HashMap<>(); - for (LauncherActivityInfo app : apps) { - componentMap.put(app.getComponentName(), app); - } - - HashSet itemsToRemove = new HashSet<>(); - Stack appsToUpdate = new Stack<>(); - - Cursor c = null; - try { - c = mIconDb.query( - new String[]{IconDB.COLUMN_ROWID, IconDB.COLUMN_COMPONENT, - IconDB.COLUMN_LAST_UPDATED, IconDB.COLUMN_VERSION, - IconDB.COLUMN_SYSTEM_STATE}, - IconDB.COLUMN_USER + " = ? ", - new String[]{Long.toString(userSerial)}); - - final int indexComponent = c.getColumnIndex(IconDB.COLUMN_COMPONENT); - final int indexLastUpdate = c.getColumnIndex(IconDB.COLUMN_LAST_UPDATED); - final int indexVersion = c.getColumnIndex(IconDB.COLUMN_VERSION); - final int rowIndex = c.getColumnIndex(IconDB.COLUMN_ROWID); - final int systemStateIndex = c.getColumnIndex(IconDB.COLUMN_SYSTEM_STATE); - - while (c.moveToNext()) { - String cn = c.getString(indexComponent); - ComponentName component = ComponentName.unflattenFromString(cn); - PackageInfo info = pkgInfoMap.get(component.getPackageName()); - if (info == null) { - if (!ignorePackages.contains(component.getPackageName())) { - remove(component, user); - itemsToRemove.add(c.getInt(rowIndex)); - } - continue; - } - if ((info.applicationInfo.flags & ApplicationInfo.FLAG_IS_DATA_ONLY) != 0) { - // Application is not present - continue; - } - - long updateTime = c.getLong(indexLastUpdate); - int version = c.getInt(indexVersion); - LauncherActivityInfo app = componentMap.remove(component); - if (version == info.versionCode && updateTime == info.lastUpdateTime && - TextUtils.equals(c.getString(systemStateIndex), - mIconProvider.getIconSystemState(info.packageName))) { - continue; - } - if (app == null) { - remove(component, user); - itemsToRemove.add(c.getInt(rowIndex)); - } else { - appsToUpdate.add(app); - } - } - } catch (SQLiteException e) { - Log.d(TAG, "Error reading icon cache", e); - // Continue updating whatever we have read so far - } finally { - if (c != null) { - c.close(); - } - } - if (!itemsToRemove.isEmpty()) { - mIconDb.delete( - Utilities.createDbSelectionQuery(IconDB.COLUMN_ROWID, itemsToRemove), null); - } - - // Insert remaining apps. - if (!componentMap.isEmpty() || !appsToUpdate.isEmpty()) { - Stack appsToAdd = new Stack<>(); - appsToAdd.addAll(componentMap.values()); - new SerializedIconUpdateTask(userSerial, pkgInfoMap, - appsToAdd, appsToUpdate).scheduleNext(); - } + return new IconCacheUpdateHandler(this); } /** @@ -363,8 +259,9 @@ public class IconCache { * @param replaceExisting if true, it will recreate the bitmap even if it already exists in * the memory. This is useful then the previous bitmap was created using * old data. + * package private */ - @Thunk synchronized void addIconToDBAndMemCache(LauncherActivityInfo app, + synchronized void addIconToDBAndMemCache(LauncherActivityInfo app, PackageInfo info, long userSerial, boolean replaceExisting) { final ComponentKey key = new ComponentKey(app.getComponentName(), app.getUser()); CacheEntry entry = null; @@ -744,81 +641,23 @@ public class IconCache { } } - /** - * A runnable that updates invalid icons and adds missing icons in the DB for the provided - * LauncherActivityInfo list. Items are updated/added one at a time, so that the - * worker thread doesn't get blocked. - */ - @Thunk class SerializedIconUpdateTask implements Runnable { - private final long mUserSerial; - private final HashMap mPkgInfoMap; - private final Stack mAppsToAdd; - private final Stack mAppsToUpdate; - private final HashSet mUpdatedPackages = new HashSet<>(); - - @Thunk SerializedIconUpdateTask(long userSerial, HashMap pkgInfoMap, - Stack appsToAdd, - Stack appsToUpdate) { - mUserSerial = userSerial; - mPkgInfoMap = pkgInfoMap; - mAppsToAdd = appsToAdd; - mAppsToUpdate = appsToUpdate; - } - - @Override - public void run() { - if (!mAppsToUpdate.isEmpty()) { - LauncherActivityInfo app = mAppsToUpdate.pop(); - String pkg = app.getComponentName().getPackageName(); - PackageInfo info = mPkgInfoMap.get(pkg); - addIconToDBAndMemCache(app, info, mUserSerial, true /*replace existing*/); - mUpdatedPackages.add(pkg); - - if (mAppsToUpdate.isEmpty() && !mUpdatedPackages.isEmpty()) { - // No more app to update. Notify model. - LauncherAppState.getInstance(mContext).getModel().onPackageIconsUpdated( - mUpdatedPackages, mUserManager.getUserForSerialNumber(mUserSerial)); - } - - // Let it run one more time. - scheduleNext(); - } else if (!mAppsToAdd.isEmpty()) { - LauncherActivityInfo app = mAppsToAdd.pop(); - PackageInfo info = mPkgInfoMap.get(app.getComponentName().getPackageName()); - // We do not check the mPkgInfoMap when generating the mAppsToAdd. Although every - // app should have package info, this is not guaranteed by the api - if (info != null) { - addIconToDBAndMemCache(app, info, mUserSerial, false /*replace existing*/); - } - - if (!mAppsToAdd.isEmpty()) { - scheduleNext(); - } - } - } - - public void scheduleNext() { - mWorkerHandler.postAtTime(this, ICON_UPDATE_TOKEN, SystemClock.uptimeMillis() + 1); - } - } - - private static final class IconDB extends SQLiteCacheHelper { + static final class IconDB extends SQLiteCacheHelper { private final static int RELEASE_VERSION = 25; - private final static String TABLE_NAME = "icons"; - private final static String COLUMN_ROWID = "rowid"; - private final static String COLUMN_COMPONENT = "componentName"; - private final static String COLUMN_USER = "profileId"; - private final static String COLUMN_LAST_UPDATED = "lastUpdated"; - private final static String COLUMN_VERSION = "version"; - private final static String COLUMN_ICON = "icon"; - private final static String COLUMN_ICON_COLOR = "icon_color"; - private final static String COLUMN_LABEL = "label"; - private final static String COLUMN_SYSTEM_STATE = "system_state"; + public final static String TABLE_NAME = "icons"; + public final static String COLUMN_ROWID = "rowid"; + public final static String COLUMN_COMPONENT = "componentName"; + public final static String COLUMN_USER = "profileId"; + public final static String COLUMN_LAST_UPDATED = "lastUpdated"; + public final static String COLUMN_VERSION = "version"; + public final static String COLUMN_ICON = "icon"; + public final static String COLUMN_ICON_COLOR = "icon_color"; + public final static String COLUMN_LABEL = "label"; + public final static String COLUMN_SYSTEM_STATE = "system_state"; - private final static String[] COLUMNS_HIGH_RES = new String[] { + public final static String[] COLUMNS_HIGH_RES = new String[] { IconDB.COLUMN_ICON_COLOR, IconDB.COLUMN_LABEL, IconDB.COLUMN_ICON }; - private final static String[] COLUMNS_LOW_RES = new String[] { + public final static String[] COLUMNS_LOW_RES = new String[] { IconDB.COLUMN_ICON_COLOR, IconDB.COLUMN_LABEL }; public IconDB(Context context, int iconPixelSize) { diff --git a/src/com/android/launcher3/icons/IconCacheUpdateHandler.java b/src/com/android/launcher3/icons/IconCacheUpdateHandler.java new file mode 100644 index 0000000000..04e29013ab --- /dev/null +++ b/src/com/android/launcher3/icons/IconCacheUpdateHandler.java @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2018 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.icons; + +import android.content.ComponentName; +import android.content.pm.ApplicationInfo; +import android.content.pm.LauncherActivityInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.database.Cursor; +import android.database.sqlite.SQLiteException; +import android.os.SystemClock; +import android.os.UserHandle; +import android.text.TextUtils; +import android.util.Log; + +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.Utilities; +import com.android.launcher3.icons.IconCache.IconDB; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.Stack; + +/** + * Utility class to handle updating the Icon cache + */ +public class IconCacheUpdateHandler { + + private static final String TAG = "IconCacheUpdateHandler"; + + private static final Object ICON_UPDATE_TOKEN = new Object(); + + private final HashMap mPkgInfoMap; + private final IconCache mIconCache; + private final HashMap> mPackagesToIgnore = new HashMap<>(); + + IconCacheUpdateHandler(IconCache cache) { + mIconCache = cache; + + mPkgInfoMap = new HashMap<>(); + + // Remove all active icon update tasks. + mIconCache.mWorkerHandler.removeCallbacksAndMessages(ICON_UPDATE_TOKEN); + + createPackageInfoMap(); + } + + public void setPackagesToIgnore(UserHandle userHandle, Set packages) { + mPackagesToIgnore.put(userHandle, packages); + } + + private void createPackageInfoMap() { + PackageManager pm = mIconCache.mPackageManager; + HashMap pkgInfoMap = new HashMap<>(); + for (PackageInfo info : pm.getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES)) { + pkgInfoMap.put(info.packageName, info); + } + } + + /** + * Updates the persistent DB, such that only entries corresponding to {@param apps} remain in + * the DB and are updated. + * @return The set of packages for which icons have updated. + */ + public void updateIcons(List apps) { + if (apps.isEmpty()) { + return; + } + UserHandle user = apps.get(0).getUser(); + + Set ignorePackages = mPackagesToIgnore.get(user); + if (ignorePackages == null) { + ignorePackages = Collections.emptySet(); + } + + long userSerial = mIconCache.mUserManager.getSerialNumberForUser(user); + HashMap componentMap = new HashMap<>(); + for (LauncherActivityInfo app : apps) { + componentMap.put(app.getComponentName(), app); + } + + HashSet itemsToRemove = new HashSet<>(); + Stack appsToUpdate = new Stack<>(); + + try (Cursor c = mIconCache.mIconDb.query( + new String[]{IconDB.COLUMN_ROWID, IconDB.COLUMN_COMPONENT, + IconDB.COLUMN_LAST_UPDATED, IconDB.COLUMN_VERSION, + IconDB.COLUMN_SYSTEM_STATE}, + IconDB.COLUMN_USER + " = ? ", + new String[]{Long.toString(userSerial)})) { + + final int indexComponent = c.getColumnIndex(IconDB.COLUMN_COMPONENT); + final int indexLastUpdate = c.getColumnIndex(IconDB.COLUMN_LAST_UPDATED); + final int indexVersion = c.getColumnIndex(IconDB.COLUMN_VERSION); + final int rowIndex = c.getColumnIndex(IconDB.COLUMN_ROWID); + final int systemStateIndex = c.getColumnIndex(IconDB.COLUMN_SYSTEM_STATE); + + while (c.moveToNext()) { + String cn = c.getString(indexComponent); + ComponentName component = ComponentName.unflattenFromString(cn); + PackageInfo info = mPkgInfoMap.get(component.getPackageName()); + if (info == null) { + if (!ignorePackages.contains(component.getPackageName())) { + mIconCache.remove(component, user); + itemsToRemove.add(c.getInt(rowIndex)); + } + continue; + } + if ((info.applicationInfo.flags & ApplicationInfo.FLAG_IS_DATA_ONLY) != 0) { + // Application is not present + continue; + } + + long updateTime = c.getLong(indexLastUpdate); + int version = c.getInt(indexVersion); + LauncherActivityInfo app = componentMap.remove(component); + if (version == info.versionCode && updateTime == info.lastUpdateTime && + TextUtils.equals(c.getString(systemStateIndex), + mIconCache.mIconProvider.getIconSystemState(info.packageName))) { + continue; + } + if (app == null) { + mIconCache.remove(component, user); + itemsToRemove.add(c.getInt(rowIndex)); + } else { + appsToUpdate.add(app); + } + } + } catch (SQLiteException e) { + Log.d(TAG, "Error reading icon cache", e); + // Continue updating whatever we have read so far + } + if (!itemsToRemove.isEmpty()) { + mIconCache.mIconDb.delete( + Utilities.createDbSelectionQuery(IconDB.COLUMN_ROWID, itemsToRemove), null); + } + + // Insert remaining apps. + if (!componentMap.isEmpty() || !appsToUpdate.isEmpty()) { + Stack appsToAdd = new Stack<>(); + appsToAdd.addAll(componentMap.values()); + new SerializedIconUpdateTask(userSerial, user, appsToAdd, appsToUpdate).scheduleNext(); + } + } + + + /** + * A runnable that updates invalid icons and adds missing icons in the DB for the provided + * LauncherActivityInfo list. Items are updated/added one at a time, so that the + * worker thread doesn't get blocked. + */ + private class SerializedIconUpdateTask implements Runnable { + private final long mUserSerial; + private final UserHandle mUserHandle; + private final Stack mAppsToAdd; + private final Stack mAppsToUpdate; + private final HashSet mUpdatedPackages = new HashSet<>(); + + SerializedIconUpdateTask(long userSerial, UserHandle userHandle, + Stack appsToAdd, Stack appsToUpdate) { + mUserHandle = userHandle; + mUserSerial = userSerial; + mAppsToAdd = appsToAdd; + mAppsToUpdate = appsToUpdate; + } + + @Override + public void run() { + if (!mAppsToUpdate.isEmpty()) { + LauncherActivityInfo app = mAppsToUpdate.pop(); + String pkg = app.getComponentName().getPackageName(); + PackageInfo info = mPkgInfoMap.get(pkg); + mIconCache.addIconToDBAndMemCache( + app, info, mUserSerial, true /*replace existing*/); + mUpdatedPackages.add(pkg); + + if (mAppsToUpdate.isEmpty() && !mUpdatedPackages.isEmpty()) { + // No more app to update. Notify model. + LauncherAppState.getInstance(mIconCache.mContext).getModel() + .onPackageIconsUpdated(mUpdatedPackages, mUserHandle); + } + + // Let it run one more time. + scheduleNext(); + } else if (!mAppsToAdd.isEmpty()) { + LauncherActivityInfo app = mAppsToAdd.pop(); + PackageInfo info = mPkgInfoMap.get(app.getComponentName().getPackageName()); + // We do not check the mPkgInfoMap when generating the mAppsToAdd. Although every + // app should have package info, this is not guaranteed by the api + if (info != null) { + mIconCache.addIconToDBAndMemCache( + app, info, mUserSerial, false /*replace existing*/); + } + + if (!mAppsToAdd.isEmpty()) { + scheduleNext(); + } + } + } + + public void scheduleNext() { + mIconCache.mWorkerHandler.postAtTime(this, ICON_UPDATE_TOKEN, + SystemClock.uptimeMillis() + 1); + } + } +} diff --git a/src/com/android/launcher3/model/CacheDataUpdatedTask.java b/src/com/android/launcher3/model/CacheDataUpdatedTask.java index 54c0542764..be83d36921 100644 --- a/src/com/android/launcher3/model/CacheDataUpdatedTask.java +++ b/src/com/android/launcher3/model/CacheDataUpdatedTask.java @@ -20,7 +20,7 @@ import android.os.UserHandle; import com.android.launcher3.AllAppsList; import com.android.launcher3.AppInfo; -import com.android.launcher3.IconCache; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.ItemInfo; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherModel.CallbackTask; diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java index 744e98aeaa..77e9721bce 100644 --- a/src/com/android/launcher3/model/LoaderCursor.java +++ b/src/com/android/launcher3/model/LoaderCursor.java @@ -32,7 +32,7 @@ import android.util.Log; import android.util.LongSparseArray; import com.android.launcher3.AppInfo; -import com.android.launcher3.IconCache; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.ItemInfo; import com.android.launcher3.LauncherAppState; diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java index 8f8bc09f3c..4ccb8d8d6d 100644 --- a/src/com/android/launcher3/model/LoaderTask.java +++ b/src/com/android/launcher3/model/LoaderTask.java @@ -43,7 +43,8 @@ import android.util.MutableInt; import com.android.launcher3.AllAppsList; import com.android.launcher3.AppInfo; import com.android.launcher3.FolderInfo; -import com.android.launcher3.IconCache; +import com.android.launcher3.icons.IconCacheUpdateHandler; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.InstallShortcutReceiver; import com.android.launcher3.ItemInfo; import com.android.launcher3.LauncherAppState; @@ -182,7 +183,7 @@ public class LoaderTask implements Runnable { // second step TraceHelper.partitionSection(TAG, "step 2.1: loading all apps"); - loadAllApps(); + List> activityListPerUser = loadAllApps(); TraceHelper.partitionSection(TAG, "step 2.2: Binding all apps"); verifyNotStopped(); @@ -190,7 +191,9 @@ public class LoaderTask implements Runnable { verifyNotStopped(); TraceHelper.partitionSection(TAG, "step 2.3: Update icon cache"); - updateIconCache(); + IconCacheUpdateHandler updateHandler = mIconCache.getUpdateHandler(); + setIgnorePackages(updateHandler); + updateIconCacheForApps(updateHandler, activityListPerUser); // Take a break TraceHelper.partitionSection(TAG, "step 2 completed, wait for idle"); @@ -774,7 +777,7 @@ public class LoaderTask implements Runnable { } } - private void updateIconCache() { + private void setIgnorePackages(IconCacheUpdateHandler updateHandler) { // Ignore packages which have a promise icon. HashSet packagesToIgnore = new HashSet<>(); synchronized (mBgDataModel) { @@ -792,12 +795,20 @@ public class LoaderTask implements Runnable { } } } - mIconCache.updateDbIcons(packagesToIgnore); + updateHandler.setPackagesToIgnore(Process.myUserHandle(), packagesToIgnore); } - private void loadAllApps() { - final List profiles = mUserManager.getUserProfiles(); + private void updateIconCacheForApps(IconCacheUpdateHandler updateHandler, + List> activityListPerUser) { + int userCount = activityListPerUser.size(); + for (int i = 0; i < userCount; i++) { + updateHandler.updateIcons(activityListPerUser.get(i)); + } + } + private List> loadAllApps() { + final List profiles = mUserManager.getUserProfiles(); + List> activityListPerUser = new ArrayList<>(); // Clear the list of apps mBgAllAppsList.clear(); for (UserHandle user : profiles) { @@ -806,7 +817,7 @@ public class LoaderTask implements Runnable { // Fail if we don't have any apps // TODO: Fix this. Only fail for the current user. if (apps == null || apps.isEmpty()) { - return; + return activityListPerUser; } boolean quietMode = mUserManager.isQuietModeEnabled(user); // Create the ApplicationInfos @@ -815,6 +826,7 @@ public class LoaderTask implements Runnable { // This builds the icon bitmaps. mBgAllAppsList.add(new AppInfo(app, user, quietMode), app); } + activityListPerUser.add(apps); } if (FeatureFlags.LAUNCHER3_PROMISE_APPS_IN_ALL_APPS) { @@ -827,6 +839,7 @@ public class LoaderTask implements Runnable { } mBgAllAppsList.added = new ArrayList<>(); + return activityListPerUser; } private void loadDeepShortcuts() { diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java index c55608310c..0af53118d3 100644 --- a/src/com/android/launcher3/model/PackageUpdatedTask.java +++ b/src/com/android/launcher3/model/PackageUpdatedTask.java @@ -24,7 +24,7 @@ import android.util.Log; import com.android.launcher3.AllAppsList; import com.android.launcher3.AppInfo; -import com.android.launcher3.IconCache; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.InstallShortcutReceiver; import com.android.launcher3.ItemInfo; import com.android.launcher3.LauncherAppState; diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java index 448ff6c920..82f4fe1894 100644 --- a/src/com/android/launcher3/model/WidgetsModel.java +++ b/src/com/android/launcher3/model/WidgetsModel.java @@ -11,7 +11,7 @@ import android.os.UserHandle; import android.util.Log; import com.android.launcher3.AppFilter; -import com.android.launcher3.IconCache; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppWidgetProviderInfo; diff --git a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java index 29b4b0ba24..50db40fde8 100644 --- a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java +++ b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java @@ -33,8 +33,8 @@ import android.view.View.OnClickListener; import com.android.launcher3.DeviceProfile; import com.android.launcher3.FastBitmapDrawable; -import com.android.launcher3.IconCache; -import com.android.launcher3.IconCache.ItemInfoUpdateReceiver; +import com.android.launcher3.icons.IconCache; +import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver; import com.android.launcher3.ItemInfoWithIcon; import com.android.launcher3.LauncherAppWidgetInfo; import com.android.launcher3.R; diff --git a/src/com/android/launcher3/widget/WidgetsDiffReporter.java b/src/com/android/launcher3/widget/WidgetsDiffReporter.java index 2ba672d7af..435125bd83 100644 --- a/src/com/android/launcher3/widget/WidgetsDiffReporter.java +++ b/src/com/android/launcher3/widget/WidgetsDiffReporter.java @@ -18,7 +18,7 @@ package com.android.launcher3.widget; import android.util.Log; -import com.android.launcher3.IconCache; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.model.PackageItemInfo; import com.android.launcher3.widget.WidgetsListAdapter.WidgetListRowEntryComparator; diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java index 1016d04005..a45521dee7 100644 --- a/src/com/android/launcher3/widget/WidgetsListAdapter.java +++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java @@ -23,7 +23,7 @@ import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; import android.view.ViewGroup; -import com.android.launcher3.IconCache; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.R; import com.android.launcher3.WidgetPreviewLoader; import com.android.launcher3.model.WidgetItem; diff --git a/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java b/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java index dd3e859fda..59b2da036f 100644 --- a/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java +++ b/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java @@ -24,7 +24,7 @@ import androidx.test.rule.provider.ProviderTestRule; import com.android.launcher3.AllAppsList; import com.android.launcher3.AppFilter; import com.android.launcher3.AppInfo; -import com.android.launcher3.IconCache; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.ItemInfo; import com.android.launcher3.LauncherAppState; diff --git a/tests/src/com/android/launcher3/model/LoaderCursorTest.java b/tests/src/com/android/launcher3/model/LoaderCursorTest.java index 29e54b1374..42a2764010 100644 --- a/tests/src/com/android/launcher3/model/LoaderCursorTest.java +++ b/tests/src/com/android/launcher3/model/LoaderCursorTest.java @@ -10,7 +10,7 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; -import com.android.launcher3.IconCache; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.ItemInfo; import com.android.launcher3.LauncherAppState; diff --git a/tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java b/tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java index d5b14f2985..307a53eefd 100644 --- a/tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java +++ b/tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java @@ -29,7 +29,7 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import android.view.LayoutInflater; -import com.android.launcher3.IconCache; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.WidgetPreviewLoader;