diff options
11 files changed, 241 insertions, 452 deletions
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java index ce8557065..b136e7d81 100644 --- a/src/com/android/launcher3/InstallShortcutReceiver.java +++ b/src/com/android/launcher3/InstallShortcutReceiver.java @@ -34,6 +34,7 @@ import android.os.UserHandle; import android.text.TextUtils; import android.util.Base64; import android.util.Log; +import android.util.Pair; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.UserManagerCompat; @@ -59,6 +60,16 @@ import java.util.List; import java.util.Set; public class InstallShortcutReceiver extends BroadcastReceiver { + + public static final int FLAG_ACTIVITY_PAUSED = 1; + public static final int FLAG_LOADER_RUNNING = 2; + public static final int FLAG_DRAG_AND_DROP = 4; + public static final int FLAG_BULK_ADD = 4; + + // Determines whether to defer installing shortcuts immediately until + // processAllPendingInstalls() is called. + private static int sInstallQueueDisabledFlags = 0; + private static final String TAG = "InstallShortcutReceiver"; private static final boolean DBG = false; @@ -151,10 +162,6 @@ public class InstallShortcutReceiver extends BroadcastReceiver { } } - // Determines whether to defer installing shortcuts immediately until - // processAllPendingInstalls() is called. - private static boolean mUseInstallQueue = false; - public void onReceive(Context context, Intent data) { if (!ACTION_INSTALL_SHORTCUT.equals(data.getAction())) { return; @@ -207,7 +214,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver { public static ShortcutInfo fromShortcutIntent(Context context, Intent data) { PendingInstallShortcutInfo info = createPendingInfo(context, data); - return info == null ? null : (ShortcutInfo) info.getItemInfo(); + return info == null ? null : (ShortcutInfo) info.getItemInfo().first; } public static void queueShortcut(ShortcutInfoCompat info, Context context) { @@ -245,27 +252,28 @@ public class InstallShortcutReceiver extends BroadcastReceiver { private static void queuePendingShortcutInfo(PendingInstallShortcutInfo info, Context context) { // Queue the item up for adding if launcher has not loaded properly yet - LauncherAppState app = LauncherAppState.getInstance(context); - boolean launcherNotLoaded = app.getModel().getCallback() == null; - addToInstallQueue(Utilities.getPrefs(context), info); - if (!mUseInstallQueue && !launcherNotLoaded) { - flushInstallQueue(context); - } + flushInstallQueue(context); } - static void enableInstallQueue() { - mUseInstallQueue = true; + public static void enableInstallQueue(int flag) { + sInstallQueueDisabledFlags |= flag; } - static void disableAndFlushInstallQueue(Context context) { - mUseInstallQueue = false; + public static void disableAndFlushInstallQueue(int flag, Context context) { + sInstallQueueDisabledFlags &= ~flag; flushInstallQueue(context); } static void flushInstallQueue(Context context) { + LauncherModel model = LauncherAppState.getInstance(context).getModel(); + boolean launcherNotLoaded = model.getCallback() == null; + if (sInstallQueueDisabledFlags != 0 || launcherNotLoaded) { + return; + } + ArrayList<PendingInstallShortcutInfo> items = getAndClearInstallQueue(context); if (!items.isEmpty()) { - LauncherAppState.getInstance(context).getModel().addAndBindAddedWorkspaceItems( + model.addAndBindAddedWorkspaceItems( new LazyShortcutsProvider(context.getApplicationContext(), items)); } } @@ -439,7 +447,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver { } } - public ItemInfo getItemInfo() { + public Pair<ItemInfo, Object> getItemInfo() { if (activityInfo != null) { AppInfo appInfo = new AppInfo(mContext, activityInfo, user); final LauncherAppState app = LauncherAppState.getInstance(mContext); @@ -459,11 +467,11 @@ public class InstallShortcutReceiver extends BroadcastReceiver { } }); } - return si; + return Pair.create((ItemInfo) si, (Object) activityInfo); } else if (shortcutInfo != null) { ShortcutInfo si = new ShortcutInfo(shortcutInfo, mContext); si.iconBitmap = LauncherIcons.createShortcutIcon(shortcutInfo, mContext); - return si; + return Pair.create((ItemInfo) si, (Object) shortcutInfo); } else if (providerInfo != null) { LauncherAppWidgetProviderInfo info = LauncherAppWidgetProviderInfo .fromProviderInfo(mContext, providerInfo); @@ -475,9 +483,10 @@ 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 widgetInfo; + return Pair.create((ItemInfo) widgetInfo, (Object) providerInfo); } else { - return createShortcutInfo(data, LauncherAppState.getInstance(mContext)); + ShortcutInfo si = createShortcutInfo(data, LauncherAppState.getInstance(mContext)); + return Pair.create((ItemInfo) si, null); } } @@ -588,7 +597,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver { return new PendingInstallShortcutInfo(info, original.mContext); } - private static class LazyShortcutsProvider extends Provider<List<ItemInfo>> { + private static class LazyShortcutsProvider extends Provider<List<Pair<ItemInfo, Object>>> { private final Context mContext; private final ArrayList<PendingInstallShortcutInfo> mPendingItems; @@ -603,9 +612,9 @@ public class InstallShortcutReceiver extends BroadcastReceiver { * packageManager and icon cache. */ @Override - public ArrayList<ItemInfo> get() { + public ArrayList<Pair<ItemInfo, Object>> get() { Preconditions.assertNonUiThread(); - ArrayList<ItemInfo> installQueue = new ArrayList<>(); + ArrayList<Pair<ItemInfo, Object>> installQueue = new ArrayList<>(); LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(mContext); for (PendingInstallShortcutInfo pendingInfo : mPendingItems) { // If the intent specifies a package, make sure the package exists diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index b63bbd548..b9b561020 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -1036,13 +1036,12 @@ public class Launcher extends BaseActivity updateInteraction(Workspace.State.NORMAL, mWorkspace.getState()); mWorkspace.onResume(); - if (!isWorkspaceLoading()) { - // Process any items that were added while Launcher was away. - InstallShortcutReceiver.disableAndFlushInstallQueue(this); + // Process any items that were added while Launcher was away. + InstallShortcutReceiver.disableAndFlushInstallQueue( + InstallShortcutReceiver.FLAG_ACTIVITY_PAUSED, this); - // Refresh shortcuts if the permission changed. - mModel.refreshShortcutsIfRequired(); - } + // Refresh shortcuts if the permission changed. + mModel.refreshShortcutsIfRequired(); if (shouldShowDiscoveryBounce()) { mAllAppsController.showDiscoveryBounce(); @@ -1057,7 +1056,7 @@ public class Launcher extends BaseActivity @Override protected void onPause() { // Ensure that items added to Launcher are queued until Launcher returns - InstallShortcutReceiver.enableInstallQueue(); + InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_ACTIVITY_PAUSED); super.onPause(); mPaused = true; @@ -3655,7 +3654,8 @@ public class Launcher extends BaseActivity mPendingActivityResult = null; } - InstallShortcutReceiver.disableAndFlushInstallQueue(this); + InstallShortcutReceiver.disableAndFlushInstallQueue( + InstallShortcutReceiver.FLAG_LOADER_RUNNING, this); NotificationListener.setNotificationsChangedListener(mPopupDataProvider); diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 12789c55a..b5ca301d0 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -40,6 +40,7 @@ import android.text.TextUtils; import android.util.Log; import android.util.LongSparseArray; import android.util.MutableInt; +import android.util.Pair; import com.android.launcher3.compat.AppWidgetManagerCompat; import com.android.launcher3.compat.LauncherAppsCompat; @@ -138,12 +139,6 @@ public class LauncherModel extends BroadcastReceiver } } - /** - * Set of runnables to be called on the background thread after the workspace binding - * is complete. - */ - static final ArrayList<Runnable> mBindCompleteRunnables = new ArrayList<Runnable>(); - @Thunk WeakReference<Callbacks> mCallbacks; // < only access in worker thread > @@ -251,15 +246,8 @@ public class LauncherModel extends BroadcastReceiver /** * Adds the provided items to the workspace. */ - public void addAndBindAddedWorkspaceItems(List<ItemInfo> workspaceApps) { - addAndBindAddedWorkspaceItems(Provider.of(workspaceApps)); - } - - /** - * Adds the provided items to the workspace. - */ public void addAndBindAddedWorkspaceItems( - Provider<List<ItemInfo>> appsProvider) { + Provider<List<Pair<ItemInfo, Object>>> appsProvider) { enqueueModelUpdateTask(new AddWorkspaceItemsTask(appsProvider)); } @@ -529,7 +517,7 @@ public class LauncherModel extends BroadcastReceiver */ public boolean startLoader(int synchronousBindPage) { // Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems - InstallShortcutReceiver.enableInstallQueue(); + InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_LOADER_RUNNING); synchronized (mLock) { // Don't bother to start the thread if we know it's not going to do anything if (mCallbacks != null && mCallbacks.get() != null) { @@ -607,7 +595,6 @@ public class LauncherModel extends BroadcastReceiver private Context mContext; private int mPageToBindFirst; - @Thunk boolean mIsLoadingAndBindingWorkspace; private boolean mStopped; LoaderTask(Context context, int pageToBindFirst) { @@ -675,8 +662,6 @@ public class LauncherModel extends BroadcastReceiver try { long now = 0; if (DEBUG_LOADERS) Log.d(TAG, "step 1.1: loading workspace"); - // Set to false in bindWorkspace() - mIsLoadingAndBindingWorkspace = true; loadWorkspace(); verifyNotStopped(); @@ -1584,18 +1569,6 @@ public class LauncherModel extends BroadcastReceiver callbacks.finishBindingItems(); } - mIsLoadingAndBindingWorkspace = false; - - // Run all the bind complete runnables after workspace is bound. - if (!mBindCompleteRunnables.isEmpty()) { - synchronized (mBindCompleteRunnables) { - for (final Runnable r : mBindCompleteRunnables) { - runOnWorkerThread(r); - } - mBindCompleteRunnables.clear(); - } - } - // If we're profiling, ensure this is the last thing in the queue. if (DEBUG_LOADERS) { Log.d(TAG, "bound workspace in " @@ -1710,31 +1683,7 @@ public class LauncherModel extends BroadcastReceiver mBgAllAppsList.add(new AppInfo(app, user, quietMode), app); } - final ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(mContext, user); - if (heuristic != null) { - final Runnable r = new Runnable() { - - @Override - public void run() { - heuristic.processUserApps(apps); - } - }; - mUiExecutor.execute(new Runnable() { - - @Override - public void run() { - // Check isLoadingWorkspace on the UI thread, as it is updated on - // the UI thread. - if (mIsLoadingAndBindingWorkspace) { - synchronized (mBindCompleteRunnables) { - mBindCompleteRunnables.add(r); - } - } else { - runOnWorkerThread(r); - } - } - }); - } + ManagedProfileHeuristic.onAllAppsLoaded(mContext, apps, user); } if (FeatureFlags.LAUNCHER3_PROMISE_APPS_IN_ALL_APPS) { @@ -1768,8 +1717,6 @@ public class LauncherModel extends BroadcastReceiver } } }); - // Cleanup any data stored for a deleted user. - ManagedProfileHeuristic.processAllUsers(profiles, mContext); if (DEBUG_LOADERS) { Log.d(TAG, "Icons processed in " + (SystemClock.uptimeMillis() - loadTime) + "ms"); diff --git a/src/com/android/launcher3/SessionCommitReceiver.java b/src/com/android/launcher3/SessionCommitReceiver.java index 61bcc178c..8caba75cd 100644 --- a/src/com/android/launcher3/SessionCommitReceiver.java +++ b/src/com/android/launcher3/SessionCommitReceiver.java @@ -67,18 +67,19 @@ public class SessionCommitReceiver extends BroadcastReceiver { SessionInfo info = intent.getParcelableExtra(PackageInstaller.EXTRA_SESSION); UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER); - if (TextUtils.isEmpty(info.getAppPackageName()) || - info.getInstallReason() != PackageManager.INSTALL_REASON_USER) { - return; + if (Process.myUserHandle().equals(user)) { + if (TextUtils.isEmpty(info.getAppPackageName()) || + info.getInstallReason() != PackageManager.INSTALL_REASON_USER) { + return; + } } - if (!Process.myUserHandle().equals(user)) { - // Managed profile is handled using ManagedProfileHeuristic - return; - } + queueAppIconAddition(context, info.getAppPackageName(), user); + } + public static void queueAppIconAddition(Context context, String packageName, UserHandle user) { List<LauncherActivityInfo> activities = LauncherAppsCompat.getInstance(context) - .getActivityList(info.getAppPackageName(), user); + .getActivityList(packageName, user); if (activities == null || activities.isEmpty()) { // no activity found return; diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index b3dd7ac60..672203cc5 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -451,7 +451,7 @@ public class Workspace extends PagedView mLauncher.lockScreenOrientation(); mLauncher.onInteractionBegin(); // Prevent any Un/InstallShortcutReceivers from updating the db while we are dragging - InstallShortcutReceiver.enableInstallQueue(); + InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_DRAG_AND_DROP); // Do not add a new page if it is a accessible drag which was not started by the workspace. // We do not support accessibility drag from other sources and instead provide a direct @@ -504,7 +504,8 @@ public class Workspace extends PagedView mLauncher.unlockScreenOrientation(false); // Re-enable any Un/InstallShortcutReceiver and now process any queued items - InstallShortcutReceiver.disableAndFlushInstallQueue(getContext()); + InstallShortcutReceiver.disableAndFlushInstallQueue( + InstallShortcutReceiver.FLAG_DRAG_AND_DROP, getContext()); mOutlineProvider = null; mDragInfo = null; diff --git a/src/com/android/launcher3/compat/UserManagerCompatVL.java b/src/com/android/launcher3/compat/UserManagerCompatVL.java index 45525f521..c7f88f63d 100644 --- a/src/com/android/launcher3/compat/UserManagerCompatVL.java +++ b/src/com/android/launcher3/compat/UserManagerCompatVL.java @@ -22,8 +22,8 @@ import android.content.pm.PackageManager; import android.os.UserHandle; import android.os.UserManager; -import com.android.launcher3.Utilities; import com.android.launcher3.util.LongArrayMap; +import com.android.launcher3.util.ManagedProfileHeuristic; import java.util.ArrayList; import java.util.Collections; @@ -122,7 +122,7 @@ public class UserManagerCompatVL extends UserManagerCompat { @Override public long getUserCreationTime(UserHandle user) { - SharedPreferences prefs = Utilities.getPrefs(mContext); + SharedPreferences prefs = ManagedProfileHeuristic.prefs(mContext); String key = USER_CREATION_TIME_KEY + getSerialNumberForUser(user); if (!prefs.contains(key)) { prefs.edit().putLong(key, System.currentTimeMillis()).apply(); diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java index 10fb5828c..2e8e15bf7 100644 --- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java +++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java @@ -17,6 +17,8 @@ package com.android.launcher3.model; import android.content.Context; import android.content.Intent; +import android.content.pm.LauncherActivityInfo; +import android.os.Process; import android.os.UserHandle; import android.util.LongSparseArray; import android.util.Pair; @@ -35,9 +37,11 @@ import com.android.launcher3.LauncherSettings; import com.android.launcher3.ShortcutInfo; import com.android.launcher3.Utilities; import com.android.launcher3.util.GridOccupancy; +import com.android.launcher3.util.ManagedProfileHeuristic.UserFolderInfo; import com.android.launcher3.util.Provider; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; /** @@ -45,18 +49,18 @@ import java.util.List; */ public class AddWorkspaceItemsTask extends ExtendedModelTask { - private final Provider<List<ItemInfo>> mAppsProvider; + private final Provider<List<Pair<ItemInfo, Object>>> mAppsProvider; /** * @param appsProvider items to add on the workspace */ - public AddWorkspaceItemsTask(Provider<List<ItemInfo>> appsProvider) { + public AddWorkspaceItemsTask(Provider<List<Pair<ItemInfo, Object>>> appsProvider) { mAppsProvider = appsProvider; } @Override public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) { - List<ItemInfo> workspaceApps = mAppsProvider.get(); + List<Pair<ItemInfo, Object>> workspaceApps = mAppsProvider.get(); if (workspaceApps.isEmpty()) { return; } @@ -64,13 +68,17 @@ public class AddWorkspaceItemsTask extends ExtendedModelTask { final ArrayList<ItemInfo> addedItemsFinal = new ArrayList<>(); final ArrayList<Long> addedWorkspaceScreensFinal = new ArrayList<>(); + HashMap<UserHandle, UserFolderInfo> userFolderMap = new HashMap<>(); // Get the list of workspace screens. We need to append to this list and // can not use sBgWorkspaceScreens because loadWorkspace() may not have been // called. ArrayList<Long> workspaceScreens = LauncherModel.loadWorkspaceScreensDb(context); synchronized(dataModel) { - for (ItemInfo item : workspaceApps) { + + List<ItemInfo> filteredItems = new ArrayList<>(); + for (Pair<ItemInfo, Object> entry : workspaceApps) { + ItemInfo item = entry.first; if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION || item.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) { // Short-circuit this logic if the icon exists somewhere on the workspace @@ -79,6 +87,32 @@ public class AddWorkspaceItemsTask extends ExtendedModelTask { } } + if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { + if (item instanceof AppInfo) { + item = ((AppInfo) item).makeShortcut(); + } + + if (!Process.myUserHandle().equals(item.user)) { + // Check if this belongs to a work folder. + if (!(entry.second instanceof LauncherActivityInfo)) { + continue; + } + + UserFolderInfo userFolderInfo = userFolderMap.get(item.user); + if (userFolderInfo == null) { + userFolderInfo = new UserFolderInfo(context, item.user, dataModel); + userFolderMap.put(item.user, userFolderInfo); + } + item = userFolderInfo.convertToWorkspaceItem( + (ShortcutInfo) item, (LauncherActivityInfo) entry.second); + } + } + if (item != null) { + filteredItems.add(item); + } + } + + for (ItemInfo item : filteredItems) { // Find appropriate space for the item. Pair<Long, int[]> coords = findSpaceForItem(app, dataModel, workspaceScreens, addedWorkspaceScreensFinal, item.spanX, item.spanY); @@ -130,6 +164,10 @@ public class AddWorkspaceItemsTask extends ExtendedModelTask { } }); } + + for (UserFolderInfo userFolderInfo : userFolderMap.values()) { + userFolderInfo.applyPendingState(getModelWriter()); + } } protected void updateScreens(Context context, ArrayList<Long> workspaceScreens) { @@ -276,4 +314,5 @@ public class AddWorkspaceItemsTask extends ExtendedModelTask { } return occupied.findVacantCell(xy, spanX, spanY); } + } diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java index b58efb647..8380f0136 100644 --- a/src/com/android/launcher3/model/PackageUpdatedTask.java +++ b/src/com/android/launcher3/model/PackageUpdatedTask.java @@ -35,6 +35,7 @@ import com.android.launcher3.LauncherModel.CallbackTask; import com.android.launcher3.LauncherModel.Callbacks; import com.android.launcher3.LauncherSettings; import com.android.launcher3.LauncherSettings.Favorites; +import com.android.launcher3.SessionCommitReceiver; import com.android.launcher3.ShortcutInfo; import com.android.launcher3.Utilities; import com.android.launcher3.compat.LauncherAppsCompat; @@ -43,7 +44,6 @@ import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.graphics.LauncherIcons; import com.android.launcher3.util.FlagOp; import com.android.launcher3.util.ItemInfoMatcher; -import com.android.launcher3.util.ManagedProfileHeuristic; import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.PackageUserKey; @@ -100,11 +100,11 @@ public class PackageUpdatedTask extends ExtendedModelTask { appsList.removePackage(packages[i], Process.myUserHandle()); } appsList.addPackage(context, packages[i], mUser); - } - ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(context, mUser); - if (heuristic != null) { - heuristic.processPackageAdd(mPackages); + // Automatically add homescreen icon for work profile apps for below O device. + if (!Utilities.isAtLeastO() && !Process.myUserHandle().equals(mUser)) { + SessionCommitReceiver.queueAppIconAddition(context, packages[i], mUser); + } } break; } @@ -119,10 +119,6 @@ public class PackageUpdatedTask extends ExtendedModelTask { flagOp = FlagOp.removeFlag(ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE); break; case OP_REMOVE: { - ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(context, mUser); - if (heuristic != null) { - heuristic.processPackageRemoved(mPackages); - } for (int i = 0; i < N; i++) { iconCache.removeIconsForPkg(packages[i], mUser); } diff --git a/src/com/android/launcher3/util/CachedPackageTracker.java b/src/com/android/launcher3/util/CachedPackageTracker.java deleted file mode 100644 index 314b4c0ed..000000000 --- a/src/com/android/launcher3/util/CachedPackageTracker.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.launcher3.util; - -import android.content.Context; -import android.content.SharedPreferences; -import android.content.pm.LauncherActivityInfo; -import android.os.UserHandle; - -import com.android.launcher3.Utilities; -import com.android.launcher3.compat.LauncherAppsCompat; -import com.android.launcher3.compat.LauncherAppsCompat.OnAppsChangedCallbackCompat; -import com.android.launcher3.compat.UserManagerCompat; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * Utility class to track list of installed packages. It persists the list so that apps - * installed/uninstalled while Launcher was dead can also be handled properly. - */ -public abstract class CachedPackageTracker implements OnAppsChangedCallbackCompat { - - protected static final String INSTALLED_PACKAGES_PREFIX = "installed_packages_for_user_"; - - protected final SharedPreferences mPrefs; - protected final UserManagerCompat mUserManager; - protected final LauncherAppsCompat mLauncherApps; - - public CachedPackageTracker(Context context, String preferenceFileName) { - mPrefs = context.getSharedPreferences(preferenceFileName, Context.MODE_PRIVATE); - mUserManager = UserManagerCompat.getInstance(context); - mLauncherApps = LauncherAppsCompat.getInstance(context); - } - - /** - * Checks the list of user apps, and generates package event accordingly. - * {@see #onLauncherAppsAdded}, {@see #onLauncherPackageRemoved} - */ - public void processUserApps(List<LauncherActivityInfo> apps, UserHandle user) { - String prefKey = INSTALLED_PACKAGES_PREFIX + mUserManager.getSerialNumberForUser(user); - HashSet<String> oldPackageSet = new HashSet<>(); - final boolean userAppsExisted = getUserApps(oldPackageSet, prefKey); - - HashSet<String> packagesRemoved = new HashSet<>(oldPackageSet); - HashSet<String> newPackageSet = new HashSet<>(); - ArrayList<LauncherActivityInstallInfo> packagesAdded = new ArrayList<>(); - - for (LauncherActivityInfo info : apps) { - String packageName = info.getComponentName().getPackageName(); - newPackageSet.add(packageName); - packagesRemoved.remove(packageName); - - if (!oldPackageSet.contains(packageName)) { - oldPackageSet.add(packageName); - packagesAdded.add(new LauncherActivityInstallInfo( - info, info.getFirstInstallTime())); - } - } - - if (!packagesAdded.isEmpty() || !packagesRemoved.isEmpty()) { - mPrefs.edit().putStringSet(prefKey, newPackageSet).apply(); - - if (!packagesAdded.isEmpty()) { - Collections.sort(packagesAdded); - onLauncherAppsAdded(packagesAdded, user, userAppsExisted); - } - - if (!packagesRemoved.isEmpty()) { - for (String pkg : packagesRemoved) { - onLauncherPackageRemoved(pkg, user); - } - } - } - } - - /** - * Reads the list of user apps which have already been processed. - * @return false if the list didn't exist, true otherwise - */ - private boolean getUserApps(HashSet<String> outExistingApps, String prefKey) { - Set<String> userApps = mPrefs.getStringSet(prefKey, null); - if (userApps == null) { - return false; - } else { - outExistingApps.addAll(userApps); - return true; - } - } - - @Override - public void onPackageRemoved(String packageName, UserHandle user) { - String prefKey = INSTALLED_PACKAGES_PREFIX + mUserManager.getSerialNumberForUser(user); - HashSet<String> packageSet = new HashSet<>(); - if (getUserApps(packageSet, prefKey) && packageSet.remove(packageName)) { - mPrefs.edit().putStringSet(prefKey, packageSet).apply(); - } - - onLauncherPackageRemoved(packageName, user); - } - - @Override - public void onPackageAdded(String packageName, UserHandle user) { - String prefKey = INSTALLED_PACKAGES_PREFIX + mUserManager.getSerialNumberForUser(user); - HashSet<String> packageSet = new HashSet<>(); - final boolean userAppsExisted = getUserApps(packageSet, prefKey); - if (!packageSet.contains(packageName)) { - List<LauncherActivityInfo> activities = - mLauncherApps.getActivityList(packageName, user); - if (!activities.isEmpty()) { - LauncherActivityInfo activityInfo = activities.get(0); - - packageSet.add(packageName); - mPrefs.edit().putStringSet(prefKey, packageSet).apply(); - onLauncherAppsAdded(Arrays.asList( - new LauncherActivityInstallInfo(activityInfo, System.currentTimeMillis())), - user, userAppsExisted); - } - } - } - - @Override - public void onPackageChanged(String packageName, UserHandle user) { } - - @Override - public void onPackagesAvailable( - String[] packageNames, UserHandle user, boolean replacing) { } - - @Override - public void onPackagesUnavailable( - String[] packageNames, UserHandle user, boolean replacing) { } - - @Override - public void onPackagesSuspended(String[] packageNames, UserHandle user) { } - - @Override - public void onPackagesUnsuspended(String[] packageNames, UserHandle user) { } - - /** - * Called when new launcher apps are added. - * @param apps list of newly added activities. Only one entry per package is sent. - * @param user the user for this event. All activities in {@param apps} will belong to - * the same user. - * @param userAppsExisted false if the list was processed for the first time, like in case - * when Launcher was newly installed or a new user was added. - */ - protected abstract void onLauncherAppsAdded(List<LauncherActivityInstallInfo> apps, - UserHandle user, boolean userAppsExisted); - - /** - * Called when apps are removed from the system. - */ - protected abstract void onLauncherPackageRemoved(String packageName, UserHandle user); - - public static class LauncherActivityInstallInfo - implements Comparable<LauncherActivityInstallInfo> { - public final LauncherActivityInfo info; - public final long installTime; - - public LauncherActivityInstallInfo(LauncherActivityInfo info, long installTime) { - this.info = info; - this.installTime = installTime; - } - - @Override - public int compareTo(LauncherActivityInstallInfo another) { - return Utilities.longCompare(installTime, another.installTime); - } - } -} diff --git a/src/com/android/launcher3/util/ManagedProfileHeuristic.java b/src/com/android/launcher3/util/ManagedProfileHeuristic.java index ce603c4c2..091dd84bc 100644 --- a/src/com/android/launcher3/util/ManagedProfileHeuristic.java +++ b/src/com/android/launcher3/util/ManagedProfileHeuristic.java @@ -19,23 +19,23 @@ package com.android.launcher3.util; import android.content.Context; import android.content.SharedPreferences; import android.content.pm.LauncherActivityInfo; +import android.os.Handler; import android.os.Process; import android.os.UserHandle; -import android.support.v4.os.BuildCompat; -import com.android.launcher3.AppInfo; import com.android.launcher3.FolderInfo; -import com.android.launcher3.IconCache; +import com.android.launcher3.InstallShortcutReceiver; import com.android.launcher3.ItemInfo; -import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherFiles; import com.android.launcher3.LauncherModel; import com.android.launcher3.MainThreadExecutor; import com.android.launcher3.R; import com.android.launcher3.SessionCommitReceiver; import com.android.launcher3.ShortcutInfo; +import com.android.launcher3.Utilities; import com.android.launcher3.compat.UserManagerCompat; -import com.android.launcher3.shortcuts.ShortcutInfoCompat; +import com.android.launcher3.model.BgDataModel; +import com.android.launcher3.model.ModelWriter; import java.util.ArrayList; import java.util.HashSet; @@ -47,11 +47,6 @@ import java.util.List; */ public class ManagedProfileHeuristic { - /** - * Maintain a set of packages installed per user. - */ - private static final String INSTALLED_PACKAGES_PREFIX = "installed_packages_for_user_"; - private static final String USER_FOLDER_ID_PREFIX = "user_folder_"; /** @@ -59,165 +54,154 @@ public class ManagedProfileHeuristic { */ private static final long AUTO_ADD_TO_FOLDER_DURATION = 8 * 60 * 60 * 1000; - public static ManagedProfileHeuristic get(Context context, UserHandle user) { - if (!Process.myUserHandle().equals(user)) { - return new ManagedProfileHeuristic(context, user); + public static void onAllAppsLoaded(final Context context, + List<LauncherActivityInfo> apps, UserHandle user) { + if (Process.myUserHandle().equals(user)) { + return; } - return null; - } - private final Context mContext; - private final LauncherModel mModel; - private final UserHandle mUser; - private final IconCache mIconCache; - private final boolean mAddIconsToHomescreen; - - private ManagedProfileHeuristic(Context context, UserHandle user) { - mContext = context; - mUser = user; - mModel = LauncherAppState.getInstance(context).getModel(); - mIconCache = LauncherAppState.getInstance(context).getIconCache(); - mAddIconsToHomescreen = - !BuildCompat.isAtLeastO() || SessionCommitReceiver.isEnabled(context); - } + UserFolderInfo ufi = new UserFolderInfo(context, user, null); + // We only handle folder creation once. Later icon additions are handled using package + // or session events. + if (ufi.folderAlreadyCreated) { + return; + } - public void processPackageRemoved(String[] packages) { - Preconditions.assertWorkerThread(); - ManagedProfilePackageHandler handler = new ManagedProfilePackageHandler(); - for (String pkg : packages) { - handler.onPackageRemoved(pkg, mUser); + if (Utilities.isAtLeastO() && !SessionCommitReceiver.isEnabled(context)) { + // Just mark the folder id preference to avoid new folder creation later. + ufi.prefs.edit().putLong(ufi.folderIdKey, ItemInfo.NO_ID).apply(); + return; } - } - public void processPackageAdd(String[] packages) { - Preconditions.assertWorkerThread(); - ManagedProfilePackageHandler handler = new ManagedProfilePackageHandler(); - for (String pkg : packages) { - handler.onPackageAdded(pkg, mUser); + InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_BULK_ADD); + for (LauncherActivityInfo app : apps) { + // Queue all items which should go in the work folder. + if (app.getFirstInstallTime() < ufi.addIconToFolderTime) { + InstallShortcutReceiver.queueActivityInfo(app, context); + } } + // Post the queue update on next frame, so that the loader gets finished. + new Handler(LauncherModel.getWorkerLooper()).post(new Runnable() { + @Override + public void run() { + InstallShortcutReceiver.disableAndFlushInstallQueue( + InstallShortcutReceiver.FLAG_BULK_ADD, context); + } + }); } - public void processUserApps(List<LauncherActivityInfo> apps) { - Preconditions.assertWorkerThread(); - new ManagedProfilePackageHandler().processUserApps(apps, mUser); - } - private class ManagedProfilePackageHandler extends CachedPackageTracker { + /** + * Utility class to help workspace icon addition. + */ + public static class UserFolderInfo { - private ManagedProfilePackageHandler() { - super(mContext, LauncherFiles.MANAGED_USER_PREFERENCES_KEY); - } + final ArrayList<ShortcutInfo> pendingShortcuts = new ArrayList<>(); - protected void onLauncherAppsAdded( - List<LauncherActivityInstallInfo> apps, UserHandle user, boolean userAppsExisted) { - ArrayList<ShortcutInfo> workFolderApps = new ArrayList<>(); - ArrayList<ShortcutInfo> homescreenApps = new ArrayList<>(); - - int count = apps.size(); - long folderCreationTime = - mUserManager.getUserCreationTime(user) + AUTO_ADD_TO_FOLDER_DURATION; - - boolean quietModeEnabled = UserManagerCompat.getInstance(mContext) - .isQuietModeEnabled(user); - for (int i = 0; i < count; i++) { - LauncherActivityInstallInfo info = apps.get(i); - AppInfo appInfo = new AppInfo(info.info, user, quietModeEnabled); - mIconCache.getTitleAndIcon(appInfo, info.info, false /* useLowResIcon */); - ShortcutInfo si = appInfo.makeShortcut(); - ((info.installTime <= folderCreationTime) ? workFolderApps : homescreenApps).add(si); - } + final UserHandle user; - finalizeWorkFolder(user, workFolderApps, homescreenApps); + final long userSerial; + // Time until which icons will be added to folder instead. + final long addIconToFolderTime; - // Do not add shortcuts on the homescreen for the first time. This prevents the launcher - // getting filled with the managed user apps, when it start with a fresh DB (or after - // a very long time). - if (userAppsExisted && !homescreenApps.isEmpty() && mAddIconsToHomescreen) { - mModel.addAndBindAddedWorkspaceItems(new ArrayList<ItemInfo>(homescreenApps)); - } - } + final String folderIdKey; + final SharedPreferences prefs; + + final boolean folderAlreadyCreated; + final FolderInfo folderInfo; + + boolean folderPendingAddition; + + public UserFolderInfo(Context context, UserHandle user, BgDataModel dataModel) { + this.user = user; + + UserManagerCompat um = UserManagerCompat.getInstance(context); + userSerial = um.getSerialNumberForUser(user); + addIconToFolderTime = um.getUserCreationTime(user) + AUTO_ADD_TO_FOLDER_DURATION; - @Override - protected void onLauncherPackageRemoved(String packageName, UserHandle user) { + folderIdKey = USER_FOLDER_ID_PREFIX + userSerial; + prefs = prefs(context); + + folderAlreadyCreated = prefs.contains(folderIdKey); + if (dataModel != null) { + if (folderAlreadyCreated) { + long folderId = prefs.getLong(folderIdKey, ItemInfo.NO_ID); + folderInfo = dataModel.folders.get(folderId); + } else { + folderInfo = new FolderInfo(); + folderInfo.title = context.getText(R.string.work_folder_name); + folderInfo.setOption(FolderInfo.FLAG_WORK_FOLDER, true, null); + folderPendingAddition = true; + } + } else { + folderInfo = null; + } } /** - * Adds and binds shortcuts marked to be added to the work folder. + * Returns the ItemInfo which should be added to the workspace. In case the the provided + * {@link ShortcutInfo} or a wrapped {@link FolderInfo} or null. */ - private void finalizeWorkFolder( - UserHandle user, final ArrayList<ShortcutInfo> workFolderApps, - ArrayList<ShortcutInfo> homescreenApps) { - if (workFolderApps.isEmpty()) { - return; + public ItemInfo convertToWorkspaceItem( + ShortcutInfo shortcut, LauncherActivityInfo activityInfo) { + if (activityInfo.getFirstInstallTime() >= addIconToFolderTime) { + return shortcut; } - // Try to get a work folder. - String folderIdKey = USER_FOLDER_ID_PREFIX + mUserManager.getSerialNumberForUser(user); - if (!mAddIconsToHomescreen) { - if (!mPrefs.contains(folderIdKey)) { - // Just mark the folder id preference to avoid new folder creation later. - mPrefs.edit().putLong(folderIdKey, -1).apply(); + + if (folderAlreadyCreated) { + if (folderInfo == null) { + // Work folder was deleted by user, add icon to home screen. + return shortcut; + } else { + // Add item to work folder instead. Nothing needs to be added + // on the homescreen. + pendingShortcuts.add(shortcut); + return null; } + } + + pendingShortcuts.add(shortcut); + folderInfo.add(shortcut, false); + if (folderPendingAddition) { + folderPendingAddition = false; + return folderInfo; + } else { + // WorkFolder already requested to be added. Nothing new needs to be added. + return null; + } + } + + public void applyPendingState(ModelWriter writer) { + if (folderInfo == null) { return; } - if (mPrefs.contains(folderIdKey)) { - long folderId = mPrefs.getLong(folderIdKey, 0); - final FolderInfo workFolder = mModel.findFolderById(folderId); - - if (workFolder == null || !workFolder.hasOption(FolderInfo.FLAG_WORK_FOLDER)) { - // Could not get a work folder. Add all the icons to homescreen. - homescreenApps.addAll(0, workFolderApps); - return; - } - saveWorkFolderShortcuts(folderId, workFolder.contents.size(), workFolderApps); + int startingRank = 0; + if (folderAlreadyCreated) { + startingRank = folderInfo.contents.size(); + } + + for (ShortcutInfo info : pendingShortcuts) { + info.rank = startingRank++; + writer.addItemToDatabase(info, folderInfo.id, 0, 0, 0); + } + + if (folderAlreadyCreated) { // FolderInfo could already be bound. We need to add shortcuts on the UI thread. new MainThreadExecutor().execute(new Runnable() { @Override public void run() { - workFolder.prepareAutoUpdate(); - for (ShortcutInfo info : workFolderApps) { - workFolder.add(info, false); + folderInfo.prepareAutoUpdate(); + for (ShortcutInfo info : pendingShortcuts) { + folderInfo.add(info, false); } } }); } else { - // Create a new folder. - final FolderInfo workFolder = new FolderInfo(); - workFolder.title = mContext.getText(R.string.work_folder_name); - workFolder.setOption(FolderInfo.FLAG_WORK_FOLDER, true, null); - - // Add all shortcuts before adding it to the UI, as an empty folder might get deleted. - for (ShortcutInfo info : workFolderApps) { - workFolder.add(info, false); - } - - // Add the item to home screen and DB. This also generates an item id synchronously. - ArrayList<ItemInfo> itemList = new ArrayList<>(1); - itemList.add(workFolder); - mModel.addAndBindAddedWorkspaceItems(itemList); - mPrefs.edit().putLong(folderIdKey, workFolder.id).apply(); - - saveWorkFolderShortcuts(workFolder.id, 0, workFolderApps); + prefs.edit().putLong(folderIdKey, folderInfo.id).apply(); } } - - @Override - public void onShortcutsChanged(String packageName, List<ShortcutInfoCompat> shortcuts, - UserHandle user) { - // Do nothing - } - } - - /** - * Add work folder shortcuts to the DB. - */ - private void saveWorkFolderShortcuts( - long workFolderId, int startingRank, ArrayList<ShortcutInfo> workFolderApps) { - for (ItemInfo info : workFolderApps) { - info.rank = startingRank++; - mModel.getWriter(false).addItemToDatabase(info, workFolderId, 0, 0, 0); - } } /** @@ -225,14 +209,12 @@ public class ManagedProfileHeuristic { */ public static void processAllUsers(List<UserHandle> users, Context context) { UserManagerCompat userManager = UserManagerCompat.getInstance(context); - HashSet<String> validKeys = new HashSet<String>(); + HashSet<String> validKeys = new HashSet<>(); for (UserHandle user : users) { - addAllUserKeys(userManager.getSerialNumberForUser(user), validKeys); + validKeys.add(USER_FOLDER_ID_PREFIX + userManager.getSerialNumberForUser(user)); } - SharedPreferences prefs = context.getSharedPreferences( - LauncherFiles.MANAGED_USER_PREFERENCES_KEY, - Context.MODE_PRIVATE); + SharedPreferences prefs = prefs(context); SharedPreferences.Editor editor = prefs.edit(); for (String key : prefs.getAll().keySet()) { if (!validKeys.contains(key)) { @@ -242,11 +224,6 @@ public class ManagedProfileHeuristic { editor.apply(); } - private static void addAllUserKeys(long userSerial, HashSet<String> keysOut) { - keysOut.add(INSTALLED_PACKAGES_PREFIX + userSerial); - keysOut.add(USER_FOLDER_ID_PREFIX + userSerial); - } - /** * For each user, if a work folder has not been created, mark it such that the folder will * never get created. @@ -260,11 +237,8 @@ public class ManagedProfileHeuristic { if (myUser.equals(user)) { continue; } - if (prefs == null) { - prefs = context.getSharedPreferences( - LauncherFiles.MANAGED_USER_PREFERENCES_KEY, - Context.MODE_PRIVATE); + prefs = prefs(context); } String folderIdKey = USER_FOLDER_ID_PREFIX + userManager.getSerialNumberForUser(user); if (!prefs.contains(folderIdKey)) { @@ -272,4 +246,9 @@ public class ManagedProfileHeuristic { } } } + + public static SharedPreferences prefs(Context context) { + return context.getSharedPreferences( + LauncherFiles.MANAGED_USER_PREFERENCES_KEY, Context.MODE_PRIVATE); + } } diff --git a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java index 883be5aa3..4c80902f0 100644 --- a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java +++ b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java @@ -21,6 +21,7 @@ import org.mockito.ArgumentCaptor; import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import static org.mockito.Mockito.any; import static org.mockito.Mockito.verify; @@ -50,7 +51,11 @@ public class AddWorkspaceItemsTaskTest extends BaseModelUpdateTaskTestCase { } private AddWorkspaceItemsTask newTask(ItemInfo... items) { - return new AddWorkspaceItemsTask(Provider.of(Arrays.asList(items))) { + List<Pair<ItemInfo, Object>> list = new ArrayList<>(); + for (ItemInfo item : items) { + list.add(Pair.create(item, null)); + } + return new AddWorkspaceItemsTask(Provider.of(list)) { @Override protected void updateScreens(Context context, ArrayList<Long> workspaceScreens) { } |