Merge "Fixing missing updates in package install sessions" into ub-launcher3-burnaby
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user