diff options
28 files changed, 1732 insertions, 502 deletions
diff --git a/src/com/android/launcher3/AllAppsList.java b/src/com/android/launcher3/AllAppsList.java index 89b291f28..c25aa4094 100644 --- a/src/com/android/launcher3/AllAppsList.java +++ b/src/com/android/launcher3/AllAppsList.java @@ -23,6 +23,10 @@ import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import com.android.launcher3.compat.LauncherActivityInfoCompat; +import com.android.launcher3.compat.LauncherAppsCompat; +import com.android.launcher3.compat.UserHandleCompat; + import java.util.ArrayList; import java.util.List; @@ -66,7 +70,7 @@ class AllAppsList { if (mAppFilter != null && !mAppFilter.shouldShowApp(info.componentName)) { return; } - if (findActivity(data, info.componentName)) { + if (findActivity(data, info.componentName, info.user)) { return; } data.add(info); @@ -92,12 +96,14 @@ class AllAppsList { /** * Add the icons for the supplied apk called packageName. */ - public void addPackage(Context context, String packageName) { - final List<ResolveInfo> matches = findActivitiesForPackage(context, packageName); + public void addPackage(Context context, String packageName, UserHandleCompat user) { + final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context); + final List<LauncherActivityInfoCompat> matches = launcherApps.getActivityList(packageName, + user); if (matches.size() > 0) { - for (ResolveInfo info : matches) { - add(new AppInfo(context.getPackageManager(), info, mIconCache, null)); + for (LauncherActivityInfoCompat info : matches) { + add(new AppInfo(context, info, user, mIconCache, null)); } } } @@ -105,34 +111,37 @@ class AllAppsList { /** * Remove the apps for the given apk identified by packageName. */ - public void removePackage(String packageName) { + public void removePackage(String packageName, UserHandleCompat user) { final List<AppInfo> data = this.data; for (int i = data.size() - 1; i >= 0; i--) { AppInfo info = data.get(i); final ComponentName component = info.intent.getComponent(); - if (packageName.equals(component.getPackageName())) { + if (info.user.equals(user) && packageName.equals(component.getPackageName())) { removed.add(info); data.remove(i); } } - mIconCache.remove(packageName); + mIconCache.remove(packageName, user); } /** * Add and remove icons for this package which has been updated. */ - public void updatePackage(Context context, String packageName) { - final List<ResolveInfo> matches = findActivitiesForPackage(context, packageName); + public void updatePackage(Context context, String packageName, UserHandleCompat user) { + final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context); + final List<LauncherActivityInfoCompat> matches = launcherApps.getActivityList(packageName, + user); if (matches.size() > 0) { // Find disabled/removed activities and remove them from data and add them // to the removed list. for (int i = data.size() - 1; i >= 0; i--) { final AppInfo applicationInfo = data.get(i); final ComponentName component = applicationInfo.intent.getComponent(); - if (packageName.equals(component.getPackageName())) { + if (user.equals(applicationInfo.user) + && packageName.equals(component.getPackageName())) { if (!findActivity(matches, component)) { removed.add(applicationInfo); - mIconCache.remove(component); + mIconCache.remove(component, user); data.remove(i); } } @@ -142,14 +151,14 @@ class AllAppsList { // Also updates existing activities with new labels/icons int count = matches.size(); for (int i = 0; i < count; i++) { - final ResolveInfo info = matches.get(i); + final LauncherActivityInfoCompat info = matches.get(i); AppInfo applicationInfo = findApplicationInfoLocked( - info.activityInfo.applicationInfo.packageName, - info.activityInfo.name); + info.getComponentName().getPackageName(), user, + info.getComponentName().getShortClassName()); if (applicationInfo == null) { - add(new AppInfo(context.getPackageManager(), info, mIconCache, null)); + add(new AppInfo(context, info, user, mIconCache, null)); } else { - mIconCache.remove(applicationInfo.componentName); + mIconCache.remove(applicationInfo.componentName, user); mIconCache.getTitleAndIcon(applicationInfo, info, null); modified.add(applicationInfo); } @@ -159,37 +168,24 @@ class AllAppsList { for (int i = data.size() - 1; i >= 0; i--) { final AppInfo applicationInfo = data.get(i); final ComponentName component = applicationInfo.intent.getComponent(); - if (packageName.equals(component.getPackageName())) { + if (user.equals(applicationInfo.user) + && packageName.equals(component.getPackageName())) { removed.add(applicationInfo); - mIconCache.remove(component); + mIconCache.remove(component, user); data.remove(i); } } } } - /** - * Query the package manager for MAIN/LAUNCHER activities in the supplied package. - */ - static List<ResolveInfo> findActivitiesForPackage(Context context, String packageName) { - final PackageManager packageManager = context.getPackageManager(); - - final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); - mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); - mainIntent.setPackage(packageName); - - final List<ResolveInfo> apps = packageManager.queryIntentActivities(mainIntent, 0); - return apps != null ? apps : new ArrayList<ResolveInfo>(); - } /** * Returns whether <em>apps</em> contains <em>component</em>. */ - private static boolean findActivity(List<ResolveInfo> apps, ComponentName component) { - final String className = component.getClassName(); - for (ResolveInfo info : apps) { - final ActivityInfo activityInfo = info.activityInfo; - if (activityInfo.name.equals(className)) { + private static boolean findActivity(List<LauncherActivityInfoCompat> apps, + ComponentName component) { + for (LauncherActivityInfoCompat info : apps) { + if (info.getComponentName().equals(component)) { return true; } } @@ -197,13 +193,24 @@ class AllAppsList { } /** + * Query the launcher apps service for whether the supplied package has + * MAIN/LAUNCHER activities in the supplied package. + */ + static boolean packageHasActivities(Context context, String packageName, + UserHandleCompat user) { + final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context); + return launcherApps.getActivityList(packageName, user).size() > 0; + } + + /** * Returns whether <em>apps</em> contains <em>component</em>. */ - private static boolean findActivity(ArrayList<AppInfo> apps, ComponentName component) { + private static boolean findActivity(ArrayList<AppInfo> apps, ComponentName component, + UserHandleCompat user) { final int N = apps.size(); - for (int i=0; i<N; i++) { + for (int i = 0; i < N; i++) { final AppInfo info = apps.get(i); - if (info.componentName.equals(component)) { + if (info.user.equals(user) && info.componentName.equals(component)) { return true; } } @@ -213,10 +220,11 @@ class AllAppsList { /** * Find an ApplicationInfo object for the given packageName and className. */ - private AppInfo findApplicationInfoLocked(String packageName, String className) { + private AppInfo findApplicationInfoLocked(String packageName, UserHandleCompat user, + String className) { for (AppInfo info: data) { final ComponentName component = info.intent.getComponent(); - if (packageName.equals(component.getPackageName()) + if (user.equals(info.user) && packageName.equals(component.getPackageName()) && className.equals(component.getClassName())) { return info; } diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java index c16e98f0c..40e8e6d0e 100644 --- a/src/com/android/launcher3/AppInfo.java +++ b/src/com/android/launcher3/AppInfo.java @@ -17,14 +17,18 @@ package com.android.launcher3; import android.content.ComponentName; +import android.content.Context; import android.content.Intent; import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.graphics.Bitmap; import android.util.Log; +import com.android.launcher3.compat.LauncherActivityInfoCompat; +import com.android.launcher3.compat.UserManagerCompat; +import com.android.launcher3.compat.UserHandleCompat; + import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -72,28 +76,24 @@ public class AppInfo extends ItemInfo { /** * Must not hold the Context. */ - public AppInfo(PackageManager pm, ResolveInfo info, IconCache iconCache, - HashMap<Object, CharSequence> labelCache) { - final String packageName = info.activityInfo.applicationInfo.packageName; - - this.componentName = new ComponentName(packageName, info.activityInfo.name); + public AppInfo(Context context, LauncherActivityInfoCompat info, UserHandleCompat user, + IconCache iconCache, HashMap<Object, CharSequence> labelCache) { + this.componentName = info.getComponentName(); this.container = ItemInfo.NO_ID; - this.setActivity(componentName, - Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); - - try { - PackageInfo pi = pm.getPackageInfo(packageName, 0); - flags = initFlags(pi); - firstInstallTime = initFirstInstallTime(pi); - } catch (NameNotFoundException e) { - Log.d(TAG, "PackageManager.getApplicationInfo failed for " + packageName); - } + flags = initFlags(info); + firstInstallTime = info.getFirstInstallTime(); iconCache.getTitleAndIcon(this, info, labelCache); + intent = new Intent(Intent.ACTION_MAIN); + intent.addCategory(Intent.CATEGORY_LAUNCHER); + intent.setComponent(info.getComponentName()); + long serialNumber = UserManagerCompat.getInstance(context).getSerialNumberForUser(user); + intent.putExtra(EXTRA_PROFILE, serialNumber); + this.user = user; } - public static int initFlags(PackageInfo pi) { - int appFlags = pi.applicationInfo.flags; + private static int initFlags(LauncherActivityInfoCompat info) { + int appFlags = info.getApplicationFlags(); int flags = 0; if ((appFlags & android.content.pm.ApplicationInfo.FLAG_SYSTEM) == 0) { flags |= DOWNLOADED_FLAG; @@ -105,10 +105,6 @@ public class AppInfo extends ItemInfo { return flags; } - public static long initFirstInstallTime(PackageInfo pi) { - return pi.firstInstallTime; - } - public AppInfo(AppInfo info) { super(info); componentName = info.componentName; @@ -116,21 +112,7 @@ public class AppInfo extends ItemInfo { intent = new Intent(info.intent); flags = info.flags; firstInstallTime = info.firstInstallTime; - } - - /** - * Creates the application intent based on a component name and various launch flags. - * Sets {@link #itemType} to {@link LauncherSettings.BaseLauncherColumns#ITEM_TYPE_APPLICATION}. - * - * @param className the class name of the component representing the intent - * @param launchFlags the launch flags - */ - final void setActivity(ComponentName className, int launchFlags) { - intent = new Intent(Intent.ACTION_MAIN); - intent.addCategory(Intent.CATEGORY_LAUNCHER); - intent.setComponent(className); - intent.setFlags(launchFlags); - itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_APPLICATION; + iconBitmap = info.iconBitmap; } @Override @@ -139,7 +121,7 @@ public class AppInfo extends ItemInfo { + " type=" + this.itemType + " container=" + this.container + " screen=" + screenId + " cellX=" + cellX + " cellY=" + cellY + " spanX=" + spanX + " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos) - + ")"; + + " user=" + user + ")"; } public static void dumpApplicationInfoList(String tag, String label, ArrayList<AppInfo> list) { diff --git a/src/com/android/launcher3/AppsCustomizePagedView.java b/src/com/android/launcher3/AppsCustomizePagedView.java index d6e0bb49e..9d86959c4 100644 --- a/src/com/android/launcher3/AppsCustomizePagedView.java +++ b/src/com/android/launcher3/AppsCustomizePagedView.java @@ -1575,7 +1575,8 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen int length = list.size(); for (int i = 0; i < length; ++i) { AppInfo info = list.get(i); - if (info.intent.getComponent().equals(removeComponent)) { + if (info.user.equals(item.user) + && info.intent.getComponent().equals(removeComponent)) { return i; } } diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java index 75d906bc2..20546b8d8 100644 --- a/src/com/android/launcher3/DeleteDropTarget.java +++ b/src/com/android/launcher3/DeleteDropTarget.java @@ -38,6 +38,9 @@ import android.view.animation.AnimationUtils; import android.view.animation.DecelerateInterpolator; import android.view.animation.LinearInterpolator; +import com.android.launcher3.compat.LauncherAppsCompat; +import com.android.launcher3.compat.UserHandleCompat; + import java.util.List; import java.util.Set; @@ -184,6 +187,11 @@ public class DeleteDropTarget extends ButtonDropTarget { if (!willAcceptDrop(info) || isAllAppsWidget(source, info)) { isVisible = false; } + if (useUninstallLabel && + !(((ItemInfo) info).user.equals(UserHandleCompat.myUserHandle()))) { + // Don't support uninstall for apps from other profiles. + isVisible = false; + } if (useUninstallLabel) { setCompoundDrawablesRelativeWithIntrinsicBounds(mUninstallDrawable, null, null, null); @@ -279,25 +287,27 @@ public class DeleteDropTarget extends ButtonDropTarget { if (isAllAppsApplication(d.dragSource, item)) { // Uninstall the application if it is being dragged from AppsCustomize AppInfo appInfo = (AppInfo) item; - mLauncher.startApplicationUninstallActivity(appInfo.componentName, appInfo.flags); + // We don't support uninstalling apps from other profiles. + if (item.user.equals(UserHandleCompat.myUserHandle())) { + mLauncher.startApplicationUninstallActivity(appInfo.componentName, appInfo.flags); + } } else if (isUninstallFromWorkspace(d)) { ShortcutInfo shortcut = (ShortcutInfo) item; - if (shortcut.intent != null && shortcut.intent.getComponent() != null) { + // We don't support uninstalling apps from other profiles. + if (shortcut.intent != null && shortcut.intent.getComponent() != null && + shortcut.user.equals(UserHandleCompat.myUserHandle())) { final ComponentName componentName = shortcut.intent.getComponent(); final DragSource dragSource = d.dragSource; - int flags = AppInfo.initFlags( - ShortcutInfo.getPackageInfo(getContext(), componentName.getPackageName())); mWaitingForUninstall = - mLauncher.startApplicationUninstallActivity(componentName, flags); + mLauncher.startApplicationUninstallActivity(componentName, shortcut.flags); if (mWaitingForUninstall) { final Runnable checkIfUninstallWasSuccess = new Runnable() { @Override public void run() { mWaitingForUninstall = false; String packageName = componentName.getPackageName(); - List<ResolveInfo> activities = - AllAppsList.findActivitiesForPackage(getContext(), packageName); - boolean uninstallSuccessful = activities.size() == 0; + boolean uninstallSuccessful = !AllAppsList.packageHasActivities( + getContext(), packageName, UserHandleCompat.myUserHandle()); if (dragSource instanceof Folder) { ((Folder) dragSource). onUninstallActivityReturned(uninstallSuccessful); diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java index 61fa7e574..85a792f4b 100644 --- a/src/com/android/launcher3/FolderInfo.java +++ b/src/com/android/launcher3/FolderInfo.java @@ -17,6 +17,9 @@ package com.android.launcher3; import android.content.ContentValues; +import android.content.Context; + +import com.android.launcher3.compat.UserHandleCompat; import java.util.ArrayList; import java.util.Arrays; @@ -40,6 +43,7 @@ public class FolderInfo extends ItemInfo { FolderInfo() { itemType = LauncherSettings.Favorites.ITEM_TYPE_FOLDER; + user = UserHandleCompat.myUserHandle(); } /** @@ -76,8 +80,8 @@ public class FolderInfo extends ItemInfo { } @Override - void onAddToDatabase(ContentValues values) { - super.onAddToDatabase(values); + void onAddToDatabase(Context context, ContentValues values) { + super.onAddToDatabase(context, values); values.put(LauncherSettings.Favorites.TITLE, title.toString()); } diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index ee9f4d4b0..05be2141e 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -31,8 +31,14 @@ import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.drawable.Drawable; +import android.os.Build; import android.util.Log; +import com.android.launcher3.compat.LauncherActivityInfoCompat; +import com.android.launcher3.compat.LauncherAppsCompat; +import com.android.launcher3.compat.UserHandleCompat; +import com.android.launcher3.compat.UserManagerCompat; + import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; @@ -61,11 +67,35 @@ public class IconCache { public String title; } - private final Bitmap mDefaultIcon; + private static class CacheKey { + public ComponentName componentName; + public UserHandleCompat user; + + CacheKey(ComponentName componentName, UserHandleCompat user) { + this.componentName = componentName; + this.user = user; + } + + @Override + public int hashCode() { + return componentName.hashCode() + user.hashCode(); + } + + @Override + public boolean equals(Object o) { + CacheKey other = (CacheKey) o; + return other.componentName.equals(componentName) && other.user.equals(user); + } + } + + private final HashMap<UserHandleCompat, Bitmap> mDefaultIcons = + new HashMap<UserHandleCompat, Bitmap>(); private final Context mContext; private final PackageManager mPackageManager; - private final HashMap<ComponentName, CacheEntry> mCache = - new HashMap<ComponentName, CacheEntry>(INITIAL_ICON_CACHE_CAPACITY); + private final UserManagerCompat mUserManager; + private final LauncherAppsCompat mLauncherApps; + private final HashMap<CacheKey, CacheEntry> mCache = + new HashMap<CacheKey, CacheEntry>(INITIAL_ICON_CACHE_CAPACITY); private int mIconDpi; public IconCache(Context context) { @@ -74,10 +104,13 @@ public class IconCache { mContext = context; mPackageManager = context.getPackageManager(); + mUserManager = UserManagerCompat.getInstance(mContext); + mLauncherApps = LauncherAppsCompat.getInstance(mContext); mIconDpi = activityManager.getLauncherLargeIconDensity(); // need to set mIconDpi before getting default icon - mDefaultIcon = makeDefaultIcon(); + UserHandleCompat myUser = UserHandleCompat.myUserHandle(); + mDefaultIcons.put(myUser, makeDefaultIcon(myUser)); } public Drawable getFullResDefaultActivityIcon() { @@ -134,8 +167,9 @@ public class IconCache { return getFullResDefaultActivityIcon(); } - private Bitmap makeDefaultIcon() { - Drawable d = getFullResDefaultActivityIcon(); + private Bitmap makeDefaultIcon(UserHandleCompat user) { + Drawable unbadged = getFullResDefaultActivityIcon(); + Drawable d = mUserManager.getBadgedDrawableForUser(unbadged, user); Bitmap b = Bitmap.createBitmap(Math.max(d.getIntrinsicWidth(), 1), Math.max(d.getIntrinsicHeight(), 1), Bitmap.Config.ARGB_8888); @@ -149,24 +183,25 @@ public class IconCache { /** * Remove any records for the supplied ComponentName. */ - public void remove(ComponentName componentName) { + public void remove(ComponentName componentName, UserHandleCompat user) { synchronized (mCache) { - mCache.remove(componentName); + mCache.remove(new CacheKey(componentName, user)); } } /** * Remove any records for the supplied package name. */ - public void remove(String packageName) { - HashSet<ComponentName> forDeletion = new HashSet<ComponentName>(); - for (ComponentName componentName: mCache.keySet()) { - if (componentName.getPackageName().equals(packageName)) { - forDeletion.add(componentName); + public void remove(String packageName, UserHandleCompat user) { + HashSet<CacheKey> forDeletion = new HashSet<CacheKey>(); + for (CacheKey key: mCache.keySet()) { + if (key.componentName.getPackageName().equals(packageName) + && key.user.equals(user)) { + forDeletion.add(key); } } - for (ComponentName condemned: forDeletion) { - remove(condemned); + for (CacheKey condemned: forDeletion) { + mCache.remove(condemned); } } @@ -184,7 +219,7 @@ public class IconCache { */ public void flushInvalidIcons(DeviceProfile grid) { synchronized (mCache) { - Iterator<Entry<ComponentName, CacheEntry>> it = mCache.entrySet().iterator(); + Iterator<Entry<CacheKey, CacheEntry>> it = mCache.entrySet().iterator(); while (it.hasNext()) { final CacheEntry e = it.next().getValue(); if (e.icon.getWidth() < grid.iconSizePx || e.icon.getHeight() < grid.iconSizePx) { @@ -197,30 +232,32 @@ public class IconCache { /** * Fill in "application" with the icon and label for "info." */ - public void getTitleAndIcon(AppInfo application, ResolveInfo info, + public void getTitleAndIcon(AppInfo application, LauncherActivityInfoCompat info, HashMap<Object, CharSequence> labelCache) { synchronized (mCache) { - CacheEntry entry = cacheLocked(application.componentName, info, labelCache); + CacheEntry entry = cacheLocked(application.componentName, info, labelCache, + info.getUser()); application.title = entry.title; application.iconBitmap = entry.icon; } } - public Bitmap getIcon(Intent intent) { - return getIcon(intent, null); + public Bitmap getIcon(Intent intent, UserHandleCompat user) { + return getIcon(intent, null, user); } - public Bitmap getIcon(Intent intent, String title) { + public Bitmap getIcon(Intent intent, String title, UserHandleCompat user) { synchronized (mCache) { - final ResolveInfo resolveInfo = mPackageManager.resolveActivity(intent, 0); + final LauncherActivityInfoCompat launcherActInfo = + mLauncherApps.resolveActivity(intent, user); ComponentName component = intent.getComponent(); - if (component == null) { - return mDefaultIcon; + if (launcherActInfo == null || component == null) { + return getDefaultIcon(user); } - CacheEntry entry = cacheLocked(component, resolveInfo, null); + CacheEntry entry = cacheLocked(component, launcherActInfo, null, user); if (title != null) { entry.title = title; } @@ -228,49 +265,54 @@ public class IconCache { } } - public Bitmap getIcon(ComponentName component, ResolveInfo resolveInfo, + public Bitmap getDefaultIcon(UserHandleCompat user) { + if (!mDefaultIcons.containsKey(user)) { + mDefaultIcons.put(user, makeDefaultIcon(user)); + } + return mDefaultIcons.get(user); + } + + public Bitmap getIcon(ComponentName component, LauncherActivityInfoCompat info, HashMap<Object, CharSequence> labelCache) { synchronized (mCache) { - if (resolveInfo == null || component == null) { + if (info == null || component == null) { return null; } - CacheEntry entry = cacheLocked(component, resolveInfo, labelCache); + CacheEntry entry = cacheLocked(component, info, labelCache, info.getUser()); return entry.icon; } } - public boolean isDefaultIcon(Bitmap icon) { - return mDefaultIcon == icon; + public boolean isDefaultIcon(Bitmap icon, UserHandleCompat user) { + return mDefaultIcons.get(user) == icon; } - private CacheEntry cacheLocked(ComponentName componentName, ResolveInfo info, - HashMap<Object, CharSequence> labelCache) { - CacheEntry entry = mCache.get(componentName); + private CacheEntry cacheLocked(ComponentName componentName, LauncherActivityInfoCompat info, + HashMap<Object, CharSequence> labelCache, UserHandleCompat user) { + CacheKey cacheKey = new CacheKey(componentName, user); + CacheEntry entry = mCache.get(cacheKey); if (entry == null) { entry = new CacheEntry(); - mCache.put(componentName, entry); + mCache.put(cacheKey, entry); if (info != null) { - ComponentName key = LauncherModel.getComponentNameFromResolveInfo(info); - if (labelCache != null && labelCache.containsKey(key)) { - entry.title = labelCache.get(key).toString(); + ComponentName labelKey = info.getComponentName(); + if (labelCache != null && labelCache.containsKey(labelKey)) { + entry.title = labelCache.get(labelKey).toString(); } else { - entry.title = info.loadLabel(mPackageManager).toString(); + entry.title = info.getLabel().toString(); if (labelCache != null) { - labelCache.put(key, entry.title); + labelCache.put(labelKey, entry.title); } } - if (entry.title == null) { - entry.title = info.activityInfo.name; - } entry.icon = Utilities.createIconBitmap( - getFullResIcon(info), mContext); + info.getBadgedIcon(mIconDpi), mContext); } else { entry.title = ""; - Bitmap preloaded = getPreloadedIcon(componentName); + Bitmap preloaded = getPreloadedIcon(componentName, user); if (preloaded != null) { if (DEBUG) Log.d(TAG, "using preloaded icon for " + componentName.toShortString()); @@ -278,7 +320,7 @@ public class IconCache { } else { if (DEBUG) Log.d(TAG, "using default icon for " + componentName.toShortString()); - entry.icon = mDefaultIcon; + entry.icon = getDefaultIcon(user); } } } @@ -288,9 +330,9 @@ public class IconCache { public HashMap<ComponentName,Bitmap> getAllIcons() { synchronized (mCache) { HashMap<ComponentName,Bitmap> set = new HashMap<ComponentName,Bitmap>(); - for (ComponentName cn : mCache.keySet()) { - final CacheEntry e = mCache.get(cn); - set.put(cn, e.icon); + for (CacheKey ck : mCache.keySet()) { + final CacheEntry e = mCache.get(ck); + set.put(ck.componentName, e.icon); } return set; } @@ -353,9 +395,14 @@ public class IconCache { * @param componentName the component that should own the icon * @returns a bitmap if one is cached, or null. */ - private Bitmap getPreloadedIcon(ComponentName componentName) { + private Bitmap getPreloadedIcon(ComponentName componentName, UserHandleCompat user) { final String key = componentName.flattenToShortString(); + // We don't keep icons for other profiles in persistent cache. + if (!user.equals(UserHandleCompat.myUserHandle())) { + return null; + } + if (DEBUG) Log.v(TAG, "looking for pre-load icon for " + key); Bitmap icon = null; FileInputStream resourceFile = null; @@ -396,7 +443,11 @@ public class IconCache { * @param componentName the component that should own the icon * @returns true on success */ - public boolean deletePreloadedIcon(ComponentName componentName) { + public boolean deletePreloadedIcon(ComponentName componentName, UserHandleCompat user) { + // We don't keep icons for other profiles in persistent cache. + if (!user.equals(UserHandleCompat.myUserHandle())) { + return false; + } if (componentName == null) { return false; } diff --git a/src/com/android/launcher3/ItemInfo.java b/src/com/android/launcher3/ItemInfo.java index 12bbee780..74f16e325 100644 --- a/src/com/android/launcher3/ItemInfo.java +++ b/src/com/android/launcher3/ItemInfo.java @@ -17,10 +17,14 @@ package com.android.launcher3; import android.content.ContentValues; +import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.util.Log; +import com.android.launcher3.compat.UserHandleCompat; +import com.android.launcher3.compat.UserManagerCompat; + import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Arrays; @@ -29,6 +33,11 @@ import java.util.Arrays; * Represents an item in the launcher. */ public class ItemInfo { + + /** + * Intent extra to store the profile. Format: UserHandle + */ + static final String EXTRA_PROFILE = "profile"; static final int NO_ID = -1; @@ -103,6 +112,8 @@ public class ItemInfo { */ int[] dropPos = null; + UserHandleCompat user; + ItemInfo() { } @@ -115,6 +126,7 @@ public class ItemInfo { screenId = info.screenId; itemType = info.itemType; container = info.container; + user = info.user; // tempdebug: LauncherModel.checkItemInfo(this); } @@ -130,9 +142,11 @@ public class ItemInfo { /** * Write the fields of this item to the DB * + * @param context A context object to use for getting UserManagerCompat * @param values */ - void onAddToDatabase(ContentValues values) { + + void onAddToDatabase(Context context, ContentValues values) { values.put(LauncherSettings.BaseLauncherColumns.ITEM_TYPE, itemType); values.put(LauncherSettings.Favorites.CONTAINER, container); values.put(LauncherSettings.Favorites.SCREEN, screenId); @@ -140,6 +154,8 @@ public class ItemInfo { values.put(LauncherSettings.Favorites.CELLY, cellY); values.put(LauncherSettings.Favorites.SPANX, spanX); values.put(LauncherSettings.Favorites.SPANY, spanY); + long serialNumber = UserManagerCompat.getInstance(context).getSerialNumberForUser(user); + values.put(LauncherSettings.Favorites.PROFILE_ID, serialNumber); if (screenId == Workspace.EXTRA_EMPTY_SCREEN_ID) { // We should never persist an item on the extra empty screen. @@ -188,6 +204,7 @@ public class ItemInfo { public String toString() { return "Item(id=" + this.id + " type=" + this.itemType + " container=" + this.container + " screen=" + screenId + " cellX=" + cellX + " cellY=" + cellY + " spanX=" + spanX - + " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos) + ")"; + + " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos) + + " user=" + user + ")"; } } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index ff5b1eb3c..217219412 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -95,6 +95,10 @@ import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; +import com.android.launcher3.compat.LauncherActivityInfoCompat; +import com.android.launcher3.compat.LauncherAppsCompat; +import com.android.launcher3.compat.UserHandleCompat; +import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.DropTarget.DragObject; import com.android.launcher3.PagedView.PageSwitchListener; @@ -134,7 +138,6 @@ public class Launcher extends Activity private static final int REQUEST_CREATE_SHORTCUT = 1; private static final int REQUEST_CREATE_APPWIDGET = 5; - private static final int REQUEST_PICK_APPLICATION = 6; private static final int REQUEST_PICK_SHORTCUT = 7; private static final int REQUEST_PICK_APPWIDGET = 9; private static final int REQUEST_PICK_WALLPAPER = 10; @@ -734,10 +737,6 @@ public class Launcher extends Activity private boolean completeAdd(PendingAddArguments args) { boolean result = false; switch (args.requestCode) { - case REQUEST_PICK_APPLICATION: - completeAddApplication(args.intent, args.container, args.screenId, args.cellX, - args.cellY); - break; case REQUEST_PICK_SHORTCUT: processShortcut(args.intent); break; @@ -1368,38 +1367,6 @@ public class Launcher extends Activity } /** - * Add an application shortcut to the workspace. - * - * @param data The intent describing the application. - * @param cellInfo The position on screen where to create the shortcut. - */ - void completeAddApplication(Intent data, long container, long screenId, int cellX, int cellY) { - final int[] cellXY = mTmpAddItemCellCoordinates; - final CellLayout layout = getCellLayout(container, screenId); - - // First we check if we already know the exact location where we want to add this item. - if (cellX >= 0 && cellY >= 0) { - cellXY[0] = cellX; - cellXY[1] = cellY; - } else if (!layout.findCellForSpan(cellXY, 1, 1)) { - showOutOfSpaceMessage(isHotseatLayout(layout)); - return; - } - - final ShortcutInfo info = mModel.getShortcutInfo(getPackageManager(), data, this); - - if (info != null) { - info.setActivity(this, data.getComponent(), Intent.FLAG_ACTIVITY_NEW_TASK | - Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); - info.container = ItemInfo.NO_ID; - mWorkspace.addApplicationShortcut(info, layout, container, screenId, cellXY[0], cellXY[1], - isWorkspaceLocked(), cellX, cellY); - } else { - Log.e(TAG, "Couldn't find ActivityInfo for selected application: " + data); - } - } - - /** * Add a shortcut to the workspace. * * @param data The intent describing the shortcut. @@ -2220,21 +2187,7 @@ public class Launcher extends Activity } void processShortcut(Intent intent) { - // Handle case where user selected "Applications" - String applicationName = getResources().getString(R.string.group_applications); - String shortcutName = intent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME); - - if (applicationName != null && applicationName.equals(shortcutName)) { - Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); - mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); - - Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY); - pickIntent.putExtra(Intent.EXTRA_INTENT, mainIntent); - pickIntent.putExtra(Intent.EXTRA_TITLE, getText(R.string.title_select_application)); - Utilities.startActivityForResultSafely(this, pickIntent, REQUEST_PICK_APPLICATION); - } else { - Utilities.startActivityForResultSafely(this, intent, REQUEST_CREATE_SHORTCUT); - } + Utilities.startActivityForResultSafely(this, intent, REQUEST_CREATE_SHORTCUT); } void processWallpaper(Intent intent) { @@ -2651,18 +2604,29 @@ public class Launcher extends Activity } boolean startActivity(View v, Intent intent, Object tag) { - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); try { // Only launch using the new animation if the shortcut has not opted out (this is a // private contract between launcher and may be ignored in the future). boolean useLaunchAnimation = (v != null) && !intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION); + LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(this); + UserManagerCompat userManager = UserManagerCompat.getInstance(this); + long serialNumber = intent.getLongExtra(AppInfo.EXTRA_PROFILE, 0); + UserHandleCompat user = serialNumber == 0 ? null : + userManager.getUserForSerialNumber(serialNumber); + if (useLaunchAnimation) { ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(v, 0, 0, v.getMeasuredWidth(), v.getMeasuredHeight()); - - startActivity(intent, opts.toBundle()); + if (user == null || user.equals(UserHandleCompat.myUserHandle())) { + // Could be launching some bookkeeping activity + startActivity(intent, opts.toBundle()); + } else { + launcherApps.startActivityForProfile(intent.getComponent(), + intent.getSourceBounds(), + opts.toBundle(), user); + } } else { startActivity(intent); } @@ -4270,10 +4234,10 @@ public class Launcher extends Activity * Implementation of the method from LauncherModel.Callbacks. */ public void bindComponentsRemoved(final ArrayList<String> packageNames, - final ArrayList<AppInfo> appInfos) { + final ArrayList<AppInfo> appInfos, final UserHandleCompat user) { Runnable r = new Runnable() { public void run() { - bindComponentsRemoved(packageNames, appInfos); + bindComponentsRemoved(packageNames, appInfos, user); } }; if (waitUntilResume(r)) { @@ -4281,10 +4245,10 @@ public class Launcher extends Activity } if (!packageNames.isEmpty()) { - mWorkspace.removeItemsByPackageName(packageNames); + mWorkspace.removeItemsByPackageName(packageNames, user); } if (!appInfos.isEmpty()) { - mWorkspace.removeItemsByApplicationInfo(appInfos); + mWorkspace.removeItemsByApplicationInfo(appInfos, user); } // Notify the drag controller @@ -4555,16 +4519,27 @@ public class Launcher extends Activity } public ItemInfo createAppDragInfo(Intent appLaunchIntent) { - ResolveInfo ri = getPackageManager().resolveActivity(appLaunchIntent, 0); - if (ri == null) { + // Called from search suggestion, not supported in other profiles. + final UserHandleCompat myUser = UserHandleCompat.myUserHandle(); + LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(this); + LauncherActivityInfoCompat activityInfo = launcherApps.resolveActivity(appLaunchIntent, + myUser); + if (activityInfo == null) { return null; } - return new AppInfo(getPackageManager(), ri, mIconCache, null); + return new AppInfo(this, activityInfo, myUser, mIconCache, null); } public ItemInfo createShortcutDragInfo(Intent shortcutIntent, CharSequence caption, Bitmap icon) { - return new ShortcutInfo(shortcutIntent, caption, icon); + // Called from search suggestion, not supported in other profiles. + return createShortcutDragInfo(shortcutIntent, caption, icon, + UserHandleCompat.myUserHandle()); + } + + public ItemInfo createShortcutDragInfo(Intent shortcutIntent, CharSequence caption, + Bitmap icon, UserHandleCompat user) { + return new ShortcutInfo(shortcutIntent, caption, icon, user); } protected void moveWorkspaceToDefaultScreen() { diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index ad0101979..0725a652f 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -24,6 +24,8 @@ import android.database.ContentObserver; import android.os.Handler; import android.util.Log; +import com.android.launcher3.compat.LauncherAppsCompat; + import java.lang.ref.WeakReference; public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks { @@ -92,16 +94,11 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks { mAppFilter = AppFilter.loadByName(sContext.getString(R.string.app_filter_class)); mBuildInfo = BuildInfo.loadByName(sContext.getString(R.string.build_info_class)); mModel = new LauncherModel(this, mIconCache, mAppFilter); + final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(sContext); + launcherApps.addOnAppsChangedListener(mModel); // Register intent receivers - IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); - filter.addAction(Intent.ACTION_PACKAGE_REMOVED); - filter.addAction(Intent.ACTION_PACKAGE_CHANGED); - filter.addDataScheme("package"); - sContext.registerReceiver(mModel, filter); - filter = new IntentFilter(); - filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); - filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); + IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_LOCALE_CHANGED); filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); sContext.registerReceiver(mModel, filter); diff --git a/src/com/android/launcher3/LauncherAppWidgetInfo.java b/src/com/android/launcher3/LauncherAppWidgetInfo.java index 950828e09..bec1f9eb3 100644 --- a/src/com/android/launcher3/LauncherAppWidgetInfo.java +++ b/src/com/android/launcher3/LauncherAppWidgetInfo.java @@ -19,6 +19,9 @@ package com.android.launcher3; import android.appwidget.AppWidgetHostView; import android.content.ComponentName; import android.content.ContentValues; +import android.content.Context; + +import com.android.launcher3.compat.UserHandleCompat; /** * Represents a widget (either instantiated or about to be) in the Launcher. @@ -59,11 +62,13 @@ public class LauncherAppWidgetInfo extends ItemInfo { // to indicate that they should be calculated based on the layout and minWidth/minHeight spanX = -1; spanY = -1; + // We only support app widgets on current user. + user = UserHandleCompat.myUserHandle(); } @Override - void onAddToDatabase(ContentValues values) { - super.onAddToDatabase(values); + void onAddToDatabase(Context context, ContentValues values) { + super.onAddToDatabase(context, values); values.put(LauncherSettings.Favorites.APPWIDGET_ID, appWidgetId); values.put(LauncherSettings.Favorites.APPWIDGET_PROVIDER, providerName.flattenToString()); } diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java index cab55c79e..275fccc16 100644 --- a/src/com/android/launcher3/LauncherBackupHelper.java +++ b/src/com/android/launcher3/LauncherBackupHelper.java @@ -28,6 +28,8 @@ import com.android.launcher3.backup.BackupProtos.Key; import com.android.launcher3.backup.BackupProtos.Resource; import com.android.launcher3.backup.BackupProtos.Screen; import com.android.launcher3.backup.BackupProtos.Widget; +import com.android.launcher3.compat.UserManagerCompat; +import com.android.launcher3.compat.UserHandleCompat; import android.app.backup.BackupDataInputStream; import android.app.backup.BackupDataOutput; @@ -459,10 +461,15 @@ public class LauncherBackupHelper implements BackupHelper { Set<String> savedIds = getSavedIdsByType(Key.ICON, in); if (DEBUG) Log.d(TAG, "icon savedIds.size()=" + savedIds.size()); + // Don't backup apps in other profiles for now. + UserHandleCompat myUserHandle = UserHandleCompat.myUserHandle(); + long userSerialNumber = + UserManagerCompat.getInstance(mContext).getSerialNumberForUser(myUserHandle); int startRows = out.rows; if (DEBUG) Log.d(TAG, "starting here: " + startRows); - String where = Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_APPLICATION + " OR " + - Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_SHORTCUT; + String where = "(" + Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_APPLICATION + " OR " + + Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_SHORTCUT + ") AND" + + Favorites.PROFILE_ID + "=" + userSerialNumber; Cursor cursor = cr.query(Favorites.CONTENT_URI, FAVORITE_PROJECTION, where, null, null); Set<String> currentIds = new HashSet<String>(cursor.getCount()); @@ -492,9 +499,9 @@ public class LauncherBackupHelper implements BackupHelper { if (DEBUG) Log.d(TAG, "I can count this high: " + out.rows); if ((out.rows - startRows) < MAX_ICONS_PER_PASS) { if (VERBOSE) Log.v(TAG, "saving icon " + backupKey); - Bitmap icon = mIconCache.getIcon(intent); + Bitmap icon = mIconCache.getIcon(intent, myUserHandle); keys.add(key); - if (icon != null && !mIconCache.isDefaultIcon(icon)) { + if (icon != null && !mIconCache.isDefaultIcon(icon, myUserHandle)) { byte[] blob = packIcon(dpi, icon); writeRowToBackup(key, blob, out, data); } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 044ddbb44..15db057ec 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -44,6 +44,10 @@ import android.text.TextUtils; import android.util.Log; import android.util.Pair; +import com.android.launcher3.compat.LauncherActivityInfoCompat; +import com.android.launcher3.compat.LauncherAppsCompat; +import com.android.launcher3.compat.UserHandleCompat; +import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.InstallWidgetReceiver.WidgetMimeTypeHandlerData; import java.lang.ref.WeakReference; @@ -67,7 +71,8 @@ import java.util.concurrent.atomic.AtomicBoolean; * LauncherModel object held in a static. Also provide APIs for updating the database state * for the Launcher. */ -public class LauncherModel extends BroadcastReceiver { +public class LauncherModel extends BroadcastReceiver + implements LauncherAppsCompat.OnAppsChangedListenerCompat { static final boolean DEBUG_LOADERS = false; private static final boolean DEBUG_RECEIVER = true; // STOPSHIP(cwren) temporary for debugging @@ -153,10 +158,12 @@ public class LauncherModel extends BroadcastReceiver { // </ only access in worker thread > private IconCache mIconCache; - private Bitmap mDefaultIcon; protected int mPreviousConfigMcc; + private final LauncherAppsCompat mLauncherApps; + private final UserManagerCompat mUserManager; + public interface Callbacks { public boolean setLoadOnResume(); public int getCurrentWorkspaceScreen(); @@ -176,7 +183,7 @@ public class LauncherModel extends BroadcastReceiver { public void bindAppsUpdated(ArrayList<AppInfo> apps); public void updatePackageState(String pkgName, int state); public void bindComponentsRemoved(ArrayList<String> packageNames, - ArrayList<AppInfo> appInfos); + ArrayList<AppInfo> appInfos, UserHandleCompat user); public void bindPackagesUpdated(ArrayList<Object> widgetsAndShortcuts); public void bindSearchablesChanged(); public boolean isAllAppsButtonRank(int rank); @@ -206,6 +213,8 @@ public class LauncherModel extends BroadcastReceiver { final Resources res = context.getResources(); Configuration config = res.getConfiguration(); mPreviousConfigMcc = config.mcc; + mLauncherApps = LauncherAppsCompat.getInstance(context); + mUserManager = UserManagerCompat.getInstance(context); } /** Runs the specified runnable immediately if called from the main thread, otherwise it is @@ -325,7 +334,7 @@ public class LauncherModel extends BroadcastReceiver { Iterator<AppInfo> iter = allAppsApps.iterator(); while (iter.hasNext()) { ItemInfo a = iter.next(); - if (LauncherModel.appWasRestored(ctx, a.getIntent())) { + if (LauncherModel.appWasRestored(ctx, a.getIntent(), a.user)) { restoredAppsFinal.add((AppInfo) a); } } @@ -341,7 +350,8 @@ public class LauncherModel extends BroadcastReceiver { for (AppInfo info : restoredAppsFinal) { final Intent intent = info.getIntent(); if (intent != null) { - mIconCache.deletePreloadedIcon(intent.getComponent()); + mIconCache.deletePreloadedIcon(intent.getComponent(), + info.user); } } callbacks.bindAppsUpdated(restoredAppsFinal); @@ -393,7 +403,7 @@ public class LauncherModel extends BroadcastReceiver { if (LauncherModel.shortcutExists(context, name, launchIntent)) { // Only InstallShortcutReceiver sends us shortcutInfos, ignore them if (a instanceof AppInfo && - LauncherModel.appWasRestored(context, launchIntent)) { + LauncherModel.appWasRestored(context, launchIntent, a.user)) { restoredAppsFinal.add((AppInfo) a); } continue; @@ -483,15 +493,6 @@ public class LauncherModel extends BroadcastReceiver { runOnWorkerThread(r); } - public Bitmap getFallbackIcon() { - if (mDefaultIcon == null) { - final Context context = LauncherAppState.getInstance().getContext(); - mDefaultIcon = Utilities.createIconBitmap( - mIconCache.getFullResDefaultActivityIcon(), context); - } - return Bitmap.createBitmap(mDefaultIcon); - } - public void unbindItemInfosAndClearQueuedBindRunnables() { if (sWorkerThread.getThreadId() == Process.myTid()) { throw new RuntimeException("Expected unbindLauncherItemInfos() to be called from the " + @@ -817,7 +818,7 @@ public class LauncherModel extends BroadcastReceiver { */ static void updateItemInDatabase(Context context, final ItemInfo item) { final ContentValues values = new ContentValues(); - item.onAddToDatabase(values); + item.onAddToDatabase(context, values); item.updateValuesWithCoordinates(values, item.cellX, item.cellY); updateItemInDatabaseHelper(context, values, item, "updateItemInDatabase"); } @@ -842,18 +843,22 @@ public class LauncherModel extends BroadcastReceiver { /** * Returns true if the shortcuts already exists in the database. - * we identify a shortcut by the component name of the intent. + * we identify a shortcut by the component name of the intent + * and the user. */ - static boolean appWasRestored(Context context, Intent intent) { + static boolean appWasRestored(Context context, Intent intent, UserHandleCompat user) { final ContentResolver cr = context.getContentResolver(); final ComponentName component = intent.getComponent(); if (component == null) { return false; } String componentName = component.flattenToString(); - final String where = "intent glob \"*component=" + componentName + "*\" and restored = 1"; + long serialNumber = UserManagerCompat.getInstance(context) + .getSerialNumberForUser(user); + final String where = "intent glob \"*component=" + componentName + "*\" and restored = 1" + + " and profileId = " + serialNumber; Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, - new String[]{"intent", "restored"}, where, null, null); + new String[]{"intent", "restored", "profileId"}, where, null, null); boolean result = false; try { result = c.moveToFirst(); @@ -873,8 +878,10 @@ public class LauncherModel extends BroadcastReceiver { final ContentResolver cr = context.getContentResolver(); Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, new String[] { LauncherSettings.Favorites.ITEM_TYPE, LauncherSettings.Favorites.CONTAINER, - LauncherSettings.Favorites.SCREEN, LauncherSettings.Favorites.CELLX, LauncherSettings.Favorites.CELLY, - LauncherSettings.Favorites.SPANX, LauncherSettings.Favorites.SPANY }, null, null, null); + LauncherSettings.Favorites.SCREEN, + LauncherSettings.Favorites.CELLX, LauncherSettings.Favorites.CELLY, + LauncherSettings.Favorites.SPANX, LauncherSettings.Favorites.SPANY, + LauncherSettings.Favorites.PROFILE_ID }, null, null, null); final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE); final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER); @@ -883,7 +890,8 @@ public class LauncherModel extends BroadcastReceiver { final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY); final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX); final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY); - + final int profileIdIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.PROFILE_ID); + UserManagerCompat userManager = UserManagerCompat.getInstance(context); try { while (c.moveToNext()) { ItemInfo item = new ItemInfo(); @@ -894,8 +902,12 @@ public class LauncherModel extends BroadcastReceiver { item.container = c.getInt(containerIndex); item.itemType = c.getInt(itemTypeIndex); item.screenId = c.getInt(screenIndex); - - items.add(item); + int serialNumber = c.getInt(profileIdIndex); + item.user = userManager.getUserForSerialNumber(serialNumber); + // Skip if user has been deleted. + if (item.user != null) { + items.add(item); + } } } catch (Exception e) { items.clear(); @@ -968,7 +980,7 @@ public class LauncherModel extends BroadcastReceiver { final ContentValues values = new ContentValues(); final ContentResolver cr = context.getContentResolver(); - item.onAddToDatabase(values); + item.onAddToDatabase(context, values); item.id = LauncherAppState.getLauncherProvider().generateNewItemId(); values.put(LauncherSettings.Favorites._ID, item.id); @@ -1158,6 +1170,57 @@ public class LauncherModel extends BroadcastReceiver { } } + @Override + public void onPackageChanged(UserHandleCompat user, String packageName) { + int op = PackageUpdatedTask.OP_UPDATE; + enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName }, + user)); + } + + @Override + public void onPackageRemoved(UserHandleCompat user, String packageName) { + int op = PackageUpdatedTask.OP_REMOVE; + enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName }, + user)); + } + + @Override + public void onPackageAdded(UserHandleCompat user, String packageName) { + int op = PackageUpdatedTask.OP_ADD; + enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName }, + user)); + } + + @Override + public void onPackagesAvailable(UserHandleCompat user, String[] packageNames, + boolean replacing) { + if (!replacing) { + enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_ADD, packageNames, + user)); + if (mAppsCanBeOnRemoveableStorage) { + // Only rebind if we support removable storage. It catches the + // case where + // apps on the external sd card need to be reloaded + startLoaderFromBackground(); + } + } else { + // If we are replacing then just update the packages in the list + enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_UPDATE, + packageNames, user)); + } + } + + @Override + public void onPackagesUnavailable(UserHandleCompat user, String[] packageNames, + boolean replacing) { + if (!replacing) { + enqueuePackageUpdated(new PackageUpdatedTask( + PackageUpdatedTask.OP_UNAVAILABLE, packageNames, + user)); + } + + } + /** * Call from the handler for ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and * ACTION_PACKAGE_CHANGED. @@ -1167,65 +1230,7 @@ public class LauncherModel extends BroadcastReceiver { if (DEBUG_RECEIVER) Log.d(TAG, "onReceive intent=" + intent); final String action = intent.getAction(); - - if (Intent.ACTION_PACKAGE_CHANGED.equals(action) - || Intent.ACTION_PACKAGE_REMOVED.equals(action) - || Intent.ACTION_PACKAGE_ADDED.equals(action)) { - final String packageName = intent.getData().getSchemeSpecificPart(); - final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); - - int op = PackageUpdatedTask.OP_NONE; - - if (packageName == null || packageName.length() == 0) { - // they sent us a bad intent - return; - } - - if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) { - op = PackageUpdatedTask.OP_UPDATE; - } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) { - if (!replacing) { - op = PackageUpdatedTask.OP_REMOVE; - } - // else, we are replacing the package, so a PACKAGE_ADDED will be sent - // later, we will update the package at this time - } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) { - if (!replacing) { - op = PackageUpdatedTask.OP_ADD; - } else { - op = PackageUpdatedTask.OP_UPDATE; - } - } - - if (op != PackageUpdatedTask.OP_NONE) { - enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName })); - } - - } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) { - final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); - String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); - if (!replacing) { - enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_ADD, packages)); - if (mAppsCanBeOnRemoveableStorage) { - // Only rebind if we support removable storage. It catches the case where - // apps on the external sd card need to be reloaded - startLoaderFromBackground(); - } - } else { - // If we are replacing then just update the packages in the list - enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_UPDATE, - packages)); - } - } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) { - final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); - if (!replacing) { - String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); - enqueuePackageUpdated(new PackageUpdatedTask( - PackageUpdatedTask.OP_UNAVAILABLE, packages)); - } - // else, we are replacing the packages, so ignore this event and wait for - // EXTERNAL_APPLICATIONS_AVAILABLE to update the packages at that time - } else if (Intent.ACTION_LOCALE_CHANGED.equals(action)) { + 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 (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) { @@ -1659,7 +1664,7 @@ public class LauncherModel extends BroadcastReceiver { ArrayList<ItemInfo> added = new ArrayList<ItemInfo>(); synchronized (sBgLock) { for (AppInfo app : mBgAllAppsList.data) { - tmpInfos = getItemInfoForComponentName(app.componentName); + tmpInfos = getItemInfoForComponentName(app.componentName, app.user); if (tmpInfos.isEmpty()) { // We are missing an application icon, so add this to the workspace added.add(app); @@ -1864,6 +1869,8 @@ public class LauncherModel extends BroadcastReceiver { LauncherSettings.Favorites.SPANY); final int restoredIndex = c.getColumnIndexOrThrow( LauncherSettings.Favorites.RESTORED); + final int profileIdIndex = c.getColumnIndexOrThrow( + LauncherSettings.Favorites.PROFILE_ID); //final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI); //final int displayModeIndex = c.getColumnIndexOrThrow( // LauncherSettings.Favorites.DISPLAY_MODE); @@ -1874,6 +1881,7 @@ public class LauncherModel extends BroadcastReceiver { int container; long id; Intent intent; + UserHandleCompat user; while (!mStopped && c.moveToNext()) { AtomicBoolean deleteOnInvalidPlacement = new AtomicBoolean(false); @@ -1886,10 +1894,17 @@ public class LauncherModel extends BroadcastReceiver { case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: id = c.getLong(idIndex); intentDescription = c.getString(intentIndex); + int serialNumber = c.getInt(profileIdIndex); + user = mUserManager.getUserForSerialNumber(serialNumber); + if (user == null) { + // User has been deleted remove the item. + itemsToRemove.add(id); + continue; + } try { intent = Intent.parseUri(intentDescription, 0); ComponentName cn = intent.getComponent(); - if (cn != null && !isValidPackageComponent(manager, cn)) { + if (cn != null && !isValidPackageActivity(context, cn, user)) { if (restored) { // might be installed later Launcher.addDumpLog(TAG, @@ -1921,14 +1936,20 @@ public class LauncherModel extends BroadcastReceiver { } if (restored) { - Launcher.addDumpLog(TAG, - "constructing info for partially restored package", - true); - info = getRestoredItemInfo(c, titleIndex, intent); - intent = getRestoredItemIntent(c, context, intent); + if (user.equals(UserHandleCompat.myUserHandle())) { + Launcher.addDumpLog(TAG, + "constructing info for partially restored package", + true); + info = getRestoredItemInfo(c, titleIndex, intent); + intent = getRestoredItemIntent(c, context, intent); + } else { + // Don't restore items for other profiles. + itemsToRemove.add(id); + continue; + } } else if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { - info = getShortcutInfo(manager, intent, context, c, iconIndex, + info = getShortcutInfo(manager, intent, user, context, c, iconIndex, titleIndex, mLabelCache); } else { info = getShortcutInfo(c, context, iconTypeIndex, @@ -1958,6 +1979,7 @@ public class LauncherModel extends BroadcastReceiver { info.cellY = c.getInt(cellYIndex); info.spanX = 1; info.spanY = 1; + info.intent.putExtra(ItemInfo.EXTRA_PROFILE, serialNumber); // check & update map of what's occupied deleteOnInvalidPlacement.set(false); @@ -2612,42 +2634,42 @@ public class LauncherModel extends BroadcastReceiver { return; } - final PackageManager packageManager = mContext.getPackageManager(); final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); + final List<UserHandleCompat> profiles = mUserManager.getUserProfiles(); + // Clear the list of apps mBgAllAppsList.clear(); + for (UserHandleCompat user : profiles) { + // Query for the set of apps + final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; + List<LauncherActivityInfoCompat> apps = mLauncherApps.getActivityList(null, user); + if (DEBUG_LOADERS) { + Log.d(TAG, "getActivityList took " + + (SystemClock.uptimeMillis()-qiaTime) + "ms for user " + user); + Log.d(TAG, "getActivityList got " + apps.size() + " apps for user " + user); + } + // Fail if we don't have any apps + if (apps == null || apps.isEmpty()) { + return; + } + // Sort the applications by name + final long sortTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; + Collections.sort(apps, + new LauncherModel.ShortcutNameComparator(mLabelCache)); + if (DEBUG_LOADERS) { + Log.d(TAG, "sort took " + + (SystemClock.uptimeMillis()-sortTime) + "ms"); + } - // Query for the set of apps - final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; - List<ResolveInfo> apps = packageManager.queryIntentActivities(mainIntent, 0); - if (DEBUG_LOADERS) { - Log.d(TAG, "queryIntentActivities took " - + (SystemClock.uptimeMillis()-qiaTime) + "ms"); - Log.d(TAG, "queryIntentActivities got " + apps.size() + " apps"); - } - // Fail if we don't have any apps - if (apps == null || apps.isEmpty()) { - return; - } - // Sort the applications by name - final long sortTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; - Collections.sort(apps, - new LauncherModel.ShortcutNameComparator(packageManager, mLabelCache)); - if (DEBUG_LOADERS) { - Log.d(TAG, "sort took " - + (SystemClock.uptimeMillis()-sortTime) + "ms"); - } - - // Create the ApplicationInfos - for (int i = 0; i < apps.size(); i++) { - ResolveInfo app = apps.get(i); - // This builds the icon bitmaps. - mBgAllAppsList.add(new AppInfo(packageManager, app, - mIconCache, mLabelCache)); + // Create the ApplicationInfos + for (int i = 0; i < apps.size(); i++) { + LauncherActivityInfoCompat app = apps.get(i); + // This builds the icon bitmaps. + mBgAllAppsList.add(new AppInfo(mContext, app, user, mIconCache, mLabelCache)); + } } - // Huh? Shouldn't this be inside the Runnable below? final ArrayList<AppInfo> added = mBgAllAppsList.added; mBgAllAppsList.added = new ArrayList<AppInfo>(); @@ -2693,6 +2715,7 @@ public class LauncherModel extends BroadcastReceiver { private class PackageUpdatedTask implements Runnable { int mOp; String[] mPackages; + UserHandleCompat mUser; public static final int OP_NONE = 0; public static final int OP_ADD = 1; @@ -2701,9 +2724,10 @@ public class LauncherModel extends BroadcastReceiver { public static final int OP_UNAVAILABLE = 4; // external media unmounted - public PackageUpdatedTask(int op, String[] packages) { + public PackageUpdatedTask(int op, String[] packages, UserHandleCompat user) { mOp = op; mPackages = packages; + mUser = user; } public void run() { @@ -2715,14 +2739,14 @@ public class LauncherModel extends BroadcastReceiver { case OP_ADD: for (int i=0; i<N; i++) { if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.addPackage " + packages[i]); - mIconCache.remove(packages[i]); - mBgAllAppsList.addPackage(context, packages[i]); + mIconCache.remove(packages[i], mUser); + mBgAllAppsList.addPackage(context, packages[i], mUser); } break; case OP_UPDATE: for (int i=0; i<N; i++) { if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.updatePackage " + packages[i]); - mBgAllAppsList.updatePackage(context, packages[i]); + mBgAllAppsList.updatePackage(context, packages[i], mUser); WidgetPreviewLoader.removePackageFromDb( mApp.getWidgetPreviewCacheDb(), packages[i]); } @@ -2731,7 +2755,7 @@ public class LauncherModel extends BroadcastReceiver { case OP_UNAVAILABLE: for (int i=0; i<N; i++) { if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.removePackage " + packages[i]); - mBgAllAppsList.removePackage(packages[i]); + mBgAllAppsList.removePackage(packages[i], mUser); WidgetPreviewLoader.removePackageFromDb( mApp.getWidgetPreviewCacheDb(), packages[i]); } @@ -2777,7 +2801,7 @@ public class LauncherModel extends BroadcastReceiver { // Update the launcher db to reflect the changes for (AppInfo a : modifiedFinal) { ArrayList<ItemInfo> infos = - getItemInfoForComponentName(a.componentName); + getItemInfoForComponentName(a.componentName, mUser); for (ItemInfo i : infos) { if (isShortcutInfoUpdateable(i)) { ShortcutInfo info = (ShortcutInfo) i; @@ -2806,21 +2830,21 @@ public class LauncherModel extends BroadcastReceiver { // Mark disabled packages in the broadcast to be removed final PackageManager pm = context.getPackageManager(); for (int i=0; i<N; i++) { - if (isPackageDisabled(pm, packages[i])) { + if (isPackageDisabled(context, packages[i], mUser)) { removedPackageNames.add(packages[i]); } } } // Remove all the components associated with this package for (String pn : removedPackageNames) { - ArrayList<ItemInfo> infos = getItemInfoForPackageName(pn); + ArrayList<ItemInfo> infos = getItemInfoForPackageName(pn, mUser); for (ItemInfo i : infos) { deleteItemFromDatabase(context, i); } } // Remove all the specific components for (AppInfo a : removedApps) { - ArrayList<ItemInfo> infos = getItemInfoForComponentName(a.componentName); + ArrayList<ItemInfo> infos = getItemInfoForComponentName(a.componentName, mUser); for (ItemInfo i : infos) { deleteItemFromDatabase(context, i); } @@ -2836,14 +2860,14 @@ public class LauncherModel extends BroadcastReceiver { public void run() { Callbacks cb = mCallbacks != null ? mCallbacks.get() : null; if (callbacks == cb && cb != null) { - callbacks.bindComponentsRemoved(removedPackageNames, removedApps); + callbacks.bindComponentsRemoved(removedPackageNames, removedApps, mUser); } } }); } final ArrayList<Object> widgetsAndShortcuts = - getSortedWidgetsAndShortcuts(context); + getSortedWidgetsAndShortcuts(context); mHandler.post(new Runnable() { @Override public void run() { @@ -2878,31 +2902,22 @@ public class LauncherModel extends BroadcastReceiver { return widgetsAndShortcuts; } - private static boolean isPackageDisabled(PackageManager pm, String packageName) { - try { - PackageInfo pi = pm.getPackageInfo(packageName, 0); - return !pi.applicationInfo.enabled; - } catch (NameNotFoundException e) { - // Fall through - } - return false; + private static boolean isPackageDisabled(Context context, String packageName, + UserHandleCompat user) { + final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context); + return !launcherApps.isPackageEnabledForProfile(packageName, user); } - public static boolean isValidPackageComponent(PackageManager pm, ComponentName cn) { + public static boolean isValidPackageActivity(Context context, ComponentName cn, + UserHandleCompat user) { if (cn == null) { return false; } - if (isPackageDisabled(pm, cn.getPackageName())) { - return false; - } - - try { - // Check the activity - PackageInfo pi = pm.getPackageInfo(cn.getPackageName(), 0); - return (pm.getActivityInfo(cn, 0) != null); - } catch (NameNotFoundException e) { + final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context); + if (!launcherApps.isPackageEnabledForProfile(cn.getPackageName(), user)) { return false; } + return launcherApps.isActivityEnabledForProfile(cn, user); } /** @@ -2916,7 +2931,8 @@ public class LauncherModel extends BroadcastReceiver { } else { info.title = ""; } - info.setIcon(mIconCache.getIcon(intent, info.title.toString())); + info.user = UserHandleCompat.myUserHandle(); + info.setIcon(mIconCache.getIcon(intent, info.title.toString(), info.user)); info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; info.restoredIntent = intent; return info; @@ -2944,8 +2960,9 @@ public class LauncherModel extends BroadcastReceiver { * This is called from the code that adds shortcuts from the intent receiver. This * doesn't have a Cursor, but */ - public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, Context context) { - return getShortcutInfo(manager, intent, context, null, -1, -1, null); + public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, + UserHandleCompat user, Context context) { + return getShortcutInfo(manager, intent, user, context, null, -1, -1, null); } /** @@ -2953,54 +2970,37 @@ 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 getShortcutInfo(PackageManager manager, Intent intent, Context context, - Cursor c, int iconIndex, int titleIndex, HashMap<Object, CharSequence> labelCache) { + public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, + UserHandleCompat user, Context context, Cursor c, int iconIndex, int titleIndex, + HashMap<Object, CharSequence> labelCache) { + if (user == null) { + Log.d(TAG, "Null user found in getShortcutInfo"); + return null; + } + ComponentName componentName = intent.getComponent(); - final ShortcutInfo info = new ShortcutInfo(); - if (componentName != null && !isValidPackageComponent(manager, componentName)) { - Log.d(TAG, "Invalid package found in getShortcutInfo: " + componentName); + if (componentName == null) { + Log.d(TAG, "Missing component found in getShortcutInfo: " + componentName); return null; - } else { - try { - PackageInfo pi = manager.getPackageInfo(componentName.getPackageName(), 0); - info.initFlagsAndFirstInstallTime(pi); - } catch (NameNotFoundException e) { - Log.d(TAG, "getPackInfo failed for package " + - componentName.getPackageName()); - } } - // TODO: See if the PackageManager knows about this case. If it doesn't - // then return null & delete this. + Intent newIntent = new Intent(intent.getAction(), null); + newIntent.addCategory(Intent.CATEGORY_LAUNCHER); + newIntent.setComponent(componentName); + LauncherActivityInfoCompat lai = mLauncherApps.resolveActivity(newIntent, user); + if (lai == null) { + Log.d(TAG, "Missing activity found in getShortcutInfo: " + componentName); + return null; + } + + final ShortcutInfo info = new ShortcutInfo(); // the resource -- This may implicitly give us back the fallback icon, // but don't worry about that. All we're doing with usingFallbackIcon is // to avoid saving lots of copies of that in the database, and most apps // have icons anyway. + Bitmap icon = mIconCache.getIcon(componentName, lai, labelCache); - // Attempt to use queryIntentActivities to get the ResolveInfo (with IntentFilter info) and - // if that fails, or is ambiguious, fallback to the standard way of getting the resolve info - // via resolveActivity(). - Bitmap icon = null; - ResolveInfo resolveInfo = null; - ComponentName oldComponent = intent.getComponent(); - Intent newIntent = new Intent(intent.getAction(), null); - newIntent.addCategory(Intent.CATEGORY_LAUNCHER); - newIntent.setPackage(oldComponent.getPackageName()); - List<ResolveInfo> infos = manager.queryIntentActivities(newIntent, 0); - for (ResolveInfo i : infos) { - ComponentName cn = new ComponentName(i.activityInfo.packageName, - i.activityInfo.name); - if (cn.equals(oldComponent)) { - resolveInfo = i; - } - } - if (resolveInfo == null) { - resolveInfo = manager.resolveActivity(intent, 0); - } - if (resolveInfo != null) { - icon = mIconCache.getIcon(componentName, resolveInfo, labelCache); - } // the db if (icon == null) { if (c != null) { @@ -3009,21 +3009,21 @@ public class LauncherModel extends BroadcastReceiver { } // the fallback icon if (icon == null) { - icon = getFallbackIcon(); + icon = mIconCache.getDefaultIcon(user); info.usingFallbackIcon = true; } info.setIcon(icon); + // From the cache. + if (labelCache != null) { + info.title = labelCache.get(componentName); + } + // from the resource - if (resolveInfo != null) { - ComponentName key = LauncherModel.getComponentNameFromResolveInfo(resolveInfo); - if (labelCache != null && labelCache.containsKey(key)) { - info.title = labelCache.get(key); - } else { - info.title = resolveInfo.activityInfo.loadLabel(manager); - if (labelCache != null) { - labelCache.put(key, info.title); - } + if (info.title == null && lai != null) { + info.title = lai.getLabel(); + if (labelCache != null) { + labelCache.put(componentName, info.title); } } // from the db @@ -3037,6 +3037,7 @@ public class LauncherModel extends BroadcastReceiver { info.title = componentName.getClassName(); } info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; + info.user = user; return info; } @@ -3069,21 +3070,27 @@ public class LauncherModel extends BroadcastReceiver { return new ArrayList<ItemInfo>(filtered); } - private ArrayList<ItemInfo> getItemInfoForPackageName(final String pn) { + private ArrayList<ItemInfo> getItemInfoForPackageName(final String pn, + final UserHandleCompat user) { ItemInfoFilter filter = new ItemInfoFilter() { @Override public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) { - return cn.getPackageName().equals(pn); + return cn.getPackageName().equals(pn) && info.user.equals(user); } }; return filterItemInfos(sBgItemsIdMap.values(), filter); } - private ArrayList<ItemInfo> getItemInfoForComponentName(final ComponentName cname) { + private ArrayList<ItemInfo> getItemInfoForComponentName(final ComponentName cname, + final UserHandleCompat user) { ItemInfoFilter filter = new ItemInfoFilter() { @Override public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) { - return cn.equals(cname); + if (info.user == null) { + return cn.equals(cname); + } else { + return cn.equals(cname) && info.user.equals(user); + } } }; return filterItemInfos(sBgItemsIdMap.values(), filter); @@ -3118,6 +3125,8 @@ public class LauncherModel extends BroadcastReceiver { Bitmap icon = null; final ShortcutInfo info = new ShortcutInfo(); + // Non-app shortcuts are only supported for current user. + info.user = UserHandleCompat.myUserHandle(); info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; // TODO: If there's an explicit component and we can't install that, delete it. @@ -3148,14 +3157,14 @@ public class LauncherModel extends BroadcastReceiver { } // the fallback icon if (icon == null) { - icon = getFallbackIcon(); + icon = mIconCache.getDefaultIcon(info.user); info.usingFallbackIcon = true; } break; case LauncherSettings.Favorites.ICON_TYPE_BITMAP: icon = getIconFromCursor(c, iconIndex, context); if (icon == null) { - icon = getFallbackIcon(); + icon = mIconCache.getDefaultIcon(info.user); info.customIcon = false; info.usingFallbackIcon = true; } else { @@ -3163,7 +3172,7 @@ public class LauncherModel extends BroadcastReceiver { } break; default: - icon = getFallbackIcon(); + icon = mIconCache.getDefaultIcon(info.user); info.usingFallbackIcon = true; info.customIcon = false; break; @@ -3280,7 +3289,8 @@ public class LauncherModel extends BroadcastReceiver { iconResource.packageName); final int id = resources.getIdentifier(iconResource.resourceName, null, null); icon = Utilities.createIconBitmap( - mIconCache.getFullResIcon(resources, id), context); + mIconCache.getFullResIcon(resources, id), + context); } catch (Exception e) { Log.w(TAG, "Could not load shortcut icon: " + extra); } @@ -3289,11 +3299,14 @@ public class LauncherModel extends BroadcastReceiver { final ShortcutInfo info = new ShortcutInfo(); + // Only support intents for current user for now. Intents sent from other + // users wouldn't get here without intent forwarding anyway. + info.user = UserHandleCompat.myUserHandle(); if (icon == null) { if (fallbackIcon != null) { icon = fallbackIcon; } else { - icon = getFallbackIcon(); + icon = mIconCache.getDefaultIcon(info.user); info.usingFallbackIcon = true; } } @@ -3365,12 +3378,18 @@ public class LauncherModel extends BroadcastReceiver { final Collator collator = Collator.getInstance(); return new Comparator<AppInfo>() { public final int compare(AppInfo a, AppInfo b) { - int result = collator.compare(a.title.toString().trim(), - b.title.toString().trim()); - if (result == 0) { - result = a.componentName.compareTo(b.componentName); + if (a.user.equals(b.user)) { + int result = collator.compare(a.title.toString().trim(), + b.title.toString().trim()); + if (result == 0) { + result = a.componentName.compareTo(b.componentName); + } + return result; + } else { + // TODO Need to figure out rules for sorting + // profiles, this puts work second. + return a.user.toString().compareTo(b.user.toString()); } - return result; } }; } @@ -3397,35 +3416,32 @@ public class LauncherModel extends BroadcastReceiver { return new ComponentName(info.serviceInfo.packageName, info.serviceInfo.name); } } - public static class ShortcutNameComparator implements Comparator<ResolveInfo> { + public static class ShortcutNameComparator implements Comparator<LauncherActivityInfoCompat> { private Collator mCollator; - private PackageManager mPackageManager; private HashMap<Object, CharSequence> mLabelCache; ShortcutNameComparator(PackageManager pm) { - mPackageManager = pm; mLabelCache = new HashMap<Object, CharSequence>(); mCollator = Collator.getInstance(); } - ShortcutNameComparator(PackageManager pm, HashMap<Object, CharSequence> labelCache) { - mPackageManager = pm; + ShortcutNameComparator(HashMap<Object, CharSequence> labelCache) { mLabelCache = labelCache; mCollator = Collator.getInstance(); } - public final int compare(ResolveInfo a, ResolveInfo b) { + public final int compare(LauncherActivityInfoCompat a, LauncherActivityInfoCompat b) { CharSequence labelA, labelB; - ComponentName keyA = LauncherModel.getComponentNameFromResolveInfo(a); - ComponentName keyB = LauncherModel.getComponentNameFromResolveInfo(b); + ComponentName keyA = a.getComponentName(); + ComponentName keyB = b.getComponentName(); if (mLabelCache.containsKey(keyA)) { labelA = mLabelCache.get(keyA); } else { - labelA = a.loadLabel(mPackageManager).toString().trim(); + labelA = a.getLabel().toString().trim(); mLabelCache.put(keyA, labelA); } if (mLabelCache.containsKey(keyB)) { labelB = mLabelCache.get(keyB); } else { - labelB = b.loadLabel(mPackageManager).toString().trim(); + labelB = b.getLabel().toString().trim(); mLabelCache.put(keyB, labelB); } diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index e43b727aa..437c2ad96 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -56,8 +56,10 @@ import android.util.Log; import android.util.SparseArray; import android.util.Xml; -import com.android.launcher3.LauncherSettings.Favorites; +import com.android.launcher3.compat.UserHandleCompat; +import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.config.ProviderConfig; +import com.android.launcher3.LauncherSettings.Favorites; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -75,7 +77,7 @@ public class LauncherProvider extends ContentProvider { private static final String DATABASE_NAME = "launcher.db"; - private static final int DATABASE_VERSION = 19; + private static final int DATABASE_VERSION = 20; static final String OLD_AUTHORITY = "com.android.launcher2.settings"; static final String AUTHORITY = ProviderConfig.AUTHORITY; @@ -429,6 +431,10 @@ public class LauncherProvider extends ContentProvider { mMaxScreenId = 0; mNewDbCreated = true; + UserManagerCompat userManager = UserManagerCompat.getInstance(mContext); + long userSerialNumber = userManager.getSerialNumberForUser( + UserHandleCompat.myUserHandle()); + db.execSQL("CREATE TABLE favorites (" + "_id INTEGER PRIMARY KEY," + "title TEXT," + @@ -450,7 +456,8 @@ public class LauncherProvider extends ContentProvider { "displayMode INTEGER," + "appWidgetProvider TEXT," + "modified INTEGER NOT NULL DEFAULT 0," + - "restored INTEGER NOT NULL DEFAULT 0" + + "restored INTEGER NOT NULL DEFAULT 0," + + "profileId INTEGER DEFAULT " + userSerialNumber + ");"); addWorkspacesTable(db); @@ -847,6 +854,14 @@ public class LauncherProvider extends ContentProvider { version = 19; } + if (version < 20) { + // Add userId column + if (addProfileColumn(db)) { + version = 20; + } + // else old version remains, which means we wipe old data + } + if (version != DATABASE_VERSION) { Log.w(TAG, "Destroying all old data."); db.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVORITES); @@ -856,6 +871,29 @@ public class LauncherProvider extends ContentProvider { } } + private boolean addProfileColumn(SQLiteDatabase db) { + db.beginTransaction(); + try { + UserManagerCompat userManager = UserManagerCompat.getInstance(mContext); + // Default to the serial number of this user, for older + // shortcuts. + long userSerialNumber = userManager.getSerialNumberForUser( + UserHandleCompat.myUserHandle()); + // Insert new column for holding user serial number + db.execSQL("ALTER TABLE favorites " + + "ADD COLUMN profileId INTEGER DEFAULT " + + userSerialNumber + ";"); + db.setTransactionSuccessful(); + } catch (SQLException ex) { + // Old version remains, which means we wipe old data + Log.e(TAG, ex.getMessage(), ex); + return false; + } finally { + db.endTransaction(); + } + return true; + } + private boolean updateContactsShortcuts(SQLiteDatabase db) { final String selectWhere = buildOrWhereString(Favorites.ITEM_TYPE, new int[] { Favorites.ITEM_TYPE_SHORTCUT }); @@ -1786,6 +1824,8 @@ public class LauncherProvider extends ContentProvider { = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI); final int displayModeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.DISPLAY_MODE); + final int profileIndex + = c.getColumnIndex(LauncherSettings.Favorites.PROFILE_ID); int i = 0; int curX = 0; @@ -1817,6 +1857,19 @@ public class LauncherProvider extends ContentProvider { final int screen = c.getInt(screenIndex); int container = c.getInt(containerIndex); final String intentStr = c.getString(intentIndex); + + UserManagerCompat userManager = UserManagerCompat.getInstance(mContext); + UserHandleCompat userHandle; + final long userSerialNumber; + if (profileIndex != -1 && !c.isNull(profileIndex)) { + userSerialNumber = c.getInt(profileIndex); + userHandle = userManager.getUserForSerialNumber(userSerialNumber); + } else { + // Default to the serial number of this user, for older + // shortcuts. + userHandle = UserHandleCompat.myUserHandle(); + userSerialNumber = userManager.getSerialNumberForUser(userHandle); + } Launcher.addDumpLog(TAG, "migrating \"" + c.getString(titleIndex) + "\" (" + cellX + "," + cellY + "@" @@ -1843,7 +1896,8 @@ public class LauncherProvider extends ContentProvider { Launcher.addDumpLog(TAG, "skipping empty intent", true); continue; } else if (cn != null && - !LauncherModel.isValidPackageComponent(pm, cn)) { + !LauncherModel.isValidPackageActivity(mContext, cn, + userHandle)) { // component no longer exists. Launcher.addDumpLog(TAG, "skipping item whose component " + "no longer exists.", true); @@ -1882,6 +1936,7 @@ public class LauncherProvider extends ContentProvider { values.put(LauncherSettings.Favorites.URI, c.getString(uriIndex)); values.put(LauncherSettings.Favorites.DISPLAY_MODE, c.getInt(displayModeIndex)); + values.put(LauncherSettings.Favorites.PROFILE_ID, userSerialNumber); if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) { hotseat.put(screen, values); diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java index 2a768a278..355370283 100644 --- a/src/com/android/launcher3/LauncherSettings.java +++ b/src/com/android/launcher3/LauncherSettings.java @@ -212,6 +212,14 @@ class LauncherSettings { static final String SPANY = "spanY"; /** + * The profile id of the item in the cell. + * <P> + * Type: INTEGER + * </P> + */ + static final String PROFILE_ID = "profileId"; + + /** * The favorite is a user created folder */ static final int ITEM_TYPE_FOLDER = 2; diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java index 92b582dba..f40cf9fa1 100644 --- a/src/com/android/launcher3/ShortcutInfo.java +++ b/src/com/android/launcher3/ShortcutInfo.java @@ -26,6 +26,8 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.graphics.Bitmap; import android.util.Log; +import com.android.launcher3.compat.UserHandleCompat; + import java.util.ArrayList; import java.util.Arrays; @@ -111,12 +113,12 @@ public class ShortcutInfo extends ItemInfo { } } - - ShortcutInfo(Intent intent, CharSequence title, Bitmap icon) { + ShortcutInfo(Intent intent, CharSequence title, Bitmap icon, UserHandleCompat user) { this(); this.intent = intent; this.title = title; mIcon = icon; + this.user = user; } public ShortcutInfo(Context context, ShortcutInfo info) { @@ -130,8 +132,9 @@ public class ShortcutInfo extends ItemInfo { } mIcon = info.mIcon; // TODO: should make a copy here. maybe we don't need this ctor at all customIcon = info.customIcon; - initFlagsAndFirstInstallTime( - getPackageInfo(context, intent.getComponent().getPackageName())); + flags = info.flags; + firstInstallTime = info.firstInstallTime; + user = info.user; } /** TODO: Remove this. It's only called by ApplicationInfo.makeShortcut. */ @@ -144,22 +147,6 @@ public class ShortcutInfo extends ItemInfo { firstInstallTime = info.firstInstallTime; } - public static PackageInfo getPackageInfo(Context context, String packageName) { - PackageInfo pi = null; - try { - PackageManager pm = context.getPackageManager(); - pi = pm.getPackageInfo(packageName, 0); - } catch (NameNotFoundException e) { - Log.d("ShortcutInfo", "PackageManager.getPackageInfo failed for " + packageName); - } - return pi; - } - - void initFlagsAndFirstInstallTime(PackageInfo pi) { - flags = AppInfo.initFlags(pi); - firstInstallTime = AppInfo.initFirstInstallTime(pi); - } - public void setIcon(Bitmap b) { mIcon = b; } @@ -172,30 +159,13 @@ public class ShortcutInfo extends ItemInfo { } public void updateIcon(IconCache iconCache) { - mIcon = iconCache.getIcon(intent); - usingFallbackIcon = iconCache.isDefaultIcon(mIcon); - } - - /** - * Creates the application intent based on a component name and various launch flags. - * Sets {@link #itemType} to {@link LauncherSettings.BaseLauncherColumns#ITEM_TYPE_APPLICATION}. - * - * @param className the class name of the component representing the intent - * @param launchFlags the launch flags - */ - final void setActivity(Context context, ComponentName className, int launchFlags) { - intent = new Intent(Intent.ACTION_MAIN); - intent.addCategory(Intent.CATEGORY_LAUNCHER); - intent.setComponent(className); - intent.setFlags(launchFlags); - itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_APPLICATION; - initFlagsAndFirstInstallTime( - getPackageInfo(context, intent.getComponent().getPackageName())); + mIcon = iconCache.getIcon(intent, user); + usingFallbackIcon = iconCache.isDefaultIcon(mIcon, user); } @Override - void onAddToDatabase(ContentValues values) { - super.onAddToDatabase(values); + void onAddToDatabase(Context context, ContentValues values) { + super.onAddToDatabase(context, values); String titleStr = title != null ? title.toString() : null; values.put(LauncherSettings.BaseLauncherColumns.TITLE, titleStr); @@ -224,10 +194,10 @@ public class ShortcutInfo extends ItemInfo { @Override public String toString() { - return "ShortcutInfo(title=" + title.toString() + "intent=" + intent + "id=" + this.id + return "ShortcutInfo(title=" + title + "intent=" + intent + "id=" + this.id + " type=" + this.itemType + " container=" + this.container + " screen=" + screenId + " cellX=" + cellX + " cellY=" + cellY + " spanX=" + spanX + " spanY=" + spanY - + " dropPos=" + Arrays.toString(dropPos) + ")"; + + " dropPos=" + Arrays.toString(dropPos) + " user=" + user + ")"; } public static void dumpShortcutInfoList(String tag, String label, diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index f7ca141a7..9bf2c2343 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -60,6 +60,8 @@ import android.view.accessibility.AccessibilityManager; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import android.widget.TextView; + +import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.FolderIcon.FolderRingAnimator; import com.android.launcher3.Launcher.CustomContentCallbacks; import com.android.launcher3.LauncherSettings.Favorites; @@ -4621,7 +4623,7 @@ public class Workspace extends SmoothPagedView // 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 ArrayList<String> packages) { + void removeItemsByPackageName(final ArrayList<String> packages, final UserHandleCompat user) { final HashSet<String> packageNames = new HashSet<String>(); packageNames.addAll(packages); @@ -4641,7 +4643,8 @@ public class Workspace extends SmoothPagedView @Override public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) { - if (packageNames.contains(cn.getPackageName())) { + if (packageNames.contains(cn.getPackageName()) + && info.user.equals(user)) { cns.add(cn); return true; } @@ -4651,13 +4654,13 @@ public class Workspace extends SmoothPagedView LauncherModel.filterItemInfos(infos, filter); // Remove the affected components - removeItemsByComponentName(cns); + removeItemsByComponentName(cns, user); } // Removes items that match the application info specified, 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 removeItemsByApplicationInfo(final ArrayList<AppInfo> appInfos) { + void removeItemsByApplicationInfo(final ArrayList<AppInfo> appInfos, UserHandleCompat user) { // Just create a hash table of all the specific components that this will affect HashSet<ComponentName> cns = new HashSet<ComponentName>(); for (AppInfo info : appInfos) { @@ -4665,10 +4668,11 @@ public class Workspace extends SmoothPagedView } // Remove all the things - removeItemsByComponentName(cns); + removeItemsByComponentName(cns, user); } - void removeItemsByComponentName(final HashSet<ComponentName> componentNames) { + void removeItemsByComponentName(final HashSet<ComponentName> componentNames, + final UserHandleCompat user) { ArrayList<CellLayout> cellLayouts = getWorkspaceAndHotseatCellLayouts(); for (final CellLayout layoutParent: cellLayouts) { final ViewGroup layout = layoutParent.getShortcutsAndWidgets(); @@ -4687,7 +4691,7 @@ public class Workspace extends SmoothPagedView public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) { if (parent instanceof FolderInfo) { - if (componentNames.contains(cn)) { + if (componentNames.contains(cn) && info.user.equals(user)) { FolderInfo folder = (FolderInfo) parent; ArrayList<ShortcutInfo> appsToRemove; if (folderAppsToRemove.containsKey(folder)) { @@ -4700,7 +4704,7 @@ public class Workspace extends SmoothPagedView return true; } } else { - if (componentNames.contains(cn)) { + if (componentNames.contains(cn) && info.user.equals(user)) { childrenToRemove.add(children.get(info)); return true; } diff --git a/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java b/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java new file mode 100644 index 000000000..3ba93ea91 --- /dev/null +++ b/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2014 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.compat; + +import android.content.ComponentName; +import android.graphics.drawable.Drawable; + +public abstract class LauncherActivityInfoCompat { + + LauncherActivityInfoCompat() { + } + + public abstract ComponentName getComponentName(); + public abstract UserHandleCompat getUser(); + public abstract CharSequence getLabel(); + public abstract Drawable getIcon(int density); + public abstract int getApplicationFlags(); + public abstract long getFirstInstallTime(); + public abstract Drawable getBadgedIcon(int density); +} diff --git a/src/com/android/launcher3/compat/LauncherActivityInfoCompatV16.java b/src/com/android/launcher3/compat/LauncherActivityInfoCompatV16.java new file mode 100644 index 000000000..9b9384d83 --- /dev/null +++ b/src/com/android/launcher3/compat/LauncherActivityInfoCompatV16.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2014 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.compat; + +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageInfo; +import android.content.pm.ResolveInfo; +import android.content.res.Resources; +import android.graphics.drawable.Drawable; + + +public class LauncherActivityInfoCompatV16 extends LauncherActivityInfoCompat { + private ActivityInfo mActivityInfo; + private ComponentName mComponentName; + private PackageManager mPm; + + LauncherActivityInfoCompatV16(Context context, ResolveInfo info) { + super(); + this.mActivityInfo = info.activityInfo; + mComponentName = new ComponentName(mActivityInfo.packageName, mActivityInfo.name); + mPm = context.getPackageManager(); + } + + public ComponentName getComponentName() { + return mComponentName; + } + + public UserHandleCompat getUser() { + return UserHandleCompat.myUserHandle(); + } + + public CharSequence getLabel() { + return mActivityInfo.loadLabel(mPm); + } + + public Drawable getIcon(int density) { + Drawable d = null; + if (mActivityInfo.getIconResource() != 0) { + Resources resources; + try { + resources = mPm.getResourcesForApplication(mActivityInfo.packageName); + } catch (PackageManager.NameNotFoundException e) { + resources = null; + } + if (resources != null) { + d = resources.getDrawableForDensity(mActivityInfo.getIconResource(), density); + } + } + if (d == null) { + Resources resources = Resources.getSystem(); + d = resources.getDrawableForDensity(android.R.mipmap.sym_def_app_icon, density); + } + return d; + } + + public int getApplicationFlags() { + return mActivityInfo.applicationInfo.flags; + } + + public long getFirstInstallTime() { + try { + PackageInfo info = mPm.getPackageInfo(mActivityInfo.packageName, 0); + return info != null ? info.firstInstallTime : 0; + } catch (NameNotFoundException e) { + return 0; + } + } + + public String getName() { + return mActivityInfo.name; + } + + public Drawable getBadgedIcon(int density) { + return getIcon(density); + } +} diff --git a/src/com/android/launcher3/compat/LauncherActivityInfoCompatVL.java b/src/com/android/launcher3/compat/LauncherActivityInfoCompatVL.java new file mode 100644 index 000000000..76125bd6a --- /dev/null +++ b/src/com/android/launcher3/compat/LauncherActivityInfoCompatVL.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2014 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.compat; + +import android.content.ComponentName; +import android.graphics.drawable.Drawable; +import android.os.UserHandle; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +public class LauncherActivityInfoCompatVL extends LauncherActivityInfoCompat { + private Object mLauncherActivityInfo; + private Class mLauncherActivityInfoClass; + private Method mGetComponentName; + private Method mGetUser; + private Method mGetLabel; + private Method mGetIcon; + private Method mGetApplicationFlags; + private Method mGetFirstInstallTime; + private Method mGetBadgedIcon; + + LauncherActivityInfoCompatVL(Object launcherActivityInfo) { + super(); + mLauncherActivityInfo = launcherActivityInfo; + mLauncherActivityInfoClass = ReflectUtils.getClassForName( + "android.content.pm.LauncherActivityInfo"); + mGetComponentName = ReflectUtils.getMethod(mLauncherActivityInfoClass, "getComponentName"); + mGetUser = ReflectUtils.getMethod(mLauncherActivityInfoClass, "getUser"); + mGetLabel = ReflectUtils.getMethod(mLauncherActivityInfoClass, "getLabel"); + mGetIcon = ReflectUtils.getMethod(mLauncherActivityInfoClass, "getIcon", int.class); + mGetApplicationFlags = ReflectUtils.getMethod(mLauncherActivityInfoClass, + "getApplicationFlags"); + mGetFirstInstallTime = ReflectUtils.getMethod(mLauncherActivityInfoClass, + "getFirstInstallTime"); + mGetBadgedIcon = ReflectUtils.getMethod(mLauncherActivityInfoClass, "getBadgedIcon", + int.class); + } + + public ComponentName getComponentName() { + return (ComponentName) ReflectUtils.invokeMethod(mLauncherActivityInfo, mGetComponentName); + } + + public UserHandleCompat getUser() { + return UserHandleCompat.fromUser((UserHandle) ReflectUtils.invokeMethod( + mLauncherActivityInfo, mGetUser)); + } + + public CharSequence getLabel() { + return (CharSequence) ReflectUtils.invokeMethod(mLauncherActivityInfo, mGetLabel); + } + + public Drawable getIcon(int density) { + return (Drawable) ReflectUtils.invokeMethod(mLauncherActivityInfo, mGetIcon, density); + } + + public int getApplicationFlags() { + return (Integer) ReflectUtils.invokeMethod(mLauncherActivityInfo, mGetApplicationFlags); + } + + public long getFirstInstallTime() { + return (Long) ReflectUtils.invokeMethod(mLauncherActivityInfo, mGetFirstInstallTime); + } + + public Drawable getBadgedIcon(int density) { + return (Drawable) ReflectUtils.invokeMethod(mLauncherActivityInfo, mGetBadgedIcon, density); + } +} diff --git a/src/com/android/launcher3/compat/LauncherAppsCompat.java b/src/com/android/launcher3/compat/LauncherAppsCompat.java new file mode 100644 index 000000000..59ee45c2c --- /dev/null +++ b/src/com/android/launcher3/compat/LauncherAppsCompat.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2014 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.compat; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.os.Bundle; +import android.os.UserHandle; +import android.os.UserManager; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public abstract class LauncherAppsCompat { + + public interface OnAppsChangedListenerCompat { + void onPackageRemoved(UserHandleCompat user, String packageName); + void onPackageAdded(UserHandleCompat user, String packageName); + void onPackageChanged(UserHandleCompat user, String packageName); + void onPackagesAvailable(UserHandleCompat user, String[] packageNames, boolean replacing); + void onPackagesUnavailable(UserHandleCompat user, String[] packageNames, boolean replacing); + } + + protected LauncherAppsCompat() { + } + + public static LauncherAppsCompat getInstance(Context context) { + // TODO change this to use api version once L gets an API number. + if ("L".equals(Build.VERSION.CODENAME)) { + Object launcherApps = context.getSystemService("launcherapps"); + if (launcherApps != null) { + LauncherAppsCompatVL compat = LauncherAppsCompatVL.build(context, launcherApps); + if (compat != null) { + return compat; + } + } + } + // Pre L or lunacher apps service not running, or reflection failed to find something. + return new LauncherAppsCompatV16(context); + } + + public abstract List<LauncherActivityInfoCompat> getActivityList(String packageName, + UserHandleCompat user); + public abstract LauncherActivityInfoCompat resolveActivity(Intent intent, + UserHandleCompat user); + public abstract void startActivityForProfile(ComponentName component, Rect sourceBounds, + Bundle opts, UserHandleCompat user); + public abstract void addOnAppsChangedListener(OnAppsChangedListenerCompat listener); + public abstract void removeOnAppsChangedListener(OnAppsChangedListenerCompat listener); + public abstract boolean isPackageEnabledForProfile(String packageName, UserHandleCompat user); + public abstract boolean isActivityEnabledForProfile(ComponentName component, + UserHandleCompat user); +}
\ No newline at end of file diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatV16.java b/src/com/android/launcher3/compat/LauncherAppsCompatV16.java new file mode 100644 index 000000000..c739eb9d2 --- /dev/null +++ b/src/com/android/launcher3/compat/LauncherAppsCompatV16.java @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2014 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.compat; + +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.PackageInfo; +import android.content.pm.ResolveInfo; +import android.graphics.Rect; +import android.os.Build; +import android.os.Bundle; +import android.os.UserHandle; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class LauncherAppsCompatV16 extends LauncherAppsCompat { + + private PackageManager mPm; + private Context mContext; + private List<OnAppsChangedListenerCompat> mListeners + = new ArrayList<OnAppsChangedListenerCompat>(); + private PackageMonitor mPackageMonitor; + + LauncherAppsCompatV16(Context context) { + mPm = context.getPackageManager(); + mContext = context; + mPackageMonitor = new PackageMonitor(); + } + + public List<LauncherActivityInfoCompat> getActivityList(String packageName, + UserHandleCompat user) { + final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); + mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); + mainIntent.setPackage(packageName); + List<ResolveInfo> infos = mPm.queryIntentActivities(mainIntent, 0); + List<LauncherActivityInfoCompat> list = + new ArrayList<LauncherActivityInfoCompat>(infos.size()); + for (ResolveInfo info : infos) { + list.add(new LauncherActivityInfoCompatV16(mContext, info)); + } + return list; + } + + public LauncherActivityInfoCompat resolveActivity(Intent intent, UserHandleCompat user) { + ResolveInfo info = mPm.resolveActivity(intent, 0); + if (info != null) { + return new LauncherActivityInfoCompatV16(mContext, info); + } + return null; + } + + public void startActivityForProfile(ComponentName component, Rect sourceBounds, + Bundle opts, UserHandleCompat user) { + Intent launchIntent = new Intent(Intent.ACTION_MAIN); + launchIntent.addCategory(Intent.CATEGORY_LAUNCHER); + launchIntent.setComponent(component); + launchIntent.setSourceBounds(sourceBounds); + launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + mContext.startActivity(launchIntent, opts); + } + + public synchronized void addOnAppsChangedListener(OnAppsChangedListenerCompat listener) { + if (listener != null && !mListeners.contains(listener)) { + mListeners.add(listener); + if (mListeners.size() == 1) { + registerForPackageIntents(); + } + } + } + + public synchronized void removeOnAppsChangedListener(OnAppsChangedListenerCompat listener) { + mListeners.remove(listener); + if (mListeners.size() == 0) { + unregisterForPackageIntents(); + } + } + + public boolean isPackageEnabledForProfile(String packageName, UserHandleCompat user) { + try { + PackageInfo info = mPm.getPackageInfo(packageName, 0); + return info != null && info.applicationInfo.enabled; + } catch (NameNotFoundException e) { + return false; + } + } + + public boolean isActivityEnabledForProfile(ComponentName component, UserHandleCompat user) { + try { + ActivityInfo info = mPm.getActivityInfo(component, 0); + return info != null && info.isEnabled(); + } catch (NameNotFoundException e) { + return false; + } + } + + private void unregisterForPackageIntents() { + mContext.unregisterReceiver(mPackageMonitor); + } + + private void registerForPackageIntents() { + IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); + filter.addAction(Intent.ACTION_PACKAGE_REMOVED); + filter.addAction(Intent.ACTION_PACKAGE_CHANGED); + filter.addDataScheme("package"); + mContext.registerReceiver(mPackageMonitor, filter); + filter = new IntentFilter(); + filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); + filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); + mContext.registerReceiver(mPackageMonitor, filter); + } + + private synchronized List<OnAppsChangedListenerCompat> getListeners() { + return new ArrayList<OnAppsChangedListenerCompat>(mListeners); + } + + private class PackageMonitor extends BroadcastReceiver { + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + final UserHandleCompat user = UserHandleCompat.myUserHandle(); + + if (Intent.ACTION_PACKAGE_CHANGED.equals(action) + || Intent.ACTION_PACKAGE_REMOVED.equals(action) + || Intent.ACTION_PACKAGE_ADDED.equals(action)) { + final String packageName = intent.getData().getSchemeSpecificPart(); + final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); + + if (packageName == null || packageName.length() == 0) { + // they sent us a bad intent + return; + } + if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) { + for (OnAppsChangedListenerCompat listener : getListeners()) { + listener.onPackageChanged(user, packageName); + } + } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) { + if (!replacing) { + for (OnAppsChangedListenerCompat listener : getListeners()) { + listener.onPackageRemoved(user, packageName); + } + } + // else, we are replacing the package, so a PACKAGE_ADDED will be sent + // later, we will update the package at this time + } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) { + if (!replacing) { + for (OnAppsChangedListenerCompat listener : getListeners()) { + listener.onPackageAdded(user, packageName); + } + } else { + for (OnAppsChangedListenerCompat listener : getListeners()) { + listener.onPackageChanged(user, packageName); + } + } + } + } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) { + final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); + String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); + for (OnAppsChangedListenerCompat listener : getListeners()) { + listener.onPackagesAvailable(user, packages, replacing); + } + } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) { + final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); + String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); + for (OnAppsChangedListenerCompat listener : getListeners()) { + listener.onPackagesUnavailable(user, packages, replacing); + } + } + } + } +} diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java new file mode 100644 index 000000000..c933712a9 --- /dev/null +++ b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2014 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.compat; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.graphics.Rect; +import android.os.Build; +import android.os.Bundle; +import android.os.UserHandle; + +import java.lang.reflect.InvocationHandler; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import java.lang.reflect.Proxy; +import java.lang.reflect.Method; + +public class LauncherAppsCompatVL extends LauncherAppsCompat { + + private Object mLauncherApps; + private Class mLauncherAppsClass; + private Class mListenerClass; + private Method mGetActivityList; + private Method mResolveActivity; + private Method mStartActivityForProfile; + private Method mAddOnAppsChangedListener; + private Method mRemoveOnAppsChangedListener; + private Method mIsPackageEnabledForProfile; + private Method mIsActivityEnabledForProfile; + + private Map<OnAppsChangedListenerCompat, Object> mListeners + = new HashMap<OnAppsChangedListenerCompat, Object>(); + + static LauncherAppsCompatVL build(Context context, Object launcherApps) { + LauncherAppsCompatVL compat = new LauncherAppsCompatVL(context, launcherApps); + + compat.mListenerClass = ReflectUtils.getClassForName( + "android.content.pm.LauncherApps$OnAppsChangedListener"); + compat.mLauncherAppsClass = ReflectUtils.getClassForName("android.content.pm.LauncherApps"); + + compat.mGetActivityList = ReflectUtils.getMethod(compat.mLauncherAppsClass, + "getActivityList", + String.class, UserHandle.class); + compat.mResolveActivity = ReflectUtils.getMethod(compat.mLauncherAppsClass, + "resolveActivity", + Intent.class, UserHandle.class); + compat.mStartActivityForProfile = ReflectUtils.getMethod(compat.mLauncherAppsClass, + "startActivityForProfile", + ComponentName.class, Rect.class, Bundle.class, UserHandle.class); + compat.mAddOnAppsChangedListener = ReflectUtils.getMethod(compat.mLauncherAppsClass, + "addOnAppsChangedListener", compat.mListenerClass); + compat.mRemoveOnAppsChangedListener = ReflectUtils.getMethod(compat.mLauncherAppsClass, + "removeOnAppsChangedListener", compat.mListenerClass); + compat.mIsPackageEnabledForProfile = ReflectUtils.getMethod(compat.mLauncherAppsClass, + "isPackageEnabledForProfile", String.class, UserHandle.class); + compat.mIsActivityEnabledForProfile = ReflectUtils.getMethod(compat.mLauncherAppsClass, + "isActivityEnabledForProfile", ComponentName.class, UserHandle.class); + + if (compat.mListenerClass != null + && compat.mLauncherAppsClass != null + && compat.mGetActivityList != null + && compat.mResolveActivity != null + && compat.mStartActivityForProfile != null + && compat.mAddOnAppsChangedListener != null + && compat.mRemoveOnAppsChangedListener != null + && compat.mIsPackageEnabledForProfile != null + && compat.mIsActivityEnabledForProfile != null) { + return compat; + } + return null; + } + + private LauncherAppsCompatVL(Context context, Object launcherApps) { + super(); + mLauncherApps = launcherApps; + } + + public List<LauncherActivityInfoCompat> getActivityList(String packageName, + UserHandleCompat user) { + List<Object> list = (List<Object>) ReflectUtils.invokeMethod(mLauncherApps, + mGetActivityList, packageName, user.getUser()); + if (list.size() == 0) { + return Collections.EMPTY_LIST; + } + ArrayList<LauncherActivityInfoCompat> compatList = + new ArrayList<LauncherActivityInfoCompat>(list.size()); + for (Object info : list) { + compatList.add(new LauncherActivityInfoCompatVL(info)); + } + return compatList; + } + + public LauncherActivityInfoCompat resolveActivity(Intent intent, UserHandleCompat user) { + return new LauncherActivityInfoCompatVL(ReflectUtils.invokeMethod(mLauncherApps, + mResolveActivity, intent, user.getUser())); + } + + public void startActivityForProfile(ComponentName component, Rect sourceBounds, + Bundle opts, UserHandleCompat user) { + ReflectUtils.invokeMethod(mLauncherApps, mStartActivityForProfile, + component, sourceBounds, opts, user.getUser()); + } + + public void addOnAppsChangedListener(LauncherAppsCompat.OnAppsChangedListenerCompat listener) { + Object wrappedListener = Proxy.newProxyInstance(mListenerClass.getClassLoader(), + new Class[]{mListenerClass}, new WrappedListener(listener)); + synchronized (mListeners) { + mListeners.put(listener, wrappedListener); + } + ReflectUtils.invokeMethod(mLauncherApps, mAddOnAppsChangedListener, wrappedListener); + } + + public void removeOnAppsChangedListener( + LauncherAppsCompat.OnAppsChangedListenerCompat listener) { + Object wrappedListener = null; + synchronized (mListeners) { + wrappedListener = mListeners.remove(listener); + } + if (wrappedListener != null) { + ReflectUtils.invokeMethod(mLauncherApps, mRemoveOnAppsChangedListener, wrappedListener); + } + } + + public boolean isPackageEnabledForProfile(String packageName, UserHandleCompat user) { + return (Boolean) ReflectUtils.invokeMethod(mLauncherApps, mIsPackageEnabledForProfile, + packageName, user.getUser()); + } + + public boolean isActivityEnabledForProfile(ComponentName component, UserHandleCompat user) { + return (Boolean) ReflectUtils.invokeMethod(mLauncherApps, mIsActivityEnabledForProfile, + component, user.getUser()); + } + + private static class WrappedListener implements InvocationHandler { + private LauncherAppsCompat.OnAppsChangedListenerCompat mListener; + + public WrappedListener(LauncherAppsCompat.OnAppsChangedListenerCompat listener) { + mListener = listener; + } + + public Object invoke(Object proxy, Method m, Object[] args) throws Throwable { + try { + String methodName = m.getName(); + if ("onPackageRemoved".equals(methodName)) { + onPackageRemoved((UserHandle) args[0], (String) args[1]); + } else if ("onPackageAdded".equals(methodName)) { + onPackageAdded((UserHandle) args[0], (String) args[1]); + } else if ("onPackageChanged".equals(methodName)) { + onPackageChanged((UserHandle) args[0], (String) args[1]); + } else if ("onPackagesAvailable".equals(methodName)) { + onPackagesAvailable((UserHandle) args[0], (String []) args[1], + (Boolean) args[2]); + } else if ("onPackagesUnavailable".equals(methodName)) { + onPackagesUnavailable((UserHandle) args[0], (String []) args[1], + (Boolean) args[2]); + } + } finally { + return null; + } + } + + public void onPackageRemoved(UserHandle user, String packageName) { + mListener.onPackageRemoved(UserHandleCompat.fromUser(user), packageName); + } + + public void onPackageAdded(UserHandle user, String packageName) { + mListener.onPackageAdded(UserHandleCompat.fromUser(user), packageName); + } + + public void onPackageChanged(UserHandle user, String packageName) { + mListener.onPackageChanged(UserHandleCompat.fromUser(user), packageName); + } + + public void onPackagesAvailable(UserHandle user, String[] packageNames, boolean replacing) { + mListener.onPackagesAvailable(UserHandleCompat.fromUser(user), packageNames, replacing); + } + + public void onPackagesUnavailable(UserHandle user, String[] packageNames, + boolean replacing) { + mListener.onPackagesUnavailable(UserHandleCompat.fromUser(user), packageNames, + replacing); + } + } +} + diff --git a/src/com/android/launcher3/compat/ReflectUtils.java b/src/com/android/launcher3/compat/ReflectUtils.java new file mode 100644 index 000000000..e1b8a1f95 --- /dev/null +++ b/src/com/android/launcher3/compat/ReflectUtils.java @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2014 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.compat; + +import android.util.Log; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +public class ReflectUtils { + private static final String TAG = "LauncherReflect"; + + public static Class getClassForName(String className) { + try { + return Class.forName(className); + } catch (ClassNotFoundException e) { + Log.e(TAG, "Couldn't find class " + className, e); + return null; + } + } + + public static Method getMethod(Class clazz, String method) { + try { + return clazz.getMethod(method); + } catch (NoSuchMethodException e) { + Log.e(TAG, "Couldn't find methid " + clazz.getName() + " " + method, e); + return null; + } + } + + public static Method getMethod(Class clazz, String method, Class param1) { + try { + return clazz.getMethod(method, param1); + } catch (NoSuchMethodException e) { + Log.e(TAG, "Couldn't find methid " + clazz.getName() + " " + method, e); + return null; + } + } + + public static Method getMethod(Class clazz, String method, Class param1, Class param2) { + try { + return clazz.getMethod(method, param1, param2); + } catch (NoSuchMethodException e) { + Log.e(TAG, "Couldn't find methid " + clazz.getName() + " " + method, e); + return null; + } + } + + public static Method getMethod(Class clazz, String method, Class param1, Class param2, + Class param3) { + try { + return clazz.getMethod(method, param1, param2, param3); + } catch (NoSuchMethodException e) { + Log.e(TAG, "Couldn't find methid " + clazz.getName() + " " + method, e); + return null; + } + } + + public static Method getMethod(Class clazz, String method, Class param1, Class param2, + Class param3, Class param4) { + try { + return clazz.getMethod(method, param1, param2, param3, param4); + } catch (NoSuchMethodException e) { + Log.e(TAG, "Couldn't find methid " + clazz.getName() + " " + method, e); + return null; + } + } + + public static Object invokeMethod(Object object, Method method) { + try { + return method.invoke(object); + } catch (SecurityException e) { + Log.e(TAG, "Couldn't invoke method " + method, e); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Couldn't invoke method " + method, e); + } catch (IllegalAccessException e) { + Log.e(TAG, "Couldn't invoke method " + method, e); + } catch (InvocationTargetException e) { + Log.e(TAG, "Couldn't invoke method " + method, e); + } + return null; + } + + public static Object invokeMethod(Object object, Method method, Object param1) { + try { + return method.invoke(object, param1); + } catch (SecurityException e) { + Log.e(TAG, "Couldn't invoke method " + method, e); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Couldn't invoke method " + method, e); + } catch (IllegalAccessException e) { + Log.e(TAG, "Couldn't invoke method " + method, e); + } catch (InvocationTargetException e) { + Log.e(TAG, "Couldn't invoke method " + method, e); + } + return null; + } + + public static Object invokeMethod(Object object, Method method, Object param1, Object param2) { + try { + return method.invoke(object, param1, param2); + } catch (SecurityException e) { + Log.e(TAG, "Couldn't invoke method " + method, e); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Couldn't invoke method " + method, e); + } catch (IllegalAccessException e) { + Log.e(TAG, "Couldn't invoke method " + method, e); + } catch (InvocationTargetException e) { + Log.e(TAG, "Couldn't invoke method " + method, e); + } + return null; + } + + public static Object invokeMethod(Object object, Method method, Object param1, Object param2, + Object param3) { + try { + return method.invoke(object, param1, param2, param3); + } catch (SecurityException e) { + Log.e(TAG, "Couldn't invoke method " + method, e); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Couldn't invoke method " + method, e); + } catch (IllegalAccessException e) { + Log.e(TAG, "Couldn't invoke method " + method, e); + } catch (InvocationTargetException e) { + Log.e(TAG, "Couldn't invoke method " + method, e); + } + return null; + } + + public static Object invokeMethod(Object object, Method method, Object param1, Object param2, + Object param3, Object param4) { + try { + return method.invoke(object, param1, param2, param3, param4); + } catch (SecurityException e) { + Log.e(TAG, "Couldn't invoke method " + method, e); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Couldn't invoke method " + method, e); + } catch (IllegalAccessException e) { + Log.e(TAG, "Couldn't invoke method " + method, e); + } catch (InvocationTargetException e) { + Log.e(TAG, "Couldn't invoke method " + method, e); + } + return null; + } +}
\ No newline at end of file diff --git a/src/com/android/launcher3/compat/UserHandleCompat.java b/src/com/android/launcher3/compat/UserHandleCompat.java new file mode 100644 index 000000000..8f5dda238 --- /dev/null +++ b/src/com/android/launcher3/compat/UserHandleCompat.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2014 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.compat; + +import android.os.Build; +import android.os.UserHandle; + +public class UserHandleCompat { + private UserHandle mUser; + + private UserHandleCompat(UserHandle user) { + mUser = user; + } + + private UserHandleCompat() { + } + + public static UserHandleCompat myUserHandle() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + return new UserHandleCompat(android.os.Process.myUserHandle()); + } else { + return new UserHandleCompat(); + } + } + + static UserHandleCompat fromUser(UserHandle user) { + if (user == null) { + return null; + } else { + return new UserHandleCompat(user); + } + } + + UserHandle getUser() { + return mUser; + } + + @Override + public String toString() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + return mUser.toString(); + } else { + return ""; + } + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof UserHandleCompat)) { + return false; + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + return mUser.equals(((UserHandleCompat) other).mUser); + } else { + return true; + } + } + + @Override + public int hashCode() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + return mUser.hashCode(); + } else { + return 0; + } + } +}
\ No newline at end of file diff --git a/src/com/android/launcher3/compat/UserManagerCompat.java b/src/com/android/launcher3/compat/UserManagerCompat.java new file mode 100644 index 000000000..256e04a7b --- /dev/null +++ b/src/com/android/launcher3/compat/UserManagerCompat.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2014 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.compat; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.os.Build; + +import java.util.List; + +public abstract class UserManagerCompat { + protected UserManagerCompat() { + } + + public static UserManagerCompat getInstance(Context context) { + // TODO change this to use api version once L gets an API number. + if ("L".equals(Build.VERSION.CODENAME)) { + return new UserManagerCompatVL(context); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + return new UserManagerCompatV17(context); + } else { + return new UserManagerCompatV16(); + } + } + + public abstract List<UserHandleCompat> getUserProfiles(); + public abstract long getSerialNumberForUser(UserHandleCompat user); + public abstract UserHandleCompat getUserForSerialNumber(long serialNumber); + public abstract Drawable getBadgedDrawableForUser(Drawable unbadged, UserHandleCompat user); +} diff --git a/src/com/android/launcher3/compat/UserManagerCompatV16.java b/src/com/android/launcher3/compat/UserManagerCompatV16.java new file mode 100644 index 000000000..2009e4e27 --- /dev/null +++ b/src/com/android/launcher3/compat/UserManagerCompatV16.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2014 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.compat; + +import android.graphics.drawable.Drawable; + +import java.util.ArrayList; +import java.util.List; + +public class UserManagerCompatV16 extends UserManagerCompat { + + UserManagerCompatV16() { + } + + public List<UserHandleCompat> getUserProfiles() { + List<UserHandleCompat> profiles = new ArrayList<UserHandleCompat>(1); + profiles.add(UserHandleCompat.myUserHandle()); + return profiles; + } + + public UserHandleCompat getUserForSerialNumber(long serialNumber) { + return UserHandleCompat.myUserHandle(); + } + + public Drawable getBadgedDrawableForUser(Drawable unbadged, + UserHandleCompat user) { + return unbadged; + } + + public long getSerialNumberForUser(UserHandleCompat user) { + return 0; + } +} diff --git a/src/com/android/launcher3/compat/UserManagerCompatV17.java b/src/com/android/launcher3/compat/UserManagerCompatV17.java new file mode 100644 index 000000000..055359afe --- /dev/null +++ b/src/com/android/launcher3/compat/UserManagerCompatV17.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2014 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.compat; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.os.UserHandle; +import android.os.UserManager; + +import java.util.ArrayList; +import java.util.List; + +public class UserManagerCompatV17 extends UserManagerCompatV16 { + protected UserManager mUserManager; + + UserManagerCompatV17(Context context) { + mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); + } + + public long getSerialNumberForUser(UserHandleCompat user) { + return mUserManager.getSerialNumberForUser(user.getUser()); + } + + public UserHandleCompat getUserForSerialNumber(long serialNumber) { + return UserHandleCompat.fromUser(mUserManager.getUserForSerialNumber(serialNumber)); + } +} + diff --git a/src/com/android/launcher3/compat/UserManagerCompatVL.java b/src/com/android/launcher3/compat/UserManagerCompatVL.java new file mode 100644 index 000000000..8d3ca8577 --- /dev/null +++ b/src/com/android/launcher3/compat/UserManagerCompatVL.java @@ -0,0 +1,68 @@ + +/* + * Copyright (C) 2014 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.compat; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.os.UserHandle; +import android.os.UserManager; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +public class UserManagerCompatVL extends UserManagerCompatV17 { + + UserManagerCompatVL(Context context) { + super(context); + } + + public List<UserHandleCompat> getUserProfiles() { + Method method = ReflectUtils.getMethod(mUserManager.getClass(), "getUserProfiles"); + if (method != null) { + List<UserHandle> users = (List<UserHandle>) ReflectUtils.invokeMethod( + mUserManager, method); + if (users != null) { + ArrayList<UserHandleCompat> compatUsers = new ArrayList<UserHandleCompat>( + users.size()); + for (UserHandle user : users) { + compatUsers.add(UserHandleCompat.fromUser(user)); + } + return compatUsers; + } + } + // Fall back to non L version. + return super.getUserProfiles(); + } + + public Drawable getBadgedDrawableForUser(Drawable unbadged, UserHandleCompat user) { + Method method = ReflectUtils.getMethod(mUserManager.getClass(), "getBadgedDrawableForUser", + Drawable.class, UserHandle.class); + if (method != null) { + Drawable d = (Drawable) ReflectUtils.invokeMethod(mUserManager, method, unbadged, + user.getUser()); + if (d != null) { + return d; + } + } + // Fall back to non L version. + return super.getBadgedDrawableForUser(unbadged, user); + } +} + |