diff options
Diffstat (limited to 'src/com/android')
14 files changed, 372 insertions, 238 deletions
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 5f5ac2e3c..5ce98e2e8 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -117,8 +117,10 @@ import com.android.launcher3.model.WidgetsModel; import com.android.launcher3.pageindicators.PageIndicator; import com.android.launcher3.shortcuts.DeepShortcutManager; import com.android.launcher3.shortcuts.DeepShortcutsContainer; +import com.android.launcher3.shortcuts.ShortcutKey; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.util.ComponentKey; +import com.android.launcher3.util.ItemInfoMatcher; import com.android.launcher3.util.MultiHashMap; import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.TestingUtils; @@ -2655,14 +2657,17 @@ public class Launcher extends Activity final ShortcutInfo shortcut = (ShortcutInfo) tag; if (shortcut.isDisabled != 0) { - if ((shortcut.isDisabled & ShortcutInfo.FLAG_DISABLED_SUSPENDED) != 0 - || (shortcut.isDisabled & ShortcutInfo.FLAG_DISABLED_QUIET_USER) != 0) { - // Launch activity anyway, framework will tell the user why the app is suspended. + if ((shortcut.isDisabled & + ~ShortcutInfo.FLAG_DISABLED_SUSPENDED & + ~ShortcutInfo.FLAG_DISABLED_QUIET_USER) == 0) { + // If the app is only disabled because of the above flags, launch activity anyway. + // Framework will tell the user why the app is suspended. } else { int error = R.string.activity_not_available; if ((shortcut.isDisabled & ShortcutInfo.FLAG_DISABLED_SAFEMODE) != 0) { error = R.string.safemode_shortcut_error; - } else if ((shortcut.isDisabled & ShortcutInfo.FLAG_DISABLED_BY_PUBLISHER) != 0) { + } else if ((shortcut.isDisabled & ShortcutInfo.FLAG_DISABLED_BY_PUBLISHER) != 0 || + (shortcut.isDisabled & ShortcutInfo.FLAG_DISABLED_LOCKED_USER) != 0) { error = R.string.shortcut_not_available; } Toast.makeText(this, error, Toast.LENGTH_SHORT).show(); @@ -4208,8 +4213,12 @@ public class Launcher extends Activity /** * Some shortcuts were updated in the background. - * * Implementation of the method from LauncherModel.Callbacks. + * + * @param updated list of shortcuts which have changed. + * @param removed list of shortcuts which were deleted in the background. This can happen when + * an app gets removed from the system or some of its components are no longer + * available. */ @Override public void bindShortcutsChanged(final ArrayList<ShortcutInfo> updated, @@ -4228,13 +4237,28 @@ public class Launcher extends Activity } if (!removed.isEmpty()) { - HashSet<ComponentName> removedComponents = new HashSet<ComponentName>(); + HashSet<ComponentName> removedComponents = new HashSet<>(); + HashSet<ShortcutKey> removedDeepShortcuts = new HashSet<>(); + for (ShortcutInfo si : removed) { - removedComponents.add(si.getTargetComponent()); + if (si.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) { + removedDeepShortcuts.add(ShortcutKey.fromItemInfo(si)); + } else { + removedComponents.add(si.getTargetComponent()); + } + } + + if (!removedComponents.isEmpty()) { + ItemInfoMatcher matcher = ItemInfoMatcher.ofComponents(removedComponents, user); + mWorkspace.removeItemsByMatcher(matcher); + mDragController.onAppsRemoved(matcher); + } + + if (!removedDeepShortcuts.isEmpty()) { + ItemInfoMatcher matcher = ItemInfoMatcher.ofShortcutKeys(removedDeepShortcuts); + mWorkspace.removeItemsByMatcher(matcher); + mDragController.onAppsRemoved(matcher); } - mWorkspace.removeItemsByComponentName(removedComponents, user); - // Notify the drag controller - mDragController.onAppsRemoved(new HashSet<String>(), removedComponents); } } @@ -4277,14 +4301,16 @@ public class Launcher extends Activity return; } if (!packageNames.isEmpty()) { - mWorkspace.removeItemsByPackageName(packageNames, user); + ItemInfoMatcher matcher = ItemInfoMatcher.ofPackages(packageNames, user); + mWorkspace.removeItemsByMatcher(matcher); + mDragController.onAppsRemoved(matcher); + } if (!components.isEmpty()) { - mWorkspace.removeItemsByComponentName(components, user); + ItemInfoMatcher matcher = ItemInfoMatcher.ofComponents(components, user); + mWorkspace.removeItemsByMatcher(matcher); + mDragController.onAppsRemoved(matcher); } - // Notify the drag controller - mDragController.onAppsRemoved(packageNames, components); - } @Override diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index 4bc76fb15..7861a106f 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -109,10 +109,11 @@ public class LauncherAppState { IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_LOCALE_CHANGED); // For handling managed profiles - filter.addAction(LauncherAppsCompat.ACTION_MANAGED_PROFILE_ADDED); - filter.addAction(LauncherAppsCompat.ACTION_MANAGED_PROFILE_REMOVED); - filter.addAction(LauncherAppsCompat.ACTION_MANAGED_PROFILE_AVAILABLE); - filter.addAction(LauncherAppsCompat.ACTION_MANAGED_PROFILE_UNAVAILABLE); + filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED); + filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED); + filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE); + filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE); + filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED); // For extracting colors from the wallpaper if (Utilities.isNycOrAbove()) { // TODO: add a broadcast entry to the manifest for pre-N. diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index b465b3a4a..89a68369b 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -322,8 +322,8 @@ public class LauncherModel extends BroadcastReceiver @Override public void run() { synchronized (sBgLock) { - final ArrayList<ShortcutInfo> updates = new ArrayList<>(); - final UserHandleCompat user = UserHandleCompat.myUserHandle(); + ArrayList<ShortcutInfo> updates = new ArrayList<>(); + UserHandleCompat user = UserHandleCompat.myUserHandle(); for (ItemInfo info : sBgItemsIdMap) { if (info instanceof ShortcutInfo) { @@ -345,19 +345,7 @@ public class LauncherModel extends BroadcastReceiver } } - 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); - } + bindUpdatedShortcuts(updates, user); } } }; @@ -926,12 +914,8 @@ public class LauncherModel extends BroadcastReceiver } } if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) { - ShortcutInfo shortcutInfo = (ShortcutInfo) item; - ShortcutKey shortcutToPin = new ShortcutKey( - shortcutInfo.intent.getPackage(), - shortcutInfo.user, - shortcutInfo.getDeepShortcutId()); - incrementPinnedShortcutCount(shortcutToPin, true /* shouldPin */); + incrementPinnedShortcutCount( + ShortcutKey.fromItemInfo(item), true /* shouldPin */); } break; case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: @@ -1000,12 +984,7 @@ public class LauncherModel extends BroadcastReceiver sBgWorkspaceItems.remove(item); break; case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: - ShortcutInfo shortcutInfo = ((ShortcutInfo) item); - ShortcutKey pinnedShortcut = new ShortcutKey( - shortcutInfo.intent.getPackage(), - shortcutInfo.user, - shortcutInfo.getDeepShortcutId()); - decrementPinnedShortcutCount(pinnedShortcut); + decrementPinnedShortcutCount(ShortcutKey.fromItemInfo(item)); // Fall through. case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: @@ -1215,17 +1194,28 @@ public class LauncherModel extends BroadcastReceiver if (Intent.ACTION_LOCALE_CHANGED.equals(action)) { // If we have changed locale we need to clear out the labels in all apps/workspace. forceReload(); - } else if (LauncherAppsCompat.ACTION_MANAGED_PROFILE_ADDED.equals(action) - || LauncherAppsCompat.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) { + } else if (Intent.ACTION_MANAGED_PROFILE_ADDED.equals(action) + || Intent.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) { UserManagerCompat.getInstance(context).enableAndResetCache(); forceReload(); - } else if (LauncherAppsCompat.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action) || - LauncherAppsCompat.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)) { + } else if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action) || + Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action) || + Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)) { UserHandleCompat user = UserHandleCompat.fromIntent(intent); if (user != null) { - enqueueItemUpdatedTask(new PackageUpdatedTask( - PackageUpdatedTask.OP_USER_AVAILABILITY_CHANGE, - new String[0], user)); + if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action) || + Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)) { + enqueueItemUpdatedTask(new PackageUpdatedTask( + PackageUpdatedTask.OP_USER_AVAILABILITY_CHANGE, + new String[0], user)); + } + + // ACTION_MANAGED_PROFILE_UNAVAILABLE sends the profile back to locked mode, so + // we need to run the state change task again. + if (Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action) || + Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)) { + enqueueItemUpdatedTask(new UserLockStateChangedTask(user)); + } } } else if (Intent.ACTION_WALLPAPER_CHANGED.equals(action)) { ExtractionUtils.startColorExtractionServiceIfNecessary(context); @@ -1702,8 +1692,6 @@ public class LauncherModel extends BroadcastReceiver final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID); final int intentIndex = c.getColumnIndexOrThrow (LauncherSettings.Favorites.INTENT); - final int titleIndex = c.getColumnIndexOrThrow - (LauncherSettings.Favorites.TITLE); final int containerIndex = c.getColumnIndexOrThrow( LauncherSettings.Favorites.CONTAINER); final int itemTypeIndex = c.getColumnIndexOrThrow( @@ -1730,20 +1718,27 @@ public class LauncherModel extends BroadcastReceiver LauncherSettings.Favorites.PROFILE_ID); final int optionsIndex = c.getColumnIndexOrThrow( LauncherSettings.Favorites.OPTIONS); - final CursorIconInfo cursorIconInfo = new CursorIconInfo(c); + final CursorIconInfo cursorIconInfo = new CursorIconInfo(mContext, c); final LongSparseArray<UserHandleCompat> allUsers = new LongSparseArray<>(); final LongSparseArray<Boolean> quietMode = new LongSparseArray<>(); + final LongSparseArray<Boolean> unlockedUsers = new LongSparseArray<>(); for (UserHandleCompat user : mUserManager.getUserProfiles()) { long serialNo = mUserManager.getSerialNumberForUser(user); allUsers.put(serialNo, user); quietMode.put(serialNo, mUserManager.isQuietModeEnabled(user)); - List<ShortcutInfoCompat> pinnedShortcuts = mDeepShortcutManager - .queryForPinnedShortcuts(null, user); - for (ShortcutInfoCompat shortcut : pinnedShortcuts) { - shortcutKeyToPinnedShortcuts.put(ShortcutKey.fromInfo(shortcut), - shortcut); + boolean userUnlocked = mUserManager.isUserUnlocked(user); + unlockedUsers.put(serialNo, userUnlocked); + + // We can only query for shortcuts when the user is unlocked. + if (userUnlocked) { + List<ShortcutInfoCompat> pinnedShortcuts = mDeepShortcutManager + .queryForPinnedShortcuts(null, user); + for (ShortcutInfoCompat shortcut : pinnedShortcuts) { + shortcutKeyToPinnedShortcuts.put(ShortcutKey.fromInfo(shortcut), + shortcut); + } } } @@ -1907,9 +1902,8 @@ public class LauncherModel extends BroadcastReceiver if (itemReplaced) { if (user.equals(UserHandleCompat.myUserHandle())) { - info = getAppShortcutInfo(intent, user, context, null, - cursorIconInfo.iconIndex, titleIndex, - false, useLowResIcon); + info = getAppShortcutInfo(intent, user, null, + cursorIconInfo, false, useLowResIcon); } else { // Don't replace items for other profiles. itemsToRemove.add(id); @@ -1917,8 +1911,8 @@ public class LauncherModel extends BroadcastReceiver } } else if (restored) { if (user.equals(UserHandleCompat.myUserHandle())) { - info = getRestoredItemInfo(c, titleIndex, intent, - promiseType, itemType, cursorIconInfo, context); + info = getRestoredItemInfo(c, intent, + promiseType, itemType, cursorIconInfo); intent = getRestoredItemIntent(c, context, intent); } else { // Don't restore items for other profiles. @@ -1927,46 +1921,34 @@ public class LauncherModel extends BroadcastReceiver } } else if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { - info = getAppShortcutInfo(intent, user, context, c, - cursorIconInfo.iconIndex, titleIndex, - allowMissingTarget, useLowResIcon); + info = getAppShortcutInfo(intent, user, c, + cursorIconInfo, allowMissingTarget, useLowResIcon); } else if (itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) { - String shortcutId = intent.getStringExtra( - ShortcutInfoCompat.EXTRA_SHORTCUT_ID); - String packageName = intent.getPackage(); - ShortcutKey key = new ShortcutKey(intent.getPackage(), - user, shortcutId); - ShortcutInfoCompat pinnedShortcut = - shortcutKeyToPinnedShortcuts.get(key); - boolean shouldPin = false; // It's already pinned. - if (pinnedShortcut == null) { - // It shouldn't be possible for a shortcut to be on the - // workspace without being pinned, but if one somehow is, - // we should pin it now to get back to a good state. - Log.w(TAG, "Shortcut was on workspace but wasn't pinned"); - // Get full details; incrementing the count will pin it. - List<ShortcutInfoCompat> fullDetails = mDeepShortcutManager - .queryForFullDetails(packageName, - Collections.singletonList(shortcutId), user); - if (fullDetails == null || fullDetails.isEmpty()) { - // There are no details for the shortcut. If this is due - // to a SecurityException, keep it in the database so - // we can restore the icon when the launcher regains - // permission. Otherwise remove the icon from the db. - if (!mDeepShortcutManager.wasLastCallSuccess()) { - itemsToRemove.add(id); - continue; - } - } else { - pinnedShortcut = fullDetails.get(0); - shouldPin = true; + + ShortcutKey key = ShortcutKey.fromIntent(intent, user); + if (unlockedUsers.get(serialNumber)) { + ShortcutInfoCompat pinnedShortcut = + shortcutKeyToPinnedShortcuts.get(key); + if (pinnedShortcut == null) { + // The shortcut is no longer valid. + itemsToRemove.add(id); + continue; } + info = new ShortcutInfo(pinnedShortcut, context); + intent = info.intent; + } else { + // Create a shortcut info in disabled mode for now. + info = new ShortcutInfo(); + info.user = user; + info.itemType = itemType; + loadInfoFromCursor(info, c, cursorIconInfo); + + info.isDisabled |= ShortcutInfo.FLAG_DISABLED_LOCKED_USER; } - incrementPinnedShortcutCount(key, shouldPin); - info = new ShortcutInfo(pinnedShortcut, context); + incrementPinnedShortcutCount(key, false /* shouldPin */); } else { // item type == ITEM_TYPE_SHORTCUT - info = getShortcutInfo(c, context, titleIndex, cursorIconInfo); + info = getShortcutInfo(c, cursorIconInfo); // Shortcuts are only available on the primary profile if (PackageManagerHelper.isAppSuspended(manager, targetPackage)) { @@ -2046,7 +2028,7 @@ public class LauncherModel extends BroadcastReceiver FolderInfo folderInfo = findOrMakeFolder(sBgFolders, id); // Do not trim the folder label, as is was set by the user. - folderInfo.title = c.getString(titleIndex); + folderInfo.title = c.getString(cursorIconInfo.titleIndex); folderInfo.id = id; folderInfo.container = container; folderInfo.screenId = c.getInt(screenIndex); @@ -2800,9 +2782,11 @@ public class LauncherModel extends BroadcastReceiver if (!mDeepShortcutsLoaded) { mBgDeepShortcutMap.clear(); for (UserHandleCompat user : mUserManager.getUserProfiles()) { - List<ShortcutInfoCompat> shortcuts = mDeepShortcutManager - .queryForAllShortcuts(user); - updateDeepShortcutMap(null, user, shortcuts); + if (mUserManager.isUserUnlocked(user)) { + List<ShortcutInfoCompat> shortcuts = mDeepShortcutManager + .queryForAllShortcuts(user); + updateDeepShortcutMap(null, user, shortcuts); + } } synchronized (LoaderTask.this) { if (mStopped) { @@ -2907,18 +2891,23 @@ public class LauncherModel extends BroadcastReceiver } } - private void bindUpdatedShortcuts(final ArrayList<ShortcutInfo> updatedShortcuts, - UserHandleCompat user) { - if (!updatedShortcuts.isEmpty()) { + private void bindUpdatedShortcuts( + ArrayList<ShortcutInfo> updatedShortcuts, UserHandleCompat user) { + bindUpdatedShortcuts(updatedShortcuts, new ArrayList<ShortcutInfo>(), user); + } + + private void bindUpdatedShortcuts( + final ArrayList<ShortcutInfo> updatedShortcuts, + final ArrayList<ShortcutInfo> removedShortcuts, + final UserHandleCompat user) { + if (!updatedShortcuts.isEmpty() || !removedShortcuts.isEmpty()) { final Callbacks callbacks = getCallback(); - final UserHandleCompat userFinal = user; mHandler.post(new Runnable() { public void run() { Callbacks cb = getCallback(); if (cb != null && callbacks == cb) { - cb.bindShortcutsChanged(updatedShortcuts, - new ArrayList<ShortcutInfo>(), userFinal); + cb.bindShortcutsChanged(updatedShortcuts, removedShortcuts, user); } } }); @@ -3210,22 +3199,11 @@ public class LauncherModel extends BroadcastReceiver } } - if (!updatedShortcuts.isEmpty() || !removedShortcuts.isEmpty()) { - final Callbacks callbacks = getCallback(); - mHandler.post(new Runnable() { - - public void run() { - Callbacks cb = getCallback(); - if (callbacks == cb && cb != null) { - callbacks.bindShortcutsChanged( - updatedShortcuts, removedShortcuts, mUser); - } - } - }); - if (!removedShortcuts.isEmpty()) { - deleteItemsFromDatabase(context, removedShortcuts); - } + bindUpdatedShortcuts(updatedShortcuts, removedShortcuts, mUser); + if (!removedShortcuts.isEmpty()) { + deleteItemsFromDatabase(context, removedShortcuts); } + if (!widgets.isEmpty()) { final Callbacks callbacks = getCallback(); mHandler.post(new Runnable() { @@ -3387,6 +3365,74 @@ public class LauncherModel extends BroadcastReceiver } } + /** + * Task to handle changing of lock state of the user + */ + private class UserLockStateChangedTask implements Runnable { + + private final UserHandleCompat mUser; + + public UserLockStateChangedTask(UserHandleCompat user) { + mUser = user; + } + + @Override + public void run() { + boolean isUserUnlocked = mUserManager.isUserUnlocked(mUser); + Context context = mApp.getContext(); + + HashMap<ShortcutKey, ShortcutInfoCompat> pinnedShortcuts = new HashMap<>(); + if (isUserUnlocked) { + for (ShortcutInfoCompat shortcut : + mDeepShortcutManager.queryForPinnedShortcuts(null, mUser)) { + pinnedShortcuts.put(ShortcutKey.fromInfo(shortcut), shortcut); + } + } + + // Update the workspace to reflect the changes to updated shortcuts residing on it. + ArrayList<ShortcutInfo> updatedShortcutInfos = new ArrayList<>(); + ArrayList<ShortcutInfo> deletedShortcutInfos = new ArrayList<>(); + for (ItemInfo itemInfo : sBgItemsIdMap) { + if (itemInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT + && mUser.equals(itemInfo.user)) { + ShortcutInfo si = (ShortcutInfo) itemInfo; + if (isUserUnlocked) { + ShortcutInfoCompat shortcut = + pinnedShortcuts.get(ShortcutKey.fromItemInfo(si)); + // We couldn't verify the shortcut during loader. If its no longer available + // (probably due to clear data), delete the workspace item as well + if (shortcut == null) { + deletedShortcutInfos.add(si); + continue; + } + si.isDisabled &= ~ShortcutInfo.FLAG_DISABLED_LOCKED_USER; + si.updateFromDeepShortcutInfo(shortcut, context); + } else { + si.isDisabled |= ShortcutInfo.FLAG_DISABLED_LOCKED_USER; + } + updatedShortcutInfos.add(si); + } + } + bindUpdatedShortcuts(updatedShortcutInfos, deletedShortcutInfos, mUser); + if (!deletedShortcutInfos.isEmpty()) { + deleteItemsFromDatabase(context, deletedShortcutInfos); + } + + // Remove shortcut id map for that user + Iterator<ComponentKey> keysIter = mBgDeepShortcutMap.keySet().iterator(); + while (keysIter.hasNext()) { + if (keysIter.next().user.equals(mUser)) { + keysIter.remove(); + } + } + + if (isUserUnlocked) { + updateDeepShortcutMap(null, mUser, mDeepShortcutManager.queryForAllShortcuts(mUser)); + } + bindDeepShortcuts(); + } + } + private void bindWidgetsModel(final Callbacks callbacks, final WidgetsModel model) { mHandler.post(new Runnable() { @Override @@ -3447,12 +3493,12 @@ public class LauncherModel extends BroadcastReceiver * Make an ShortcutInfo object for a restored application or shortcut item that points * to a package that is not yet installed on the system. */ - public ShortcutInfo getRestoredItemInfo(Cursor c, int titleIndex, Intent intent, - int promiseType, int itemType, CursorIconInfo iconInfo, Context context) { + public ShortcutInfo getRestoredItemInfo(Cursor c, Intent intent, + int promiseType, int itemType, CursorIconInfo iconInfo) { final ShortcutInfo info = new ShortcutInfo(); info.user = UserHandleCompat.myUserHandle(); - Bitmap icon = iconInfo.loadIcon(c, info, context); + Bitmap icon = iconInfo.loadIcon(c, info); // the fallback icon if (icon == null) { mIconCache.getTitleAndIcon(info, intent, info.user, false /* useLowResIcon */); @@ -3461,13 +3507,13 @@ public class LauncherModel extends BroadcastReceiver } if ((promiseType & ShortcutInfo.FLAG_RESTORED_ICON) != 0) { - String title = (c != null) ? c.getString(titleIndex) : null; + String title = iconInfo.getTitle(c); if (!TextUtils.isEmpty(title)) { info.title = Utilities.trim(title); } } else if ((promiseType & ShortcutInfo.FLAG_AUTOINTALL_ICON) != 0) { if (TextUtils.isEmpty(info.title)) { - info.title = (c != null) ? Utilities.trim(c.getString(titleIndex)) : ""; + info.title = iconInfo.getTitle(c); } } else { throw new InvalidParameterException("Invalid restoreType " + promiseType); @@ -3504,7 +3550,7 @@ public class LauncherModel extends BroadcastReceiver * If c is not null, then it will be used to fill in missing data like the title and icon. */ public ShortcutInfo getAppShortcutInfo(Intent intent, - UserHandleCompat user, Context context, Cursor c, int iconIndex, int titleIndex, + UserHandleCompat user, Cursor c, CursorIconInfo iconInfo, boolean allowMissingTarget, boolean useLowResIcon) { if (user == null) { Log.d(TAG, "Null user found in getShortcutInfo"); @@ -3529,7 +3575,7 @@ public class LauncherModel extends BroadcastReceiver final ShortcutInfo info = new ShortcutInfo(); mIconCache.getTitleAndIcon(info, componentName, lai, user, false, useLowResIcon); if (mIconCache.isDefaultIcon(info.getIcon(mIconCache), user) && c != null) { - Bitmap icon = Utilities.createIconBitmap(c, iconIndex, context); + Bitmap icon = iconInfo.loadIcon(c); info.setIcon(icon == null ? mIconCache.getDefaultIcon(user) : icon); } @@ -3539,7 +3585,7 @@ public class LauncherModel extends BroadcastReceiver // from the db if (TextUtils.isEmpty(info.title) && c != null) { - info.title = Utilities.trim(c.getString(titleIndex)); + info.title = iconInfo.getTitle(c); } // fall back to the class name of the activity @@ -3603,8 +3649,7 @@ public class LauncherModel extends BroadcastReceiver /** * Make an ShortcutInfo object for a shortcut that isn't an application. */ - @Thunk ShortcutInfo getShortcutInfo(Cursor c, Context context, - int titleIndex, CursorIconInfo iconInfo) { + @Thunk ShortcutInfo getShortcutInfo(Cursor c, CursorIconInfo iconInfo) { final ShortcutInfo info = new ShortcutInfo(); // Non-app shortcuts are only supported for current user. info.user = UserHandleCompat.myUserHandle(); @@ -3612,16 +3657,22 @@ public class LauncherModel extends BroadcastReceiver // TODO: If there's an explicit component and we can't install that, delete it. - info.title = Utilities.trim(c.getString(titleIndex)); + loadInfoFromCursor(info, c, iconInfo); + return info; + } - Bitmap icon = iconInfo.loadIcon(c, info, context); + /** + * Make an ShortcutInfo object for a shortcut that isn't an application. + */ + public void loadInfoFromCursor(ShortcutInfo info, Cursor c, CursorIconInfo iconInfo) { + info.title = iconInfo.getTitle(c); + Bitmap icon = iconInfo.loadIcon(c, info); // the fallback icon if (icon == null) { icon = mIconCache.getDefaultIcon(info.user); info.usingFallbackIcon = true; } info.setIcon(icon); - return info; } ShortcutInfo infoFromShortcutIntent(Context context, Intent data) { diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java index 00ac9bda4..b460944f8 100644 --- a/src/com/android/launcher3/ShortcutInfo.java +++ b/src/com/android/launcher3/ShortcutInfo.java @@ -117,13 +117,17 @@ public class ShortcutInfo extends ItemInfo { */ public static final int FLAG_DISABLED_QUIET_USER = 1 << 3; - /** * Indicates that the icon is disabled as the publisher has disabled the actual shortcut. */ public static final int FLAG_DISABLED_BY_PUBLISHER = 1 << 4; /** + * Indicates that the icon is disabled as the user partition is currently locked. + */ + public static final int FLAG_DISABLED_LOCKED_USER = 1 << 5; + + /** * Could be disabled, if the the app is installed but unavailable (eg. in safe mode or when * sd-card is not available). */ @@ -206,7 +210,6 @@ public class ShortcutInfo extends ItemInfo { public ShortcutInfo(ShortcutInfoCompat shortcutInfo, Context context) { user = shortcutInfo.getUserHandle(); itemType = LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT; - intent = shortcutInfo.makeIntent(context); flags = 0; updateFromDeepShortcutInfo(shortcutInfo, context); } @@ -291,6 +294,8 @@ public class ShortcutInfo extends ItemInfo { } public void updateFromDeepShortcutInfo(ShortcutInfoCompat shortcutInfo, Context context) { + // {@link ShortcutInfoCompat#getActivity} can change during an update. Recreate the intent + intent = shortcutInfo.makeIntent(context); title = shortcutInfo.getShortLabel(); CharSequence label = shortcutInfo.getLongLabel(); diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 566344485..5105c1a8b 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -80,6 +80,7 @@ import com.android.launcher3.shortcuts.DeepShortcutManager; import com.android.launcher3.shortcuts.ShortcutsContainerListener; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto.Target; +import com.android.launcher3.util.ItemInfoMatcher; import com.android.launcher3.util.LongArrayMap; import com.android.launcher3.util.MultiStateAlphaController; import com.android.launcher3.util.Thunk; @@ -4023,66 +4024,30 @@ public class Workspace extends PagedView }); } - // Removes ALL items that match a given package name, this is usually called when a package - // has been removed and we want to remove all components (widgets, shortcuts, apps) that - // belong to that package. - void removeItemsByPackageName(final HashSet<String> packageNames, final UserHandleCompat user) { - // Filter out all the ItemInfos that this is going to affect - final HashSet<ItemInfo> infos = new HashSet<ItemInfo>(); - final HashSet<ComponentName> cns = new HashSet<ComponentName>(); - ArrayList<CellLayout> cellLayouts = getWorkspaceAndHotseatCellLayouts(); - for (CellLayout layoutParent : cellLayouts) { - ViewGroup layout = layoutParent.getShortcutsAndWidgets(); - int childCount = layout.getChildCount(); - for (int i = 0; i < childCount; ++i) { - View view = layout.getChildAt(i); - infos.add((ItemInfo) view.getTag()); - } - } - LauncherModel.ItemInfoFilter filter = new LauncherModel.ItemInfoFilter() { - @Override - public boolean filterItem(ItemInfo parent, ItemInfo info, - ComponentName cn) { - if (packageNames.contains(cn.getPackageName()) - && info.user.equals(user)) { - cns.add(cn); - return true; - } - return false; - } - }; - LauncherModel.filterItemInfos(infos, filter); - - // Remove the affected components - removeItemsByComponentName(cns, user); - } - /** - * Removes items that match the item info specified. When applications are removed + * Removes items that match the {@param matcher}. When applications are removed * as a part of an update, this is called to ensure that other widgets and application * shortcuts are not removed. */ - void removeItemsByComponentName(final HashSet<ComponentName> componentNames, - final UserHandleCompat user) { + public void removeItemsByMatcher(final ItemInfoMatcher matcher) { ArrayList<CellLayout> cellLayouts = getWorkspaceAndHotseatCellLayouts(); for (final CellLayout layoutParent: cellLayouts) { final ViewGroup layout = layoutParent.getShortcutsAndWidgets(); - final HashMap<ItemInfo, View> children = new HashMap<ItemInfo, View>(); + final HashMap<ItemInfo, View> children = new HashMap<>(); for (int j = 0; j < layout.getChildCount(); j++) { final View view = layout.getChildAt(j); children.put((ItemInfo) view.getTag(), view); } - final ArrayList<View> childrenToRemove = new ArrayList<View>(); - final HashMap<FolderInfo, ArrayList<ShortcutInfo>> folderAppsToRemove = - new HashMap<FolderInfo, ArrayList<ShortcutInfo>>(); + final ArrayList<View> childrenToRemove = new ArrayList<>(); + final HashMap<FolderInfo, ArrayList<ShortcutInfo>> folderAppsToRemove = new HashMap<>(); LauncherModel.ItemInfoFilter filter = new LauncherModel.ItemInfoFilter() { @Override public boolean filterItem(ItemInfo parent, ItemInfo info, - ComponentName cn) { + ComponentName cn) { if (parent instanceof FolderInfo) { - if (componentNames.contains(cn) && info.user.equals(user)) { + if (matcher.matches(info, cn)) { FolderInfo folder = (FolderInfo) parent; ArrayList<ShortcutInfo> appsToRemove; if (folderAppsToRemove.containsKey(folder)) { @@ -4095,7 +4060,7 @@ public class Workspace extends PagedView return true; } } else { - if (componentNames.contains(cn) && info.user.equals(user)) { + if (matcher.matches(info, cn)) { childrenToRemove.add(children.get(info)); return true; } @@ -4227,7 +4192,7 @@ public class Workspace extends PagedView HashSet<String> packages = new HashSet<>(1); packages.add(packageName); LauncherModel.deletePackageFromDatabase(mLauncher, packageName, user); - removeItemsByPackageName(packages, user); + removeItemsByMatcher(ItemInfoMatcher.ofPackages(packages, user)); } public void updateRestoreItems(final HashSet<ItemInfo> updates) { diff --git a/src/com/android/launcher3/compat/LauncherAppsCompat.java b/src/com/android/launcher3/compat/LauncherAppsCompat.java index 3c1013db4..645e68a8f 100644 --- a/src/com/android/launcher3/compat/LauncherAppsCompat.java +++ b/src/com/android/launcher3/compat/LauncherAppsCompat.java @@ -31,15 +31,6 @@ import java.util.List; public abstract class LauncherAppsCompat { - public static final String ACTION_MANAGED_PROFILE_ADDED = - "android.intent.action.MANAGED_PROFILE_ADDED"; - public static final String ACTION_MANAGED_PROFILE_REMOVED = - "android.intent.action.MANAGED_PROFILE_REMOVED"; - public static final String ACTION_MANAGED_PROFILE_AVAILABLE = - "android.intent.action.MANAGED_PROFILE_AVAILABLE"; - public static final String ACTION_MANAGED_PROFILE_UNAVAILABLE = - "android.intent.action.MANAGED_PROFILE_UNAVAILABLE"; - public interface OnAppsChangedCallbackCompat { void onPackageRemoved(String packageName, UserHandleCompat user); void onPackageAdded(String packageName, UserHandleCompat user); diff --git a/src/com/android/launcher3/compat/UserManagerCompat.java b/src/com/android/launcher3/compat/UserManagerCompat.java index 978f9229d..29ed5d9ba 100644 --- a/src/com/android/launcher3/compat/UserManagerCompat.java +++ b/src/com/android/launcher3/compat/UserManagerCompat.java @@ -57,4 +57,5 @@ public abstract class UserManagerCompat { public abstract CharSequence getBadgedLabelForUser(CharSequence label, UserHandleCompat user); public abstract long getUserCreationTime(UserHandleCompat user); public abstract boolean isQuietModeEnabled(UserHandleCompat user); + public abstract boolean isUserUnlocked(UserHandleCompat user); } diff --git a/src/com/android/launcher3/compat/UserManagerCompatV16.java b/src/com/android/launcher3/compat/UserManagerCompatV16.java index a006efd50..e678ffa3d 100644 --- a/src/com/android/launcher3/compat/UserManagerCompatV16.java +++ b/src/com/android/launcher3/compat/UserManagerCompatV16.java @@ -55,4 +55,9 @@ public class UserManagerCompatV16 extends UserManagerCompat { public boolean isQuietModeEnabled(UserHandleCompat user) { return false; } + + @Override + public boolean isUserUnlocked(UserHandleCompat user) { + return true; + } } diff --git a/src/com/android/launcher3/compat/UserManagerCompatVN.java b/src/com/android/launcher3/compat/UserManagerCompatVN.java index ae41e68a3..771d141c3 100644 --- a/src/com/android/launcher3/compat/UserManagerCompatVN.java +++ b/src/com/android/launcher3/compat/UserManagerCompatVN.java @@ -16,15 +16,11 @@ package com.android.launcher3.compat; +import android.annotation.TargetApi; import android.content.Context; -import android.os.UserHandle; -import android.os.UserManager; -import android.util.Log; +import android.os.Build; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -//TODO: Once gogole3 SDK is updated to N, add @TargetApi(Build.VERSION_CODES.N) +@TargetApi(Build.VERSION_CODES.N) public class UserManagerCompatVN extends UserManagerCompatVL { private static final String TAG = "UserManagerCompatVN"; @@ -35,21 +31,17 @@ public class UserManagerCompatVN extends UserManagerCompatVL { @Override public boolean isQuietModeEnabled(UserHandleCompat user) { - if (user != null) { - try { - //TODO: Replace with proper API call once google3 SDK is updated. - Method isQuietModeEnabledMethod = UserManager.class.getMethod("isQuietModeEnabled", - UserHandle.class); - return (boolean) isQuietModeEnabledMethod.invoke(mUserManager, user.getUser()); - } catch (NoSuchMethodError | NoSuchMethodException | IllegalAccessException - | InvocationTargetException e) { - Log.e(TAG, "Running on N without isQuietModeEnabled", e); - } catch (IllegalArgumentException e) { - // TODO remove this when API is fixed to not throw this - // when called on user that isn't a managed profile. - } + return mUserManager.isQuietModeEnabled(user.getUser()); + } + + @Override + public boolean isUserUnlocked(UserHandleCompat user) { + // TODO: Remove the try-catch block when the API permission has been relaxed (b/30475753) + try { + return mUserManager.isUserUnlocked(user.getUser()); + } catch (RuntimeException e) { + return !isQuietModeEnabled(user); } - return false; } } diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java index 3447d5148..9da1cb313 100644 --- a/src/com/android/launcher3/dragndrop/DragController.java +++ b/src/com/android/launcher3/dragndrop/DragController.java @@ -46,6 +46,7 @@ import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; import com.android.launcher3.accessibility.DragViewStateAnnouncer; import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.util.ItemInfoMatcher; import com.android.launcher3.util.Thunk; import com.android.launcher3.util.TouchController; @@ -325,22 +326,14 @@ public class DragController implements DragDriver.EventListener, TouchController endDrag(); } - public void onAppsRemoved(final HashSet<String> packageNames, HashSet<ComponentName> cns) { + public void onAppsRemoved(ItemInfoMatcher matcher) { // Cancel the current drag if we are removing an app that we are dragging if (mDragObject != null) { - Object rawDragInfo = mDragObject.dragInfo; - if (rawDragInfo instanceof ShortcutInfo) { - ShortcutInfo dragInfo = (ShortcutInfo) rawDragInfo; - for (ComponentName componentName : cns) { - if (dragInfo.intent != null) { - ComponentName cn = dragInfo.intent.getComponent(); - boolean isSameComponent = cn != null && (cn.equals(componentName) || - packageNames.contains(cn.getPackageName())); - if (isSameComponent) { - cancelDrag(); - return; - } - } + ItemInfo dragInfo = mDragObject.dragInfo; + if (dragInfo instanceof ShortcutInfo) { + ComponentName cn = dragInfo.getTargetComponent(); + if (cn != null && matcher.matches(dragInfo, cn)) { + cancelDrag(); } } } diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutManager.java b/src/com/android/launcher3/shortcuts/DeepShortcutManager.java index 450c36df4..36bb2b5df 100644 --- a/src/com/android/launcher3/shortcuts/DeepShortcutManager.java +++ b/src/com/android/launcher3/shortcuts/DeepShortcutManager.java @@ -98,7 +98,7 @@ public class DeepShortcutManager { public void unpinShortcut(final ShortcutKey key) { if (Utilities.isNycMR1OrAbove()) { String packageName = key.componentName.getPackageName(); - String id = key.id; + String id = key.getId(); UserHandleCompat user = key.user; List<String> pinnedIds = extractIds(queryForPinnedShortcuts(packageName, user)); pinnedIds.remove(id); @@ -120,7 +120,7 @@ public class DeepShortcutManager { public void pinShortcut(final ShortcutKey key) { if (Utilities.isNycMR1OrAbove()) { String packageName = key.componentName.getPackageName(); - String id = key.id; + String id = key.getId(); UserHandleCompat user = key.user; List<String> pinnedIds = extractIds(queryForPinnedShortcuts(packageName, user)); pinnedIds.add(id); diff --git a/src/com/android/launcher3/shortcuts/ShortcutKey.java b/src/com/android/launcher3/shortcuts/ShortcutKey.java index c15f91dd3..405303042 100644 --- a/src/com/android/launcher3/shortcuts/ShortcutKey.java +++ b/src/com/android/launcher3/shortcuts/ShortcutKey.java @@ -1,7 +1,9 @@ package com.android.launcher3.shortcuts; import android.content.ComponentName; +import android.content.Intent; +import com.android.launcher3.ItemInfo; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.util.ComponentKey; @@ -9,16 +11,28 @@ import com.android.launcher3.util.ComponentKey; * A key that uniquely identifies a shortcut using its package, id, and user handle. */ public class ShortcutKey extends ComponentKey { - final String id; public ShortcutKey(String packageName, UserHandleCompat user, String id) { // Use the id as the class name. super(new ComponentName(packageName, id), user); - this.id = id; + } + + public String getId() { + return componentName.getClassName(); } public static ShortcutKey fromInfo(ShortcutInfoCompat shortcutInfo) { return new ShortcutKey(shortcutInfo.getPackage(), shortcutInfo.getUserHandle(), shortcutInfo.getId()); } + + public static ShortcutKey fromIntent(Intent intent, UserHandleCompat user) { + String shortcutId = intent.getStringExtra( + ShortcutInfoCompat.EXTRA_SHORTCUT_ID); + return new ShortcutKey(intent.getPackage(), user, shortcutId); + } + + public static ShortcutKey fromItemInfo(ItemInfo info) { + return fromIntent(info.getIntent(), info.user); + } } diff --git a/src/com/android/launcher3/util/CursorIconInfo.java b/src/com/android/launcher3/util/CursorIconInfo.java index 120eacd0a..4fefa986e 100644 --- a/src/com/android/launcher3/util/CursorIconInfo.java +++ b/src/com/android/launcher3/util/CursorIconInfo.java @@ -34,13 +34,24 @@ public class CursorIconInfo { public final int iconResourceIndex; public final int iconIndex; - public CursorIconInfo(Cursor c) { + public final int titleIndex; + + private final Context mContext; + + public CursorIconInfo(Context context, Cursor c) { + mContext = context; + iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON); iconPackageIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_PACKAGE); iconResourceIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE); + + titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE); } - public Bitmap loadIcon(Cursor c, ShortcutInfo info, Context context) { + /** + * Loads the icon from the cursor and updates the {@param info} if the icon is an app resource. + */ + public Bitmap loadIcon(Cursor c, ShortcutInfo info) { Bitmap icon = null; String packageName = c.getString(iconPackageIndex); String resourceName = c.getString(iconResourceIndex); @@ -48,12 +59,27 @@ public class CursorIconInfo { info.iconResource = new ShortcutIconResource(); info.iconResource.packageName = packageName; info.iconResource.resourceName = resourceName; - icon = Utilities.createIconBitmap(packageName, resourceName, context); + icon = Utilities.createIconBitmap(packageName, resourceName, mContext); } if (icon == null) { // Failed to load from resource, try loading from DB. - icon = Utilities.createIconBitmap(c, iconIndex, context); + icon = loadIcon(c); } return icon; } + + /** + * Loads the fixed bitmap from the icon if available. + */ + public Bitmap loadIcon(Cursor c) { + return Utilities.createIconBitmap(c, iconIndex, mContext); + } + + /** + * Returns the title or empty string + */ + public String getTitle(Cursor c) { + String title = c.getString(titleIndex); + return TextUtils.isEmpty(title) ? "" : Utilities.trim(c.getString(titleIndex)); + } } diff --git a/src/com/android/launcher3/util/ItemInfoMatcher.java b/src/com/android/launcher3/util/ItemInfoMatcher.java new file mode 100644 index 000000000..6189bf2f4 --- /dev/null +++ b/src/com/android/launcher3/util/ItemInfoMatcher.java @@ -0,0 +1,64 @@ +/* + * 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.ComponentName; + +import com.android.launcher3.ItemInfo; +import com.android.launcher3.LauncherSettings.Favorites; +import com.android.launcher3.compat.UserHandleCompat; +import com.android.launcher3.shortcuts.ShortcutKey; + +import java.util.HashSet; + +/** + * A utility class to check for {@link ItemInfo} + */ +public abstract class ItemInfoMatcher { + + public abstract boolean matches(ItemInfo info, ComponentName cn); + + public static ItemInfoMatcher ofComponents( + final HashSet<ComponentName> components, final UserHandleCompat user) { + return new ItemInfoMatcher() { + @Override + public boolean matches(ItemInfo info, ComponentName cn) { + return components.contains(cn) && info.user.equals(user); + } + }; + } + + public static ItemInfoMatcher ofPackages( + final HashSet<String> packageNames, final UserHandleCompat user) { + return new ItemInfoMatcher() { + @Override + public boolean matches(ItemInfo info, ComponentName cn) { + return packageNames.contains(cn.getPackageName()) && info.user.equals(user); + } + }; + } + + public static ItemInfoMatcher ofShortcutKeys(final HashSet<ShortcutKey> keys) { + return new ItemInfoMatcher() { + @Override + public boolean matches(ItemInfo info, ComponentName cn) { + return info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT && + keys.contains(ShortcutKey.fromItemInfo(info)); + } + }; + } +} |