From 756adbc3e41ee1edb53c580b8c679f343924fab5 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 16 Apr 2015 15:20:51 -0700 Subject: Fixing missing updates in package install sessions > Ensure icon cache never returns null icon > Enabling install shortuct receiver only after workspace has finished binding > Making all the model changes for package installs on worker thread and only posting the updaes on the UI > Making shortcut exists check on the loaded items and not on the DB > Explicitely using worker thread for PckageInstallListener > Removing backward compatibility support from PackageInstallerCompat Change-Id: I9592771b9670c1c1c84c8208cae8dafa7b393e65 --- src/com/android/launcher3/IconCache.java | 16 +- .../android/launcher3/InstallShortcutReceiver.java | 8 +- src/com/android/launcher3/Launcher.java | 36 ++-- src/com/android/launcher3/LauncherAppState.java | 11 -- src/com/android/launcher3/LauncherFiles.java | 3 +- src/com/android/launcher3/LauncherModel.java | 196 ++++++++++++++++----- src/com/android/launcher3/ShortcutInfo.java | 1 + src/com/android/launcher3/Workspace.java | 69 +------- .../launcher3/compat/PackageInstallerCompat.java | 17 +- .../compat/PackageInstallerCompatV16.java | 151 +--------------- .../launcher3/compat/PackageInstallerCompatVL.java | 144 +++------------ 11 files changed, 225 insertions(+), 427 deletions(-) diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index 48b38f182..fd4571482 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -101,7 +101,7 @@ public class IconCache { mIconDpi = activityManager.getLauncherLargeIconDensity(); mIconDb = new IconDB(context); - mWorkerHandler = new Handler(LauncherModel.sWorkerThread.getLooper()); + mWorkerHandler = new Handler(LauncherModel.getWorkerLooper()); } private Drawable getFullResDefaultActivityIcon() { @@ -388,16 +388,20 @@ public class IconCache { return new IconLoadRequest(request, mWorkerHandler); } + private Bitmap getNonNullIcon(CacheEntry entry, UserHandleCompat user) { + return entry.icon == null ? getDefaultIcon(user) : entry.icon; + } + /** * Fill in "application" with the icon and label for "info." */ public synchronized void getTitleAndIcon(AppInfo application, LauncherActivityInfoCompat info, boolean useLowResIcon) { - CacheEntry entry = cacheLocked(application.componentName, info, - info == null ? application.user : info.getUser(), + UserHandleCompat user = info == null ? application.user : info.getUser(); + CacheEntry entry = cacheLocked(application.componentName, info, user, false, useLowResIcon); application.title = entry.title; - application.iconBitmap = entry.icon; + application.iconBitmap = getNonNullIcon(entry, user); application.contentDescription = entry.contentDescription; application.usingLowResIcon = entry.isLowResIcon; } @@ -445,7 +449,7 @@ public class IconCache { ShortcutInfo shortcutInfo, ComponentName component, LauncherActivityInfoCompat info, UserHandleCompat user, boolean usePkgIcon, boolean useLowResIcon) { CacheEntry entry = cacheLocked(component, info, user, usePkgIcon, useLowResIcon); - shortcutInfo.setIcon(entry.icon); + shortcutInfo.setIcon(getNonNullIcon(entry, user)); shortcutInfo.title = entry.title; shortcutInfo.usingFallbackIcon = isDefaultIcon(entry.icon, user); shortcutInfo.usingLowResIcon = entry.isLowResIcon; @@ -458,7 +462,7 @@ public class IconCache { String packageName, UserHandleCompat user, boolean useLowResIcon, PackageItemInfo infoOut) { CacheEntry entry = getEntryForPackageLocked(packageName, user, useLowResIcon); - infoOut.iconBitmap = entry.icon; + infoOut.iconBitmap = getNonNullIcon(entry, user); infoOut.title = entry.title; infoOut.usingLowResIcon = entry.isLowResIcon; infoOut.contentDescription = entry.contentDescription; diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java index 0c69154aa..27dda6404 100644 --- a/src/com/android/launcher3/InstallShortcutReceiver.java +++ b/src/com/android/launcher3/InstallShortcutReceiver.java @@ -199,12 +199,8 @@ public class InstallShortcutReceiver extends BroadcastReceiver { } } - final boolean exists = LauncherModel.shortcutExists(context, pendingInfo.label, - intent, pendingInfo.user); - if (!exists) { - // Generate a shortcut info to add into the model - addShortcuts.add(pendingInfo.getShortcutInfo()); - } + // Generate a shortcut info to add into the model + addShortcuts.add(pendingInfo.getShortcutInfo()); } // Add the new apps to the model and bind them diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 7364a9f20..20844777f 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -98,8 +98,6 @@ import com.android.launcher3.PagedView.PageSwitchListener; import com.android.launcher3.compat.AppWidgetManagerCompat; import com.android.launcher3.compat.LauncherActivityInfoCompat; import com.android.launcher3.compat.LauncherAppsCompat; -import com.android.launcher3.compat.PackageInstallerCompat; -import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.util.Thunk; @@ -1060,9 +1058,6 @@ public class Launcher extends Activity getWorkspace().reinflateWidgetsIfNecessary(); reinflateQSBIfNecessary(); - // Process any items that were added while Launcher was away. - InstallShortcutReceiver.disableAndFlushInstallQueue(this); - if (DEBUG_RESUME_TIME) { Log.d(TAG, "Time spent in onResume: " + (System.currentTimeMillis() - startTime)); } @@ -1078,7 +1073,10 @@ public class Launcher extends Activity mWorkspace.updateInteractionForState(); mWorkspace.onResume(); - PackageInstallerCompat.getInstance(this).onResume(); + if (!isWorkspaceLoading()) { + // Process any items that were added while Launcher was away. + InstallShortcutReceiver.disableAndFlushInstallQueue(this); + } if (mLauncherCallbacks != null) { mLauncherCallbacks.onResume(); @@ -1089,7 +1087,6 @@ public class Launcher extends Activity protected void onPause() { // Ensure that items added to Launcher are queued until Launcher returns InstallShortcutReceiver.enableInstallQueue(); - PackageInstallerCompat.getInstance(this).onPause(); super.onPause(); mPaused = true; @@ -4084,7 +4081,7 @@ public class Launcher extends Activity sPendingAddItem = null; } - PackageInstallerCompat.getInstance(this).onFinishBind(); + InstallShortcutReceiver.disableAndFlushInstallQueue(this); if (mLauncherCallbacks != null) { mLauncherCallbacks.finishBindingItems(false); @@ -4236,22 +4233,17 @@ public class Launcher extends Activity * Implementation of the method from LauncherModel.Callbacks. */ @Override - public void updatePackageState(ArrayList installInfo) { - if (mWorkspace != null) { - mWorkspace.updatePackageState(installInfo); + public void bindRestoreItemsChange(final HashSet updates) { + Runnable r = new Runnable() { + public void run() { + bindRestoreItemsChange(updates); + } + }; + if (waitUntilResume(r)) { + return; } - } - /** - * Update the label and icon of all the icons in a package - * - * Implementation of the method from LauncherModel.Callbacks. - */ - @Override - public void updatePackageBadge(String packageName) { - if (mWorkspace != null) { - mWorkspace.updatePackageBadge(packageName, UserHandleCompat.myUserHandle()); - } + mWorkspace.updateRestoreItems(updates); } /** diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index 6e77d0628..7f31e4999 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -256,15 +256,4 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks { public static boolean isDogfoodBuild() { return getInstance().mBuildInfo.isDogfoodBuild(); } - - public void setPackageState(ArrayList installInfo) { - mModel.setPackageState(installInfo); - } - - /** - * Updates the icons and label of all icons for the provided package name. - */ - public void updatePackageBadge(String packageName) { - mModel.updatePackageBadge(packageName); - } } diff --git a/src/com/android/launcher3/LauncherFiles.java b/src/com/android/launcher3/LauncherFiles.java index 9dd8dc50c..03ec4bf7a 100644 --- a/src/com/android/launcher3/LauncherFiles.java +++ b/src/com/android/launcher3/LauncherFiles.java @@ -42,5 +42,6 @@ public class LauncherFiles { // TODO: Delete these files on upgrade public static final List OBSOLETE_FILES = Collections.unmodifiableList(Arrays.asList( "launches.log", - "stats.log")); + "stats.log", + "com.android.launcher3.compat.PackageInstallerCompatV16.queue")); } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index e5561e219..5a65cab82 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -42,6 +42,7 @@ import android.os.Build; import android.os.Environment; import android.os.Handler; import android.os.HandlerThread; +import android.os.Looper; import android.os.Parcelable; import android.os.Process; import android.os.RemoteException; @@ -199,8 +200,7 @@ public class LauncherModel extends BroadcastReceiver public void bindShortcutsChanged(ArrayList updated, ArrayList removed, UserHandleCompat user); public void bindWidgetsRestored(ArrayList widgets); - public void updatePackageState(ArrayList installInfo); - public void updatePackageBadge(String packageName); + public void bindRestoreItemsChange(HashSet updates); public void bindComponentsRemoved(ArrayList packageNames, ArrayList appInfos, UserHandleCompat user, int reason); public void bindPackagesUpdated(ArrayList widgetsAndShortcuts); @@ -282,30 +282,110 @@ public class LauncherModel extends BroadcastReceiver return mOldContentProviderExists && !launcher.isLauncherPreinstalled() ; } - public void setPackageState(final ArrayList installInfo) { - // Process the updated package state - Runnable r = new Runnable() { + public void setPackageState(final PackageInstallInfo installInfo) { + Runnable updateRunnable = new Runnable() { + + @Override public void run() { - Callbacks callbacks = getCallback(); - if (callbacks != null) { - callbacks.updatePackageState(installInfo); + synchronized (sBgLock) { + final HashSet updates = new HashSet<>(); + + if (installInfo.state == PackageInstallerCompat.STATUS_INSTALLED) { + // Ignore install success events as they are handled by Package add events. + return; + } + + for (ItemInfo info : sBgItemsIdMap.values()) { + if (info instanceof ShortcutInfo) { + ShortcutInfo si = (ShortcutInfo) info; + ComponentName cn = si.getTargetComponent(); + if (si.isPromise() && (cn != null) + && installInfo.packageName.equals(cn.getPackageName())) { + si.setInstallProgress(installInfo.progress); + + if (installInfo.state == PackageInstallerCompat.STATUS_FAILED) { + // Mark this info as broken. + si.status &= ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE; + } + updates.add(si); + } + } + } + + for (LauncherAppWidgetInfo widget : sBgAppWidgets) { + if (widget.providerName.getPackageName().equals(installInfo.packageName)) { + widget.installProgress = installInfo.progress; + updates.add(widget); + } + } + + if (!updates.isEmpty()) { + // Push changes to the callback. + Runnable r = new Runnable() { + public void run() { + Callbacks callbacks = getCallback(); + if (callbacks != null) { + callbacks.bindRestoreItemsChange(updates); + } + } + }; + mHandler.post(r); + } } } }; - mHandler.post(r); + runOnWorkerThread(updateRunnable); } - public void updatePackageBadge(final String packageName) { - // Process the updated package badge - Runnable r = new Runnable() { + /** + * Updates the icons and label of all pending icons for the provided package name. + */ + public void updateSessionDisplayInfo(final String packageName) { + Runnable updateRunnable = new Runnable() { + + @Override public void run() { - Callbacks callbacks = getCallback(); - if (callbacks != null) { - callbacks.updatePackageBadge(packageName); + synchronized (sBgLock) { + final ArrayList updates = new ArrayList<>(); + final UserHandleCompat user = UserHandleCompat.myUserHandle(); + + for (ItemInfo info : sBgItemsIdMap.values()) { + if (info instanceof ShortcutInfo) { + ShortcutInfo si = (ShortcutInfo) info; + ComponentName cn = si.getTargetComponent(); + if (si.isPromise() && (cn != null) + && packageName.equals(cn.getPackageName())) { + if (si.hasStatusFlag(ShortcutInfo.FLAG_AUTOINTALL_ICON)) { + // For auto install apps update the icon as well as label. + mIconCache.getTitleAndIcon(si, + si.promisedIntent, user, + si.shouldUseLowResIcon()); + } else { + // Only update the icon for restored apps. + si.updateIcon(mIconCache); + } + updates.add(si); + } + } + } + + if (!updates.isEmpty()) { + // Push changes to the callback. + Runnable r = new Runnable() { + public void run() { + Callbacks callbacks = getCallback(); + if (callbacks != null) { + callbacks.bindShortcutsChanged(updates, + new ArrayList(), user); + } + } + }; + mHandler.post(r); + } } } }; - mHandler.post(r); + runOnWorkerThread(updateRunnable); } public void addAppsToAllApps(final Context ctx, final ArrayList allAppsApps) { @@ -537,8 +617,7 @@ public class LauncherModel extends BroadcastReceiver for (ItemInfo item : workspaceApps) { if (!allowDuplicate && item instanceof ShortcutInfo) { // Short-circuit this logic if the icon exists somewhere on the workspace - if (shortcutExists(context, item.title.toString(), - item.getIntent(), item.user)) { + if (shortcutExists(context, item.getIntent(), item.user)) { continue; } } @@ -904,41 +983,42 @@ public class LauncherModel extends BroadcastReceiver } /** - * Returns true if the shortcuts already exists in the database. - * we identify a shortcut by its title and intent. + * Returns true if the shortcuts already exists on the workspace. This must be called after + * the workspace has been loaded. We identify a shortcut by its intent. + * TODO: Throw exception is above condition is not met. */ - static boolean shortcutExists(Context context, String title, Intent intent, - UserHandleCompat user) { - final ContentResolver cr = context.getContentResolver(); + @Thunk static boolean shortcutExists(Context context, Intent intent, UserHandleCompat user) { final Intent intentWithPkg, intentWithoutPkg; - + final String packageName; if (intent.getComponent() != null) { // If component is not null, an intent with null package will produce // the same result and should also be a match. + packageName = intent.getComponent().getPackageName(); if (intent.getPackage() != null) { intentWithPkg = intent; intentWithoutPkg = new Intent(intent).setPackage(null); } else { - intentWithPkg = new Intent(intent).setPackage( - intent.getComponent().getPackageName()); + intentWithPkg = new Intent(intent).setPackage(packageName); intentWithoutPkg = intent; } } else { intentWithPkg = intent; intentWithoutPkg = intent; + packageName = intent.getPackage(); } - String userSerial = Long.toString(UserManagerCompat.getInstance(context) - .getSerialNumberForUser(user)); - Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, - new String[] { "title", "intent", "profileId" }, - "title=? and (intent=? or intent=?) and profileId=?", - new String[] { title, intentWithPkg.toUri(0), intentWithoutPkg.toUri(0), userSerial }, - null); - try { - return c.moveToFirst(); - } finally { - c.close(); + + synchronized (sBgLock) { + for (ItemInfo item : sBgItemsIdMap.values()) { + if (item instanceof ShortcutInfo) { + ShortcutInfo info = (ShortcutInfo) item; + if (intentWithPkg.equals(info.getIntent()) + || intentWithoutPkg.equals(info.getIntent())) { + return true; + } + } + } } + return false; } /** @@ -1366,6 +1446,8 @@ public class LauncherModel extends BroadcastReceiver } public void startLoader(boolean isLaunching, int synchronousBindPage, int loadFlags) { + // Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems + InstallShortcutReceiver.enableInstallQueue(); synchronized (mLock) { if (DEBUG_LOADERS) { Log.d(TAG, "startLoader isLaunching=" + isLaunching); @@ -1812,7 +1894,7 @@ public class LauncherModel extends BroadcastReceiver synchronized (sBgLock) { clearSBgDataStructures(); - final HashSet installingPkgs = PackageInstallerCompat + final HashMap installingPkgs = PackageInstallerCompat .getInstance(mContext).updateAndGetActiveSessionCache(); final ArrayList itemsToRemove = new ArrayList(); @@ -1951,7 +2033,7 @@ public class LauncherModel extends BroadcastReceiver if ((promiseType & ShortcutInfo.FLAG_RESTORE_STARTED) != 0) { // Restore has started once. - } else if (installingPkgs.contains(cn.getPackageName())) { + } else if (installingPkgs.containsKey(cn.getPackageName())) { // App restore has started. Update the flag promiseType |= ShortcutInfo.FLAG_RESTORE_STARTED; ContentValues values = new ContentValues(); @@ -2093,6 +2175,18 @@ public class LauncherModel extends BroadcastReceiver break; } + if (restored) { + ComponentName cn = info.getTargetComponent(); + if (cn != null) { + Integer progress = installingPkgs.get(cn.getPackageName()); + if (progress != null) { + info.setInstallProgress(progress); + } else { + info.status &= ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE; + } + } + } + switch (container) { case LauncherSettings.Favorites.CONTAINER_DESKTOP: case LauncherSettings.Favorites.CONTAINER_HOTSEAT: @@ -2220,10 +2314,11 @@ public class LauncherModel extends BroadcastReceiver appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId, component); appWidgetInfo.restoreStatus = restoreStatus; + Integer installProgress = installingPkgs.get(component.getPackageName()); if ((restoreStatus & LauncherAppWidgetInfo.FLAG_RESTORE_STARTED) != 0) { // Restore has started once. - } else if (installingPkgs.contains(component.getPackageName())) { + } else if (installProgress != null) { // App restore has started. Update the flag appWidgetInfo.restoreStatus |= LauncherAppWidgetInfo.FLAG_RESTORE_STARTED; @@ -2233,6 +2328,9 @@ public class LauncherModel extends BroadcastReceiver itemsToRemove.add(id); continue; } + + appWidgetInfo.installProgress = + installProgress == null ? 0 : installProgress; } appWidgetInfo.id = id; @@ -3112,15 +3210,13 @@ public class LauncherModel extends BroadcastReceiver } // Restore the shortcut. - si.intent = si.promisedIntent; - si.promisedIntent = null; - si.status &= ~ShortcutInfo.FLAG_RESTORED_ICON - & ~ShortcutInfo.FLAG_AUTOINTALL_ICON - & ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE; if (appInfo != null) { si.flags = appInfo.flags; } + si.intent = si.promisedIntent; + si.promisedIntent = null; + si.status = ShortcutInfo.DEFAULT; infoUpdated = true; si.updateIcon(mIconCache); } @@ -3353,12 +3449,10 @@ public class LauncherModel extends BroadcastReceiver if (!TextUtils.isEmpty(title)) { info.title = title; } - info.status = ShortcutInfo.FLAG_RESTORED_ICON; } else if ((promiseType & ShortcutInfo.FLAG_AUTOINTALL_ICON) != 0) { if (TextUtils.isEmpty(info.title)) { info.title = (cursor != null) ? cursor.getString(titleIndex) : ""; } - info.status = ShortcutInfo.FLAG_AUTOINTALL_ICON; } else { throw new InvalidParameterException("Invalid restoreType " + promiseType); } @@ -3367,6 +3461,7 @@ public class LauncherModel extends BroadcastReceiver info.title.toString(), info.user); info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; info.promisedIntent = intent; + info.status = promiseType; return info; } @@ -3669,4 +3764,11 @@ public class LauncherModel extends BroadcastReceiver return sBgFolders.get(folderId); } } + + /** + * @return the looper for the worker thread which can be used to start background tasks. + */ + public static Looper getWorkerLooper() { + return sWorkerThread.getLooper(); + } } diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java index 5bef845bb..6354fcd28 100644 --- a/src/com/android/launcher3/ShortcutInfo.java +++ b/src/com/android/launcher3/ShortcutInfo.java @@ -214,6 +214,7 @@ public class ShortcutInfo extends ItemInfo { String uri = promisedIntent != null ? promisedIntent.toUri(0) : (intent != null ? intent.toUri(0) : null); values.put(LauncherSettings.BaseLauncherColumns.INTENT, uri); + values.put(LauncherSettings.Favorites.RESTORED, status); if (customIcon) { values.put(LauncherSettings.BaseLauncherColumns.ICON_TYPE, diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 9e680fb82..abb8489fd 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -62,8 +62,6 @@ import com.android.launcher3.Launcher.CustomContentCallbacks; import com.android.launcher3.Launcher.LauncherOverlay; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.UninstallDropTarget.UninstallSource; -import com.android.launcher3.compat.PackageInstallerCompat; -import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.util.Thunk; import com.android.launcher3.util.WallpaperUtils; @@ -4358,32 +4356,17 @@ public class Workspace extends SmoothPagedView removeItemsByPackageName(packages, user); } - public void updatePackageBadge(final String packageName, final UserHandleCompat user) { + public void updateRestoreItems(final HashSet updates) { mapOverItems(MAP_RECURSE, new ItemOperator() { @Override public boolean evaluate(ItemInfo info, View v, View parent) { - if (info instanceof ShortcutInfo && v instanceof BubbleTextView) { - ShortcutInfo shortcutInfo = (ShortcutInfo) info; - ComponentName cn = shortcutInfo.getTargetComponent(); - if (user.equals(shortcutInfo.user) && cn != null - && shortcutInfo.isPromise() - && packageName.equals(cn.getPackageName())) { - if (shortcutInfo.hasStatusFlag(ShortcutInfo.FLAG_AUTOINTALL_ICON)) { - // For auto install apps update the icon as well as label. - mIconCache.getTitleAndIcon(shortcutInfo, - shortcutInfo.promisedIntent, user, - shortcutInfo.shouldUseLowResIcon()); - } else { - // Only update the icon for restored apps. - shortcutInfo.updateIcon(mIconCache); - } - BubbleTextView shortcut = (BubbleTextView) v; - shortcut.applyFromShortcutInfo(shortcutInfo, mIconCache, true, false); - - if (parent != null) { - parent.invalidate(); - } - } + if (info instanceof ShortcutInfo && v instanceof BubbleTextView + && updates.contains(info)) { + ((BubbleTextView) v).applyState(false); + } else if (v instanceof PendingAppWidgetHostView + && info instanceof LauncherAppWidgetInfo + && updates.contains(info)) { + ((PendingAppWidgetHostView) v).applyState(); } // process all the shortcuts return false; @@ -4391,42 +4374,6 @@ public class Workspace extends SmoothPagedView }); } - public void updatePackageState(ArrayList installInfos) { - for (final PackageInstallInfo installInfo : installInfos) { - if (installInfo.state == PackageInstallerCompat.STATUS_INSTALLED) { - continue; - } - - mapOverItems(MAP_RECURSE, new ItemOperator() { - @Override - public boolean evaluate(ItemInfo info, View v, View parent) { - if (info instanceof ShortcutInfo && v instanceof BubbleTextView) { - ShortcutInfo si = (ShortcutInfo) info; - ComponentName cn = si.getTargetComponent(); - if (si.isPromise() && (cn != null) - && installInfo.packageName.equals(cn.getPackageName())) { - si.setInstallProgress(installInfo.progress); - if (installInfo.state == PackageInstallerCompat.STATUS_FAILED) { - // Mark this info as broken. - si.status &= ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE; - } - ((BubbleTextView)v).applyState(false); - } - } else if (v instanceof PendingAppWidgetHostView - && info instanceof LauncherAppWidgetInfo - && ((LauncherAppWidgetInfo) info).providerName.getPackageName() - .equals(installInfo.packageName)) { - ((LauncherAppWidgetInfo) info).installProgress = installInfo.progress; - ((PendingAppWidgetHostView) v).applyState(); - } - - // process all the shortcuts - return false; - } - }); - } - } - void widgetsRestored(ArrayList changedInfo) { if (!changedInfo.isEmpty()) { DeferredWidgetRefresh widgetRefresh = new DeferredWidgetRefresh(changedInfo, diff --git a/src/com/android/launcher3/compat/PackageInstallerCompat.java b/src/com/android/launcher3/compat/PackageInstallerCompat.java index 0eb8754e8..c49908328 100644 --- a/src/com/android/launcher3/compat/PackageInstallerCompat.java +++ b/src/com/android/launcher3/compat/PackageInstallerCompat.java @@ -20,7 +20,7 @@ import android.content.Context; import com.android.launcher3.Utilities; -import java.util.HashSet; +import java.util.HashMap; public abstract class PackageInstallerCompat { @@ -37,25 +37,20 @@ public abstract class PackageInstallerCompat { if (Utilities.isLmpOrAbove()) { sInstance = new PackageInstallerCompatVL(context); } else { - sInstance = new PackageInstallerCompatV16(context) { }; + sInstance = new PackageInstallerCompatV16(); } } return sInstance; } } - public abstract HashSet updateAndGetActiveSessionCache(); - - public abstract void onPause(); - - public abstract void onResume(); - - public abstract void onFinishBind(); + /** + * @return a map of active installs to their progress + */ + public abstract HashMap updateAndGetActiveSessionCache(); public abstract void onStop(); - public abstract void recordPackageUpdate(String packageName, int state, int progress); - public static final class PackageInstallInfo { public final String packageName; diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatV16.java b/src/com/android/launcher3/compat/PackageInstallerCompatV16.java index 1910d22ae..654e34968 100644 --- a/src/com/android/launcher3/compat/PackageInstallerCompatV16.java +++ b/src/com/android/launcher3/compat/PackageInstallerCompatV16.java @@ -16,160 +16,17 @@ package com.android.launcher3.compat; -import android.content.Context; -import android.content.SharedPreferences; -import android.text.TextUtils; -import android.util.Log; - -import com.android.launcher3.LauncherAppState; - -import org.json.JSONException; -import org.json.JSONObject; -import org.json.JSONStringer; -import org.json.JSONTokener; - -import java.util.ArrayList; -import java.util.HashSet; +import java.util.HashMap; public class PackageInstallerCompatV16 extends PackageInstallerCompat { - private static final String TAG = "PackageInstallerCompatV16"; - private static final boolean DEBUG = false; - - private static final String KEY_PROGRESS = "progress"; - private static final String KEY_STATE = "state"; - - private static final String PREFS = - "com.android.launcher3.compat.PackageInstallerCompatV16.queue"; - - protected final SharedPreferences mPrefs; - - boolean mUseQueue; - boolean mFinishedBind; - boolean mReplayPending; - - PackageInstallerCompatV16(Context context) { - mPrefs = context.getSharedPreferences(PREFS, Context.MODE_PRIVATE); - } - - @Override - public void onPause() { - mUseQueue = true; - if (DEBUG) Log.d(TAG, "updates paused"); - } - - @Override - public void onResume() { - mUseQueue = false; - if (mFinishedBind) { - replayUpdates(); - } - } - - @Override - public void onFinishBind() { - mFinishedBind = true; - if (!mUseQueue) { - replayUpdates(); - } - } + PackageInstallerCompatV16() { } @Override public void onStop() { } - private void replayUpdates() { - if (DEBUG) Log.d(TAG, "updates resumed"); - LauncherAppState app = LauncherAppState.getInstanceNoCreate(); - if (app == null) { - mReplayPending = true; // try again later - if (DEBUG) Log.d(TAG, "app is null, delaying send"); - return; - } - mReplayPending = false; - ArrayList updates = new ArrayList(); - for (String packageName: mPrefs.getAll().keySet()) { - final String json = mPrefs.getString(packageName, null); - if (!TextUtils.isEmpty(json)) { - updates.add(infoFromJson(packageName, json)); - } - } - if (!updates.isEmpty()) { - sendUpdate(app, updates); - } - } - - /** - * This should be called by the implementations to register a package update. - */ - @Override - public synchronized void recordPackageUpdate(String packageName, int state, int progress) { - SharedPreferences.Editor editor = mPrefs.edit(); - PackageInstallInfo installInfo = new PackageInstallInfo(packageName); - installInfo.progress = progress; - installInfo.state = state; - if (state == STATUS_INSTALLED) { - // no longer necessary to track this package - editor.remove(packageName); - if (DEBUG) Log.d(TAG, "no longer tracking " + packageName); - } else { - editor.putString(packageName, infoToJson(installInfo)); - if (DEBUG) - Log.d(TAG, "saved state: " + infoToJson(installInfo) - + " for package: " + packageName); - - } - editor.commit(); - - if (!mUseQueue) { - if (mReplayPending) { - replayUpdates(); - } else if (state != STATUS_INSTALLED) { - LauncherAppState app = LauncherAppState.getInstanceNoCreate(); - ArrayList update = new ArrayList(); - update.add(installInfo); - sendUpdate(app, update); - } - } - } - - private void sendUpdate(LauncherAppState app, ArrayList updates) { - if (app == null) { - mReplayPending = true; // try again later - if (DEBUG) Log.d(TAG, "app is null, delaying send"); - } else { - app.setPackageState(updates); - } - } - - private static PackageInstallInfo infoFromJson(String packageName, String json) { - PackageInstallInfo info = new PackageInstallInfo(packageName); - try { - JSONObject object = (JSONObject) new JSONTokener(json).nextValue(); - info.state = object.getInt(KEY_STATE); - info.progress = object.getInt(KEY_PROGRESS); - } catch (JSONException e) { - Log.e(TAG, "failed to deserialize app state update", e); - } - return info; - } - - private static String infoToJson(PackageInstallInfo info) { - String value = null; - try { - JSONStringer json = new JSONStringer() - .object() - .key(KEY_STATE).value(info.state) - .key(KEY_PROGRESS).value(info.progress) - .endObject(); - value = json.toString(); - } catch (JSONException e) { - Log.e(TAG, "failed to serialize app state update", e); - } - return value; - } - @Override - public HashSet updateAndGetActiveSessionCache() { - return new HashSet(); + public HashMap updateAndGetActiveSessionCache() { + return new HashMap<>(); } } diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java index d6d4b8287..395d5f9e2 100644 --- a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java +++ b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java @@ -21,63 +21,41 @@ import android.content.pm.PackageInstaller; import android.content.pm.PackageInstaller.SessionCallback; import android.content.pm.PackageInstaller.SessionInfo; import android.os.Handler; -import android.util.Log; import android.util.SparseArray; import com.android.launcher3.IconCache; import com.android.launcher3.LauncherAppState; +import com.android.launcher3.LauncherModel; import com.android.launcher3.util.Thunk; -import java.util.ArrayList; -import java.util.HashSet; +import java.util.HashMap; -public class PackageInstallerCompatVL extends PackageInstallerCompat implements Runnable { +public class PackageInstallerCompatVL extends PackageInstallerCompat { - private static final String TAG = "PackageInstallerCompatVL"; - private static final boolean DEBUG = false; - - // All updates to these sets must happen on the {@link #mWorker} thread. - @Thunk final SparseArray mPendingReplays = new SparseArray(); - @Thunk final HashSet mPendingBadgeUpdates = new HashSet(); + @Thunk final SparseArray mActiveSessions = new SparseArray<>(); @Thunk final PackageInstaller mInstaller; private final IconCache mCache; private final Handler mWorker; - private boolean mResumed; - private boolean mBound; - PackageInstallerCompatVL(Context context) { mInstaller = context.getPackageManager().getPackageInstaller(); LauncherAppState.setApplicationContext(context.getApplicationContext()); mCache = LauncherAppState.getInstance().getIconCache(); - mWorker = new Handler(); - - mResumed = false; - mBound = false; + mWorker = new Handler(LauncherModel.getWorkerLooper()); mInstaller.registerSessionCallback(mCallback, mWorker); - - // On start, send updates for all active sessions - mWorker.post(new Runnable() { - - @Override - public void run() { - for (SessionInfo info : mInstaller.getAllSessions()) { - mPendingReplays.append(info.getSessionId(), info); - } - } - }); } @Override - public HashSet updateAndGetActiveSessionCache() { - HashSet activePackages = new HashSet(); + public HashMap updateAndGetActiveSessionCache() { + HashMap activePackages = new HashMap<>(); UserHandleCompat user = UserHandleCompat.myUserHandle(); for (SessionInfo info : mInstaller.getAllSessions()) { addSessionInfoToCahce(info, user); if (info.getAppPackageName() != null) { - activePackages.add(info.getAppPackageName()); + activePackages.put(info.getAppPackageName(), (int) (info.getProgress() * 100)); + mActiveSessions.put(info.getSessionId(), info.getAppPackageName()); } } return activePackages; @@ -96,74 +74,10 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat implements mInstaller.unregisterSessionCallback(mCallback); } - @Override - public void onFinishBind() { - mBound = true; - mWorker.post(this); - } - - @Override - public void onPause() { - mResumed = false; - } - - @Override - public void onResume() { - mResumed = true; - mWorker.post(this); - } - - @Override - public void recordPackageUpdate(String packageName, int state, int progress) { - // No op - } - - @Override - public void run() { - // Called on mWorker thread. - replayUpdates(null); - } - - @Thunk void replayUpdates(PackageInstallInfo newInfo) { - if (DEBUG) Log.d(TAG, "updates resumed"); - if (!mResumed || !mBound) { - // Not yet ready - return; - } - if ((mPendingReplays.size() == 0) && (newInfo == null)) { - // Nothing to update - return; - } - + @Thunk void sendUpdate(PackageInstallInfo info) { LauncherAppState app = LauncherAppState.getInstanceNoCreate(); - if (app == null) { - // Try again later - if (DEBUG) Log.d(TAG, "app is null, delaying send"); - return; - } - - ArrayList updates = new ArrayList(); - if ((newInfo != null) && (newInfo.state != STATUS_INSTALLED)) { - updates.add(newInfo); - } - for (int i = mPendingReplays.size() - 1; i >= 0; i--) { - SessionInfo session = mPendingReplays.valueAt(i); - if (session.getAppPackageName() != null) { - updates.add(new PackageInstallInfo(session.getAppPackageName(), - STATUS_INSTALLING, - (int) (session.getProgress() * 100))); - } - } - mPendingReplays.clear(); - if (!updates.isEmpty()) { - app.setPackageState(updates); - } - - if (!mPendingBadgeUpdates.isEmpty()) { - for (String pkg : mPendingBadgeUpdates) { - app.updatePackageBadge(pkg); - } - mPendingBadgeUpdates.clear(); + if (app != null) { + app.getModel().setPackageState(info); } } @@ -171,19 +85,18 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat implements @Override public void onCreated(int sessionId) { - pushSessionBadgeToLauncher(sessionId); + pushSessionDisplayToLauncher(sessionId); } @Override public void onFinished(int sessionId, boolean success) { - mPendingReplays.remove(sessionId); - SessionInfo session = mInstaller.getSessionInfo(sessionId); - if ((session != null) && (session.getAppPackageName() != null)) { - mPendingBadgeUpdates.remove(session.getAppPackageName()); - // Replay all updates with a one time update for this installed package. No - // need to store this record for future updates, as the app list will get - // refreshed on resume. - replayUpdates(new PackageInstallInfo(session.getAppPackageName(), + // For a finished session, we can't get the session info. So use the + // packageName from our local cache. + String packageName = mActiveSessions.get(sessionId); + mActiveSessions.remove(sessionId); + + if (packageName != null) { + sendUpdate(new PackageInstallInfo(packageName, success ? STATUS_INSTALLED : STATUS_FAILED, 0)); } } @@ -192,8 +105,9 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat implements public void onProgressChanged(int sessionId, float progress) { SessionInfo session = mInstaller.getSessionInfo(sessionId); if (session != null) { - mPendingReplays.put(sessionId, session); - replayUpdates(null); + sendUpdate(new PackageInstallInfo(session.getAppPackageName(), + STATUS_INSTALLING, + (int) (session.getProgress() * 100))); } } @@ -202,18 +116,18 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat implements @Override public void onBadgingChanged(int sessionId) { - pushSessionBadgeToLauncher(sessionId); + pushSessionDisplayToLauncher(sessionId); } - private void pushSessionBadgeToLauncher(int sessionId) { + private void pushSessionDisplayToLauncher(int sessionId) { SessionInfo session = mInstaller.getSessionInfo(sessionId); if (session != null) { addSessionInfoToCahce(session, UserHandleCompat.myUserHandle()); - if (session.getAppPackageName() != null) { - mPendingBadgeUpdates.add(session.getAppPackageName()); + LauncherAppState app = LauncherAppState.getInstanceNoCreate(); + + if (app != null) { + app.getModel().updateSessionDisplayInfo(session.getAppPackageName()); } - mPendingReplays.put(sessionId, session); - replayUpdates(null); } } }; -- cgit v1.2.3