Merge "Fixing missing updates in package install sessions" into ub-launcher3-burnaby

This commit is contained in:
Sunny Goyal
2015-04-23 17:26:34 +00:00
committed by Android (Google) Code Review
11 changed files with 225 additions and 427 deletions
+10 -6
View File
@@ -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;
@@ -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
+14 -22
View File
@@ -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<PackageInstallInfo> installInfo) {
if (mWorkspace != null) {
mWorkspace.updatePackageState(installInfo);
public void bindRestoreItemsChange(final HashSet<ItemInfo> 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);
}
/**
@@ -256,15 +256,4 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks {
public static boolean isDogfoodBuild() {
return getInstance().mBuildInfo.isDogfoodBuild();
}
public void setPackageState(ArrayList<PackageInstallInfo> 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);
}
}
+2 -1
View File
@@ -42,5 +42,6 @@ public class LauncherFiles {
// TODO: Delete these files on upgrade
public static final List<String> OBSOLETE_FILES = Collections.unmodifiableList(Arrays.asList(
"launches.log",
"stats.log"));
"stats.log",
"com.android.launcher3.compat.PackageInstallerCompatV16.queue"));
}
+149 -47
View File
@@ -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<ShortcutInfo> updated,
ArrayList<ShortcutInfo> removed, UserHandleCompat user);
public void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets);
public void updatePackageState(ArrayList<PackageInstallInfo> installInfo);
public void updatePackageBadge(String packageName);
public void bindRestoreItemsChange(HashSet<ItemInfo> updates);
public void bindComponentsRemoved(ArrayList<String> packageNames,
ArrayList<AppInfo> appInfos, UserHandleCompat user, int reason);
public void bindPackagesUpdated(ArrayList<Object> widgetsAndShortcuts);
@@ -282,30 +282,110 @@ public class LauncherModel extends BroadcastReceiver
return mOldContentProviderExists && !launcher.isLauncherPreinstalled() ;
}
public void setPackageState(final ArrayList<PackageInstallInfo> 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<ItemInfo> 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<ShortcutInfo> 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<ShortcutInfo>(), user);
}
}
};
mHandler.post(r);
}
}
}
};
mHandler.post(r);
runOnWorkerThread(updateRunnable);
}
public void addAppsToAllApps(final Context ctx, final ArrayList<AppInfo> 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<String> installingPkgs = PackageInstallerCompat
final HashMap<String, Integer> installingPkgs = PackageInstallerCompat
.getInstance(mContext).updateAndGetActiveSessionCache();
final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
@@ -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();
}
}
@@ -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,
+8 -61
View File
@@ -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<ItemInfo> 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<PackageInstallInfo> 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<LauncherAppWidgetInfo> changedInfo) {
if (!changedInfo.isEmpty()) {
DeferredWidgetRefresh widgetRefresh = new DeferredWidgetRefresh(changedInfo,
@@ -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<String> 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<String, Integer> updateAndGetActiveSessionCache();
public abstract void onStop();
public abstract void recordPackageUpdate(String packageName, int state, int progress);
public static final class PackageInstallInfo {
public final String packageName;
@@ -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<PackageInstallInfo> updates = new ArrayList<PackageInstallInfo>();
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<PackageInstallInfo> update = new ArrayList<PackageInstallInfo>();
update.add(installInfo);
sendUpdate(app, update);
}
}
}
private void sendUpdate(LauncherAppState app, ArrayList<PackageInstallInfo> 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<String> updateAndGetActiveSessionCache() {
return new HashSet<String>();
public HashMap<String, Integer> updateAndGetActiveSessionCache() {
return new HashMap<>();
}
}
@@ -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<SessionInfo> mPendingReplays = new SparseArray<SessionInfo>();
@Thunk final HashSet<String> mPendingBadgeUpdates = new HashSet<String>();
@Thunk final SparseArray<String> 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<String> updateAndGetActiveSessionCache() {
HashSet<String> activePackages = new HashSet<String>();
public HashMap<String, Integer> updateAndGetActiveSessionCache() {
HashMap<String, Integer> 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<PackageInstallInfo> updates = new ArrayList<PackageInstallInfo>();
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);
}
}
};