diff --git a/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java b/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java index e9324f9ce2..42a4f5cc99 100644 --- a/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java +++ b/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java @@ -29,7 +29,8 @@ public class PackageInstallStateChangedTaskTest extends BaseModelUpdateTaskTestC private PackageInstallStateChangedTask newTask(String pkg, int progress) { int state = PackageInstallerCompat.STATUS_INSTALLING; - PackageInstallInfo installInfo = new PackageInstallInfo(pkg, state, progress); + PackageInstallInfo installInfo = new PackageInstallInfo(pkg, state, progress, + android.os.Process.myUserHandle()); return new PackageInstallStateChangedTask(installInfo); } diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java index 351635c25c..9f05795922 100644 --- a/src/com/android/launcher3/InstallShortcutReceiver.java +++ b/src/com/android/launcher3/InstallShortcutReceiver.java @@ -30,7 +30,6 @@ import android.content.pm.PackageManager; import android.content.pm.ShortcutInfo; import android.graphics.Bitmap; import android.graphics.BitmapFactory; -import android.os.Looper; import android.os.Parcelable; import android.os.Process; import android.os.UserHandle; @@ -138,15 +137,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver { if (DBG) { Log.d(TAG, "Ignoring shortcut for absent package: " + info.launchIntent); } - continue; } - - // Generate a shortcut info to add into the model - installQueue.add(info.getItemInfo()); - } - prefs.edit().remove(APPS_PENDING_INSTALL).apply(); - if (!installQueue.isEmpty()) { - model.addAndBindAddedWorkspaceItems(installQueue); } } @@ -240,7 +231,8 @@ public class InstallShortcutReceiver extends BroadcastReceiver { } public static WorkspaceItemInfo fromActivityInfo(LauncherActivityInfo info, Context context) { - return (WorkspaceItemInfo) (new PendingInstallShortcutInfo(info, context).getItemInfo().first); + return (WorkspaceItemInfo) + new PendingInstallShortcutInfo(info, context).getItemInfo().first; } public static void queueShortcut(ShortcutInfo info, Context context) { @@ -251,8 +243,9 @@ public class InstallShortcutReceiver extends BroadcastReceiver { queuePendingShortcutInfo(new PendingInstallShortcutInfo(info, widgetId, context), context); } - public static void queueActivityInfo(LauncherActivityInfo activity, Context context) { - queuePendingShortcutInfo(new PendingInstallShortcutInfo(activity, context), context); + public static void queueApplication(Intent data, UserHandle user, Context context) { + queuePendingShortcutInfo(new PendingInstallShortcutInfo(data, context, user), + context); } public static HashSet getPendingShortcuts(Context context) { @@ -316,7 +309,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver { private static class PendingInstallShortcutInfo { - final LauncherActivityInfo activityInfo; + final boolean isActivity; final ShortcutInfo shortcutInfo; final AppWidgetProviderInfo providerInfo; @@ -330,7 +323,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver { * Initializes a PendingInstallShortcutInfo received from a different app. */ public PendingInstallShortcutInfo(Intent data, UserHandle user, Context context) { - activityInfo = null; + isActivity = false; shortcutInfo = null; providerInfo = null; @@ -340,14 +333,13 @@ public class InstallShortcutReceiver extends BroadcastReceiver { launchIntent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT); label = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME); - } /** * Initializes a PendingInstallShortcutInfo to represent a launcher target. */ public PendingInstallShortcutInfo(LauncherActivityInfo info, Context context) { - activityInfo = info; + isActivity = true; shortcutInfo = null; providerInfo = null; @@ -359,11 +351,27 @@ public class InstallShortcutReceiver extends BroadcastReceiver { label = info.getLabel().toString(); } + /** + * Initializes a PendingInstallShortcutInfo to represent a launcher target. + */ + public PendingInstallShortcutInfo(Intent data, Context context, UserHandle user) { + isActivity = true; + shortcutInfo = null; + providerInfo = null; + + this.data = data; + this.user = user; + mContext = context; + + launchIntent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT); + label = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME); + } + /** * Initializes a PendingInstallShortcutInfo to represent a launcher target. */ public PendingInstallShortcutInfo(ShortcutInfo info, Context context) { - activityInfo = null; + isActivity = false; shortcutInfo = info; providerInfo = null; @@ -380,7 +388,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver { */ public PendingInstallShortcutInfo( AppWidgetProviderInfo info, int widgetId, Context context) { - activityInfo = null; + isActivity = false; shortcutInfo = null; providerInfo = info; @@ -395,17 +403,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver { public String encodeToString() { try { - if (activityInfo != null) { - // If it a launcher target, we only need component name, and user to - // recreate this. - return new JSONStringer() - .object() - .key(LAUNCH_INTENT_KEY).value(launchIntent.toUri(0)) - .key(APP_SHORTCUT_TYPE_KEY).value(true) - .key(USER_HANDLE_KEY).value(UserManagerCompat.getInstance(mContext) - .getSerialNumberForUser(user)) - .endObject().toString(); - } else if (shortcutInfo != null) { + if (shortcutInfo != null) { // If it a launcher target, we only need component name, and user to // recreate this. return new JSONStringer() @@ -447,7 +445,8 @@ public class InstallShortcutReceiver extends BroadcastReceiver { JSONStringer json = new JSONStringer() .object() .key(LAUNCH_INTENT_KEY).value(launchIntent.toUri(0)) - .key(NAME_KEY).value(name); + .key(NAME_KEY).value(name) + .key(APP_SHORTCUT_TYPE_KEY).value(isActivity); if (icon != null) { byte[] iconByteArray = GraphicsUtils.flattenBitmap(icon); json = json.key(ICON_KEY).value( @@ -467,29 +466,18 @@ public class InstallShortcutReceiver extends BroadcastReceiver { } public Pair getItemInfo() { - if (activityInfo != null) { - AppInfo appInfo = new AppInfo(mContext, activityInfo, user); - final LauncherAppState app = LauncherAppState.getInstance(mContext); - // Set default values until proper values is loaded. - appInfo.title = ""; - appInfo.applyFrom(app.getIconCache().getDefaultIcon(user)); - final WorkspaceItemInfo si = appInfo.makeWorkspaceItem(); - if (Looper.myLooper() == MODEL_EXECUTOR.getLooper()) { - app.getIconCache().getTitleAndIcon(si, activityInfo, false /* useLowResIcon */); - } else { - app.getModel().updateAndBindWorkspaceItem(() -> { - app.getIconCache().getTitleAndIcon( - si, activityInfo, false /* useLowResIcon */); - return si; - }); - } - return Pair.create((ItemInfo) si, (Object) activityInfo); + if (isActivity) { + WorkspaceItemInfo si = createWorkspaceItemInfo(data, + LauncherAppState.getInstance(mContext)); + si.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; + si.status |= WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON; + return Pair.create(si, null); } else if (shortcutInfo != null) { - WorkspaceItemInfo si = new WorkspaceItemInfo(shortcutInfo, mContext); + WorkspaceItemInfo itemInfo = new WorkspaceItemInfo(shortcutInfo, mContext); LauncherIcons li = LauncherIcons.obtain(mContext); - si.applyFrom(li.createShortcutIcon(shortcutInfo)); + itemInfo.applyFrom(li.createShortcutIcon(shortcutInfo)); li.recycle(); - return Pair.create((ItemInfo) si, (Object) shortcutInfo); + return Pair.create(itemInfo, shortcutInfo); } else if (providerInfo != null) { LauncherAppWidgetProviderInfo info = LauncherAppWidgetProviderInfo .fromProviderInfo(mContext, providerInfo); @@ -501,15 +489,16 @@ public class InstallShortcutReceiver extends BroadcastReceiver { widgetInfo.minSpanY = info.minSpanY; widgetInfo.spanX = Math.min(info.spanX, idp.numColumns); widgetInfo.spanY = Math.min(info.spanY, idp.numRows); - return Pair.create((ItemInfo) widgetInfo, (Object) providerInfo); + return Pair.create(widgetInfo, providerInfo); } else { - WorkspaceItemInfo si = createWorkspaceItemInfo(data, LauncherAppState.getInstance(mContext)); - return Pair.create((ItemInfo) si, null); + WorkspaceItemInfo itemInfo = + createWorkspaceItemInfo(data, LauncherAppState.getInstance(mContext)); + return Pair.create(itemInfo, null); } } public boolean isLauncherActivity() { - return activityInfo != null; + return isActivity; } } @@ -524,7 +513,9 @@ public class InstallShortcutReceiver extends BroadcastReceiver { if (decoder.optBoolean(APP_SHORTCUT_TYPE_KEY)) { LauncherActivityInfo info = LauncherAppsCompat.getInstance(context) .resolveActivity(decoder.launcherIntent, decoder.user); - return info == null ? null : new PendingInstallShortcutInfo(info, context); + if (info != null) { + return new PendingInstallShortcutInfo(info, context); + } } else if (decoder.optBoolean(DEEPSHORTCUT_TYPE_KEY)) { DeepShortcutManager sm = DeepShortcutManager.getInstance(context); List si = sm.queryForFullDetails( @@ -568,7 +559,11 @@ public class InstallShortcutReceiver extends BroadcastReceiver { data.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconResource); } - return new PendingInstallShortcutInfo(data, decoder.user, context); + if (decoder.optBoolean(APP_SHORTCUT_TYPE_KEY)) { + return new PendingInstallShortcutInfo(data, context, decoder.user); + } else { + return new PendingInstallShortcutInfo(data, decoder.user, context); + } } catch (JSONException | URISyntaxException e) { Log.d(TAG, "Exception reading shortcut to add: " + e); } diff --git a/src/com/android/launcher3/SessionCommitReceiver.java b/src/com/android/launcher3/SessionCommitReceiver.java index a1c77ef1d1..6853bf6941 100644 --- a/src/com/android/launcher3/SessionCommitReceiver.java +++ b/src/com/android/launcher3/SessionCommitReceiver.java @@ -18,6 +18,7 @@ package com.android.launcher3; import android.annotation.TargetApi; import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; @@ -27,6 +28,7 @@ import android.content.pm.PackageInstaller.SessionInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.database.Cursor; +import android.graphics.Bitmap; import android.net.Uri; import android.os.AsyncTask; import android.os.Build; @@ -37,9 +39,12 @@ import android.util.Log; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.util.Executors; +import com.android.launcher3.compat.PackageInstallerCompat; import java.util.List; +import static com.android.launcher3.compat.PackageInstallerCompat.getUserHandle; + /** * BroadcastReceiver to handle session commit intent. */ @@ -66,15 +71,29 @@ public class SessionCommitReceiver extends BroadcastReceiver { SessionInfo info = intent.getParcelableExtra(PackageInstaller.EXTRA_SESSION); UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER); + PackageInstallerCompat packageInstallerCompat = PackageInstallerCompat.getInstance(context); - if (TextUtils.isEmpty(info.getAppPackageName()) || - info.getInstallReason() != PackageManager.INSTALL_REASON_USER) { + if (TextUtils.isEmpty(info.getAppPackageName()) + || info.getInstallReason() != PackageManager.INSTALL_REASON_USER + || packageInstallerCompat.promiseIconAddedForId(info.getSessionId())) { + packageInstallerCompat.removePromiseIconId(info.getSessionId()); return; } queueAppIconAddition(context, info.getAppPackageName(), user); } + public static void queuePromiseAppIconAddition(Context context, SessionInfo sessionInfo) { + String packageName = sessionInfo.getAppPackageName(); + List activities = LauncherAppsCompat.getInstance(context) + .getActivityList(packageName, getUserHandle(sessionInfo)); + if (activities == null || activities.isEmpty()) { + // Ensure application isn't already installed. + queueAppIconAddition(context, packageName, sessionInfo.getAppLabel(), + sessionInfo.getAppIcon(), getUserHandle(sessionInfo)); + } + } + public static void queueAppIconAddition(Context context, String packageName, UserHandle user) { List activities = LauncherAppsCompat.getInstance(context) .getActivityList(packageName, user); @@ -82,7 +101,18 @@ public class SessionCommitReceiver extends BroadcastReceiver { // no activity found return; } - InstallShortcutReceiver.queueActivityInfo(activities.get(0), context); + queueAppIconAddition(context, packageName, activities.get(0).getLabel(), null, user); + } + + private static void queueAppIconAddition(Context context, String packageName, + CharSequence label, Bitmap icon, UserHandle user) { + Intent data = new Intent(); + data.putExtra(Intent.EXTRA_SHORTCUT_INTENT, new Intent().setComponent( + new ComponentName(packageName, "")).setPackage(packageName)); + data.putExtra(Intent.EXTRA_SHORTCUT_NAME, label); + data.putExtra(Intent.EXTRA_SHORTCUT_ICON, icon); + + InstallShortcutReceiver.queueApplication(data, user, context); } public static boolean isEnabled(Context context) { diff --git a/src/com/android/launcher3/WorkspaceItemInfo.java b/src/com/android/launcher3/WorkspaceItemInfo.java index 9a9aa976f0..1323588e32 100644 --- a/src/com/android/launcher3/WorkspaceItemInfo.java +++ b/src/com/android/launcher3/WorkspaceItemInfo.java @@ -50,24 +50,26 @@ public class WorkspaceItemInfo extends ItemInfoWithIcon { * The icon was added as an auto-install app, and is not ready to be used. This flag can't * be present along with {@link #FLAG_RESTORED_ICON}, and is set during default layout * parsing. + * + * OR this icon was added due to it being an active install session created by the user. */ - public static final int FLAG_AUTOINSTALL_ICON = 2; //0B10; + public static final int FLAG_AUTOINSTALL_ICON = 1 << 1; /** * The icon is being installed. If {@link #FLAG_RESTORED_ICON} or {@link #FLAG_AUTOINSTALL_ICON} * is set, then the icon is either being installed or is in a broken state. */ - public static final int FLAG_INSTALL_SESSION_ACTIVE = 4; // 0B100; + public static final int FLAG_INSTALL_SESSION_ACTIVE = 1 << 2; /** * Indicates that the widget restore has started. */ - public static final int FLAG_RESTORE_STARTED = 8; //0B1000; + public static final int FLAG_RESTORE_STARTED = 1 << 3; /** * Web UI supported. */ - public static final int FLAG_SUPPORTS_WEB_UI = 16; //0B10000; + public static final int FLAG_SUPPORTS_WEB_UI = 1 << 4; /** * The intent used to start the application. @@ -210,7 +212,7 @@ public class WorkspaceItemInfo extends ItemInfoWithIcon { public ComponentName getTargetComponent() { ComponentName cn = super.getTargetComponent(); if (cn == null && (itemType == Favorites.ITEM_TYPE_SHORTCUT - || hasStatusFlag(FLAG_SUPPORTS_WEB_UI))) { + || hasStatusFlag(FLAG_SUPPORTS_WEB_UI | FLAG_AUTOINSTALL_ICON))) { // Legacy shortcuts and promise icons with web UI may not have a componentName but just // a packageName. In that case create a dummy componentName instead of adding additional // check everywhere. diff --git a/src/com/android/launcher3/compat/PackageInstallerCompat.java b/src/com/android/launcher3/compat/PackageInstallerCompat.java index 4f4d64161a..11cb1f88d0 100644 --- a/src/com/android/launcher3/compat/PackageInstallerCompat.java +++ b/src/com/android/launcher3/compat/PackageInstallerCompat.java @@ -19,15 +19,23 @@ package com.android.launcher3.compat; import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageInstaller; +import android.os.Process; import android.os.UserHandle; +import java.util.Collections; import java.util.HashMap; import java.util.List; import androidx.annotation.NonNull; +import com.android.launcher3.Utilities; + public abstract class PackageInstallerCompat { + // Set of session ids of promise icons that have been added to the home screen + // as FLAG_PROMISE_NEW_INSTALLS. + protected static final String PROMISE_ICON_IDS = "promise_icon_ids"; + public static final int STATUS_INSTALLED = 0; public static final int STATUS_INSTALLING = 1; public static final int STATUS_FAILED = 2; @@ -44,6 +52,10 @@ public abstract class PackageInstallerCompat { } } + public static UserHandle getUserHandle(PackageInstaller.SessionInfo info) { + return Utilities.ATLEAST_Q ? info.getUser() : Process.myUserHandle(); + } + /** * @return a map of active installs to their progress */ @@ -61,30 +73,44 @@ public abstract class PackageInstallerCompat { public final String packageName; public final int state; public final int progress; + public final UserHandle user; private PackageInstallInfo(@NonNull PackageInstaller.SessionInfo info) { this.state = STATUS_INSTALLING; this.packageName = info.getAppPackageName(); this.componentName = new ComponentName(packageName, ""); this.progress = (int) (info.getProgress() * 100f); + this.user = getUserHandle(info); } - public PackageInstallInfo(String packageName, int state, int progress) { + public PackageInstallInfo(String packageName, int state, int progress, UserHandle user) { this.state = state; this.packageName = packageName; this.componentName = new ComponentName(packageName, ""); this.progress = progress; + this.user = user; } public static PackageInstallInfo fromInstallingState(PackageInstaller.SessionInfo info) { return new PackageInstallInfo(info); } - public static PackageInstallInfo fromState(int state, String packageName) { - return new PackageInstallInfo(packageName, state, 0 /* progress */); + public static PackageInstallInfo fromState(int state, String packageName, UserHandle user) { + return new PackageInstallInfo(packageName, state, 0 /* progress */, user); } } public abstract List getAllVerifiedSessions(); + + /** + * Returns true if a promise icon was already added to the home screen for {@param sessionId}. + * Applicable only for icons with flag FLAG_PROMISE_NEW_INSTALLS. + */ + public abstract boolean promiseIconAddedForId(int sessionId); + + /** + * Applicable only for icons with flag FLAG_PROMISE_NEW_INSTALLS. + */ + public abstract void removePromiseIconId(int sessionId); } diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java index 052b0c373e..607be5b614 100644 --- a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java +++ b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java @@ -23,15 +23,20 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageInstaller; import android.content.pm.PackageInstaller.SessionCallback; import android.content.pm.PackageInstaller.SessionInfo; -import android.os.Process; +import android.content.pm.PackageManager; import android.os.UserHandle; import android.text.TextUtils; import android.util.SparseArray; -import com.android.launcher3.LauncherAppState; +import com.android.launcher3.LauncherModel; +import com.android.launcher3.SessionCommitReceiver; import com.android.launcher3.Utilities; -import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.icons.IconCache; +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.util.IntArray; +import com.android.launcher3.util.IntSet; +import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.util.Thunk; import java.util.ArrayList; @@ -39,17 +44,20 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; +import static com.android.launcher3.Utilities.getPrefs; + public class PackageInstallerCompatVL extends PackageInstallerCompat { private static final boolean DEBUG = false; - @Thunk final SparseArray mActiveSessions = new SparseArray<>(); + @Thunk final SparseArray mActiveSessions = new SparseArray<>(); @Thunk final PackageInstaller mInstaller; private final IconCache mCache; private final Context mAppContext; private final HashMap mSessionVerifiedMap = new HashMap<>(); private final LauncherAppsCompat mLauncherApps; + private final IntSet mPromiseIconIds; PackageInstallerCompatVL(Context context) { mAppContext = context.getApplicationContext(); @@ -57,17 +65,38 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat { mCache = LauncherAppState.getInstance(context).getIconCache(); mInstaller.registerSessionCallback(mCallback, MODEL_EXECUTOR.getHandler()); mLauncherApps = LauncherAppsCompat.getInstance(context); + mPromiseIconIds = IntSet.wrap(IntArray.fromConcatString( + getPrefs(context).getString(PROMISE_ICON_IDS, ""))); + + cleanUpPromiseIconIds(); + } + + private void cleanUpPromiseIconIds() { + IntArray existingIds = new IntArray(); + for (SessionInfo info : updateAndGetActiveSessionCache().values()) { + existingIds.add(info.getSessionId()); + } + IntArray idsToRemove = new IntArray(); + + for (int i = mPromiseIconIds.size() - 1; i >= 0; --i) { + if (!existingIds.contains(mPromiseIconIds.getArray().get(i))) { + idsToRemove.add(mPromiseIconIds.getArray().get(i)); + } + } + for (int i = idsToRemove.size() - 1; i >= 0; --i) { + mPromiseIconIds.getArray().removeValue(idsToRemove.get(i)); + } } @Override public HashMap updateAndGetActiveSessionCache() { HashMap activePackages = new HashMap<>(); - UserHandle primaryUser = Process.myUserHandle(); for (SessionInfo info : getAllVerifiedSessions()) { - addSessionInfoToCache(info, Utilities.ATLEAST_Q ? info.getUser() : primaryUser); + addSessionInfoToCache(info, getUserHandle(info)); if (info.getAppPackageName() != null) { activePackages.put(info.getAppPackageName(), info); - mActiveSessions.put(info.getSessionId(), info.getAppPackageName()); + mActiveSessions.put(info.getSessionId(), + new PackageUserKey(info.getAppPackageName(), getUserHandle(info))); } } return activePackages; @@ -76,7 +105,7 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat { public SessionInfo getActiveSessionInfo(UserHandle user, String pkg) { for (SessionInfo info : getAllVerifiedSessions()) { boolean match = pkg.equals(info.getAppPackageName()); - if (Utilities.ATLEAST_Q && !user.equals(info.getUser())) { + if (Utilities.ATLEAST_Q && !user.equals(getUserHandle(info))) { match = false; } if (match) { @@ -118,19 +147,38 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat { PackageInstallInfo.fromInstallingState(sessionInfo)); } } + + if (Utilities.ATLEAST_OREO && FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get() + && SessionCommitReceiver.isEnabled(mAppContext) + && sessionInfo != null + && sessionInfo.getInstallReason() == PackageManager.INSTALL_REASON_USER) { + SessionCommitReceiver.queuePromiseAppIconAddition(mAppContext, sessionInfo); + if (!mPromiseIconIds.contains(sessionInfo.getSessionId())) { + mPromiseIconIds.add(sessionInfo.getSessionId()); + updatePromiseIconPrefs(); + } + } } @Override public void onFinished(int sessionId, boolean success) { // For a finished session, we can't get the session info. So use the // packageName from our local cache. - String packageName = mActiveSessions.get(sessionId); + PackageUserKey key = mActiveSessions.get(sessionId); mActiveSessions.remove(sessionId); - if (packageName != null) { - sendUpdate(PackageInstallInfo.fromState( - success ? STATUS_INSTALLED : STATUS_FAILED, - packageName)); + if (key != null && key.mPackageName != null) { + String packageName = key.mPackageName; + sendUpdate(PackageInstallInfo.fromState(success ? STATUS_INSTALLED : STATUS_FAILED, + packageName, key.mUser)); + + if (!success && FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()) { + LauncherAppState appState = LauncherAppState.getInstanceNoCreate(); + if (appState != null) { + LauncherModel model = appState.getModel(); + model.onPackageRemoved(packageName, key.mUser); + } + } } } @@ -153,8 +201,9 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat { private SessionInfo pushSessionDisplayToLauncher(int sessionId) { SessionInfo session = verify(mInstaller.getSessionInfo(sessionId)); if (session != null && session.getAppPackageName() != null) { - mActiveSessions.put(sessionId, session.getAppPackageName()); - addSessionInfoToCache(session, Process.myUserHandle()); + mActiveSessions.put(session.getSessionId(), + new PackageUserKey(session.getAppPackageName(), getUserHandle(session))); + addSessionInfoToCache(session, getUserHandle(session)); LauncherAppState app = LauncherAppState.getInstanceNoCreate(); if (app != null) { app.getModel().updateSessionDisplayInfo(session.getAppPackageName()); @@ -176,7 +225,7 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat { if (!mSessionVerifiedMap.containsKey(pkg)) { LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(mAppContext); boolean hasSystemFlag = launcherApps.getApplicationInfo(pkg, - ApplicationInfo.FLAG_SYSTEM, Process.myUserHandle()) != null; + ApplicationInfo.FLAG_SYSTEM, getUserHandle(sessionInfo)) != null; mSessionVerifiedMap.put(pkg, DEBUG || hasSystemFlag); } } @@ -196,4 +245,23 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat { } return list; } + + @Override + public boolean promiseIconAddedForId(int sessionId) { + return mPromiseIconIds.contains(sessionId); + } + + @Override + public void removePromiseIconId(int sessionId) { + if (mPromiseIconIds.contains(sessionId)) { + mPromiseIconIds.getArray().removeValue(sessionId); + updatePromiseIconPrefs(); + } + } + + private void updatePromiseIconPrefs() { + getPrefs(mAppContext).edit() + .putString(PROMISE_ICON_IDS, mPromiseIconIds.getArray().toConcatString()) + .apply(); + } } diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java index 7df982926d..15232781d2 100644 --- a/src/com/android/launcher3/config/BaseFlags.java +++ b/src/com/android/launcher3/config/BaseFlags.java @@ -60,6 +60,11 @@ public abstract class BaseFlags { // When enabled the promise icon is visible in all apps while installation an app. public static final boolean LAUNCHER3_PROMISE_APPS_IN_ALL_APPS = false; + // When enabled a promise icon is added to the home screen when install session is active. + public static final TogglableFlag PROMISE_APPS_NEW_INSTALLS = + new TogglableFlag("PROMISE_APPS_NEW_INSTALLS", true, + "Adds a promise icon to the home screen for new install sessions."); + // Enable moving the QSB on the 0th screen of the workspace public static final boolean QSB_ON_FIRST_SCREEN = true; diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java index 935326d75c..6e60773d9f 100644 --- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java +++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java @@ -16,6 +16,8 @@ package com.android.launcher3.model; import android.content.Intent; +import android.content.pm.LauncherActivityInfo; +import android.content.pm.PackageInstaller.SessionInfo; import android.os.UserHandle; import android.util.LongSparseArray; import android.util.Pair; @@ -30,6 +32,8 @@ import com.android.launcher3.LauncherModel.CallbackTask; import com.android.launcher3.model.BgDataModel.Callbacks; import com.android.launcher3.LauncherSettings; import com.android.launcher3.WorkspaceItemInfo; +import com.android.launcher3.compat.LauncherAppsCompat; +import com.android.launcher3.compat.PackageInstallerCompat; import com.android.launcher3.util.GridOccupancy; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.PackageManagerHelper; @@ -84,6 +88,10 @@ public class AddWorkspaceItemsTask extends BaseModelUpdateTask { } } + PackageInstallerCompat packageInstaller = + PackageInstallerCompat.getInstance(app.getContext()); + LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(app.getContext()); + for (ItemInfo item : filteredItems) { // Find appropriate space for the item. int[] coords = findSpaceForItem(app, dataModel, workspaceScreens, @@ -100,6 +108,38 @@ public class AddWorkspaceItemsTask extends BaseModelUpdateTask { throw new RuntimeException("Unexpected info type"); } + if (item instanceof WorkspaceItemInfo && ((WorkspaceItemInfo) item).isPromise()) { + WorkspaceItemInfo workspaceInfo = (WorkspaceItemInfo) item; + String packageName = item.getTargetComponent() != null + ? item.getTargetComponent().getPackageName() : null; + if (packageName == null) { + continue; + } + SessionInfo sessionInfo = packageInstaller.getActiveSessionInfo(item.user, + packageName); + if (sessionInfo == null) { + List activities = launcherApps + .getActivityList(packageName, item.user); + if (activities != null && !activities.isEmpty()) { + // App was installed while launcher was in the background. + itemInfo = new AppInfo(app.getContext(), activities.get(0), item.user) + .makeWorkspaceItem(); + PackageItemInfo info = new PackageItemInfo(packageName); + WorkspaceItemInfo wii = (WorkspaceItemInfo) itemInfo; + app.getIconCache().getTitleAndIconForApp(info, wii.usingLowResIcon()); + wii.title = info.title; + wii.contentDescription = info.contentDescription; + wii.iconBitmap = info.iconBitmap; + wii.iconColor = info.iconColor; + } else { + // Session was cancelled, do not add. + continue; + } + } else { + workspaceInfo.setInstallProgress((int) sessionInfo.getProgress()); + } + } + // Add the shortcut to the db getModelWriter().addItemToDatabase(itemInfo, LauncherSettings.Favorites.CONTAINER_DESKTOP, screenId, diff --git a/src/com/android/launcher3/model/AllAppsList.java b/src/com/android/launcher3/model/AllAppsList.java index 370a8128d1..3873a17e03 100644 --- a/src/com/android/launcher3/model/AllAppsList.java +++ b/src/com/android/launcher3/model/AllAppsList.java @@ -113,7 +113,7 @@ public class AllAppsList { public void addPromiseApp(Context context, PackageInstallerCompat.PackageInstallInfo installInfo) { ApplicationInfo applicationInfo = LauncherAppsCompat.getInstance(context) - .getApplicationInfo(installInfo.packageName, 0, Process.myUserHandle()); + .getApplicationInfo(installInfo.packageName, 0, installInfo.user); // only if not yet installed if (applicationInfo == null) { PromiseAppInfo info = new PromiseAppInfo(installInfo); diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java index 1a03b77714..1c39d1f23c 100644 --- a/src/com/android/launcher3/model/LoaderCursor.java +++ b/src/com/android/launcher3/model/LoaderCursor.java @@ -227,7 +227,7 @@ public class LoaderCursor extends CursorWrapper { if (!TextUtils.isEmpty(title)) { info.title = Utilities.trim(title); } - } else if (hasRestoreFlag(WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON)) { + } else if (hasRestoreFlag(WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON)) { if (TextUtils.isEmpty(info.title)) { info.title = getTitle(); } diff --git a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java index e43412d60c..802cbc7c5e 100644 --- a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java +++ b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java @@ -18,7 +18,6 @@ package com.android.launcher3.model; import android.content.ComponentName; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; -import android.os.Process; import com.android.launcher3.AppInfo; import com.android.launcher3.ItemInfo; @@ -54,7 +53,7 @@ public class PackageInstallStateChangedTask extends BaseModelUpdateTask { ApplicationInfo ai = app.getContext() .getPackageManager().getApplicationInfo(mInstallInfo.packageName, 0); if (InstantAppResolver.newInstance(app.getContext()).isInstantApp(ai)) { - app.getModel().onPackageAdded(ai.packageName, Process.myUserHandle()); + app.getModel().onPackageAdded(ai.packageName, mInstallInfo.user); } } catch (PackageManager.NameNotFoundException e) { // Ignore diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java index 17a9a02d56..4adf59a331 100644 --- a/src/com/android/launcher3/model/PackageUpdatedTask.java +++ b/src/com/android/launcher3/model/PackageUpdatedTask.java @@ -52,6 +52,8 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; +import static com.android.launcher3.WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON; + /** * Handles updates due to changes in package manager (app installed/updated/removed) * or when a user availability changes. @@ -98,7 +100,7 @@ public class PackageUpdatedTask extends BaseModelUpdateTask { if (DEBUG) Log.d(TAG, "mAllAppsList.addPackage " + packages[i]); iconCache.updateIconsForPkg(packages[i], mUser); if (FeatureFlags.LAUNCHER3_PROMISE_APPS_IN_ALL_APPS) { - appsList.removePackage(packages[i], Process.myUserHandle()); + appsList.removePackage(packages[i], mUser); } appsList.addPackage(context, packages[i], mUser); @@ -213,8 +215,7 @@ public class PackageUpdatedTask extends BaseModelUpdateTask { isTargetValid = LauncherAppsCompat.getInstance(context) .isActivityEnabledForProfile(cn, mUser); } - if (si.hasStatusFlag(WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON) - && !isTargetValid) { + if (si.hasStatusFlag(FLAG_AUTOINSTALL_ICON)) { if (updateWorkspaceItemIntent(context, si, packageName)) { infoUpdated = true; } else if (si.hasPromiseIconUi()) {