From 29ad445ee2dbdf924ec7ac980c26f01f94290bb4 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 10 Nov 2014 18:05:31 -0800 Subject: Adding shortcuts corresponding to ManagedUsers automatically. Bug: 16188104 Change-Id: Ic07578dd187263f59f3c431cbb78dea90d0c24f4 --- src/com/android/launcher3/AppInfo.java | 19 +- .../android/launcher3/InstallShortcutReceiver.java | 377 ++++++++++++--------- src/com/android/launcher3/LauncherAppState.java | 15 +- src/com/android/launcher3/LauncherModel.java | 137 ++++++-- 4 files changed, 331 insertions(+), 217 deletions(-) (limited to 'src/com/android/launcher3') diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java index 674d8e819..6d1350a59 100644 --- a/src/com/android/launcher3/AppInfo.java +++ b/src/com/android/launcher3/AppInfo.java @@ -88,16 +88,11 @@ public class AppInfo extends ItemInfo { 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()); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); - long serialNumber = UserManagerCompat.getInstance(context).getSerialNumberForUser(user); - intent.putExtra(EXTRA_PROFILE, serialNumber); + intent = makeLaunchIntent(context, info, user); this.user = user; } - private static int initFlags(LauncherActivityInfoCompat info) { + public static int initFlags(LauncherActivityInfoCompat info) { int appFlags = info.getApplicationInfo().flags; int flags = 0; if ((appFlags & android.content.pm.ApplicationInfo.FLAG_SYSTEM) == 0) { @@ -154,4 +149,14 @@ public class AppInfo extends ItemInfo { public ShortcutInfo makeShortcut() { return new ShortcutInfo(this); } + + public static Intent makeLaunchIntent(Context context, LauncherActivityInfoCompat info, + UserHandleCompat user) { + long serialNumber = UserManagerCompat.getInstance(context).getSerialNumberForUser(user); + return new Intent(Intent.ACTION_MAIN) + .addCategory(Intent.CATEGORY_LAUNCHER) + .setComponent(info.getComponentName()) + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) + .putExtra(EXTRA_PROFILE, serialNumber); + } } diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java index e9fb499ad..9103fad77 100644 --- a/src/com/android/launcher3/InstallShortcutReceiver.java +++ b/src/com/android/launcher3/InstallShortcutReceiver.java @@ -27,14 +27,18 @@ import android.graphics.BitmapFactory; import android.text.TextUtils; import android.util.Base64; import android.util.Log; -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 org.json.JSONException; import org.json.JSONObject; import org.json.JSONStringer; import org.json.JSONTokener; +import java.net.URISyntaxException; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; @@ -47,73 +51,49 @@ public class InstallShortcutReceiver extends BroadcastReceiver { private static final String ACTION_INSTALL_SHORTCUT = "com.android.launcher.action.INSTALL_SHORTCUT"; - private static final String DATA_INTENT_KEY = "intent.data"; private static final String LAUNCH_INTENT_KEY = "intent.launch"; private static final String NAME_KEY = "name"; private static final String ICON_KEY = "icon"; private static final String ICON_RESOURCE_NAME_KEY = "iconResource"; private static final String ICON_RESOURCE_PACKAGE_NAME_KEY = "iconResourcePackage"; + + private static final String APP_SHORTCUT_TYPE_KEY = "isAppShortcut"; + private static final String USER_HANDLE_KEY = "userHandle"; + // The set of shortcuts that are pending install private static final String APPS_PENDING_INSTALL = "apps_to_install"; public static final int NEW_SHORTCUT_BOUNCE_DURATION = 450; public static final int NEW_SHORTCUT_STAGGER_DELAY = 85; - private static final int INSTALL_SHORTCUT_SUCCESSFUL = 0; - private static final int INSTALL_SHORTCUT_IS_DUPLICATE = -1; - - private static Object sLock = new Object(); - - private static void addToStringSet(SharedPreferences sharedPrefs, - SharedPreferences.Editor editor, String key, String value) { - Set strings = sharedPrefs.getStringSet(key, null); - if (strings == null) { - strings = new HashSet(1); - } else { - strings = new HashSet(strings); - } - strings.add(value); - editor.putStringSet(key, strings); - } + private static final Object sLock = new Object(); private static void addToInstallQueue( SharedPreferences sharedPrefs, PendingInstallShortcutInfo info) { synchronized(sLock) { - try { - JSONStringer json = new JSONStringer() - .object() - .key(DATA_INTENT_KEY).value(info.data.toUri(0)) - .key(LAUNCH_INTENT_KEY).value(info.launchIntent.toUri(0)) - .key(NAME_KEY).value(info.name); - if (info.icon != null) { - byte[] iconByteArray = ItemInfo.flattenBitmap(info.icon); - json = json.key(ICON_KEY).value( - Base64.encodeToString( - iconByteArray, 0, iconByteArray.length, Base64.DEFAULT)); + String encoded = info.encodeToString(); + if (encoded != null) { + Set strings = sharedPrefs.getStringSet(APPS_PENDING_INSTALL, null); + if (strings == null) { + strings = new HashSet(1); + } else { + strings = new HashSet(strings); } - if (info.iconResource != null) { - json = json.key(ICON_RESOURCE_NAME_KEY).value(info.iconResource.resourceName); - json = json.key(ICON_RESOURCE_PACKAGE_NAME_KEY) - .value(info.iconResource.packageName); - } - json = json.endObject(); - SharedPreferences.Editor editor = sharedPrefs.edit(); - if (DBG) Log.d(TAG, "Adding to APPS_PENDING_INSTALL: " + json); - addToStringSet(sharedPrefs, editor, APPS_PENDING_INSTALL, json.toString()); - editor.commit(); - } catch (org.json.JSONException e) { - Log.d(TAG, "Exception when adding shortcut: " + e); + strings.add(encoded); + sharedPrefs.edit().putStringSet(APPS_PENDING_INSTALL, strings).commit(); } } } - public static void removeFromInstallQueue(SharedPreferences sharedPrefs, - ArrayList packageNames) { + public static void removeFromInstallQueue(Context context, ArrayList packageNames, + UserHandleCompat user) { if (packageNames.isEmpty()) { return; } + String spKey = LauncherAppState.getSharedPreferencesKey(); + SharedPreferences sp = context.getSharedPreferences(spKey, Context.MODE_PRIVATE); synchronized(sLock) { - Set strings = sharedPrefs.getStringSet(APPS_PENDING_INSTALL, null); + Set strings = sp.getStringSet(APPS_PENDING_INSTALL, null); if (DBG) { Log.d(TAG, "APPS_PENDING_INSTALL: " + strings + ", removing packages: " + packageNames); @@ -122,34 +102,20 @@ public class InstallShortcutReceiver extends BroadcastReceiver { Set newStrings = new HashSet(strings); Iterator newStringsIter = newStrings.iterator(); while (newStringsIter.hasNext()) { - String json = newStringsIter.next(); - try { - JSONObject object = (JSONObject) new JSONTokener(json).nextValue(); - Intent launchIntent = Intent.parseUri(object.getString(LAUNCH_INTENT_KEY), 0); - String pn = launchIntent.getPackage(); - if (pn == null) { - if (launchIntent.getComponent() == null) { - continue; - } - pn = launchIntent.getComponent().getPackageName(); - } - if (packageNames.contains(pn)) { - newStringsIter.remove(); - } - } catch (org.json.JSONException e) { - Log.d(TAG, "Exception reading shortcut to remove: " + e); - } catch (java.net.URISyntaxException e) { - Log.d(TAG, "Exception reading shortcut to remove: " + e); + String encoded = newStringsIter.next(); + PendingInstallShortcutInfo info = decode(encoded, context); + if (info == null || (packageNames.contains(info.getTargetPackage()) + && user.equals(info.user))) { + newStringsIter.remove(); } } - sharedPrefs.edit().putStringSet(APPS_PENDING_INSTALL, - new HashSet(newStrings)).commit(); + sp.edit().putStringSet(APPS_PENDING_INSTALL, newStrings).commit(); } } } private static ArrayList getAndClearInstallQueue( - SharedPreferences sharedPrefs) { + SharedPreferences sharedPrefs, Context context) { synchronized(sLock) { Set strings = sharedPrefs.getStringSet(APPS_PENDING_INSTALL, null); if (DBG) Log.d(TAG, "Getting and clearing APPS_PENDING_INSTALL: " + strings); @@ -158,36 +124,10 @@ public class InstallShortcutReceiver extends BroadcastReceiver { } ArrayList infos = new ArrayList(); - for (String json : strings) { - try { - JSONObject object = (JSONObject) new JSONTokener(json).nextValue(); - Intent data = Intent.parseUri(object.getString(DATA_INTENT_KEY), 0); - Intent launchIntent = - Intent.parseUri(object.getString(LAUNCH_INTENT_KEY), 0); - String name = object.getString(NAME_KEY); - String iconBase64 = object.optString(ICON_KEY); - String iconResourceName = object.optString(ICON_RESOURCE_NAME_KEY); - String iconResourcePackageName = - object.optString(ICON_RESOURCE_PACKAGE_NAME_KEY); - if (iconBase64 != null && !iconBase64.isEmpty()) { - byte[] iconArray = Base64.decode(iconBase64, Base64.DEFAULT); - Bitmap b = BitmapFactory.decodeByteArray(iconArray, 0, iconArray.length); - data.putExtra(Intent.EXTRA_SHORTCUT_ICON, b); - } else if (iconResourceName != null && !iconResourceName.isEmpty()) { - Intent.ShortcutIconResource iconResource = - new Intent.ShortcutIconResource(); - iconResource.resourceName = iconResourceName; - iconResource.packageName = iconResourcePackageName; - data.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconResource); - } - data.putExtra(Intent.EXTRA_SHORTCUT_INTENT, launchIntent); - PendingInstallShortcutInfo info = - new PendingInstallShortcutInfo(data, name, launchIntent); + for (String encoded : strings) { + PendingInstallShortcutInfo info = decode(encoded, context); + if (info != null) { infos.add(info); - } catch (org.json.JSONException e) { - Log.d(TAG, "Exception reading shortcut to add: " + e); - } catch (java.net.URISyntaxException e) { - Log.d(TAG, "Exception reading shortcut to add: " + e); } } sharedPrefs.edit().putStringSet(APPS_PENDING_INSTALL, new HashSet()).commit(); @@ -199,49 +139,26 @@ public class InstallShortcutReceiver extends BroadcastReceiver { // processAllPendingInstalls() is called. private static boolean mUseInstallQueue = false; - private static class PendingInstallShortcutInfo { - Intent data; - Intent launchIntent; - String name; - Bitmap icon; - Intent.ShortcutIconResource iconResource; - - public PendingInstallShortcutInfo(Intent rawData, String shortcutName, - Intent shortcutIntent) { - data = rawData; - name = shortcutName; - launchIntent = shortcutIntent; - } - } - public void onReceive(Context context, Intent data) { if (!ACTION_INSTALL_SHORTCUT.equals(data.getAction())) { return; } if (DBG) Log.d(TAG, "Got INSTALL_SHORTCUT: " + data.toUri(0)); + PendingInstallShortcutInfo info = new PendingInstallShortcutInfo(data, context); - Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT); - if (intent == null) { - return; - } + queuePendingShortcutInfo(info, context); + } - // This name is only used for comparisons and notifications, so fall back to activity name - // if not supplied - String name = ensureValidName(context, intent, - data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME)).toString(); - Bitmap icon = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON); - Intent.ShortcutIconResource iconResource = - data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE); + static void queueInstallShortcut(LauncherActivityInfoCompat info, Context context) { + queuePendingShortcutInfo(new PendingInstallShortcutInfo(info, context), context); + } + private static void queuePendingShortcutInfo(PendingInstallShortcutInfo info, Context context) { // Queue the item up for adding if launcher has not loaded properly yet LauncherAppState.setApplicationContext(context.getApplicationContext()); LauncherAppState app = LauncherAppState.getInstance(); - boolean launcherNotLoaded = (app.getDynamicGrid() == null); - - PendingInstallShortcutInfo info = new PendingInstallShortcutInfo(data, name, intent); - info.icon = icon; - info.iconResource = iconResource; + boolean launcherNotLoaded = app.getModel().getCallback() == null; String spKey = LauncherAppState.getSharedPreferencesKey(); SharedPreferences sp = context.getSharedPreferences(spKey, Context.MODE_PRIVATE); @@ -261,33 +178,22 @@ public class InstallShortcutReceiver extends BroadcastReceiver { static void flushInstallQueue(Context context) { String spKey = LauncherAppState.getSharedPreferencesKey(); SharedPreferences sp = context.getSharedPreferences(spKey, Context.MODE_PRIVATE); - ArrayList installQueue = getAndClearInstallQueue(sp); + ArrayList installQueue = getAndClearInstallQueue(sp, context); if (!installQueue.isEmpty()) { Iterator iter = installQueue.iterator(); ArrayList addShortcuts = new ArrayList(); - int result = INSTALL_SHORTCUT_SUCCESSFUL; - String duplicateName = ""; while (iter.hasNext()) { final PendingInstallShortcutInfo pendingInfo = iter.next(); - //final Intent data = pendingInfo.data; final Intent intent = pendingInfo.launchIntent; - final String name = pendingInfo.name; if (LauncherAppState.isDisableAllApps() && !isValidShortcutLaunchIntent(intent)) { if (DBG) Log.d(TAG, "Ignoring shortcut with launchIntent:" + intent); continue; } - final boolean exists = LauncherModel.shortcutExists(context, name, intent); - //final boolean allowDuplicate = data.getBooleanExtra(Launcher.EXTRA_SHORTCUT_DUPLICATE, true); - // If the intent specifies a package, make sure the package exists - String packageName = intent.getPackage(); - if (packageName == null) { - packageName = intent.getComponent() == null ? null : - intent.getComponent().getPackageName(); - } - if (packageName != null && !packageName.isEmpty()) { + String packageName = pendingInfo.getTargetPackage(); + if (TextUtils.isEmpty(packageName)) { UserHandleCompat myUserHandle = UserHandleCompat.myUserHandle(); if (!LauncherModel.isValidPackage(context, packageName, myUserHandle)) { if (DBG) Log.d(TAG, "Ignoring shortcut for absent package:" + intent); @@ -295,19 +201,12 @@ public class InstallShortcutReceiver extends BroadcastReceiver { } } + final boolean exists = LauncherModel.shortcutExists(context, pendingInfo.label, + intent, pendingInfo.user); if (!exists) { // Generate a shortcut info to add into the model - ShortcutInfo info = getShortcutInfo(context, pendingInfo.data, - pendingInfo.launchIntent); - addShortcuts.add(info); + addShortcuts.add(pendingInfo.getShortcutInfo()); } - - } - - // Notify the user once if we weren't able to place any duplicates - if (result == INSTALL_SHORTCUT_IS_DUPLICATE) { - Toast.makeText(context, context.getString(R.string.shortcut_duplicate, - duplicateName), Toast.LENGTH_SHORT).show(); } // Add the new apps to the model and bind them @@ -342,22 +241,6 @@ public class InstallShortcutReceiver extends BroadcastReceiver { return true; } - private static ShortcutInfo getShortcutInfo(Context context, Intent data, - Intent launchIntent) { - if (launchIntent.getAction() == null) { - launchIntent.setAction(Intent.ACTION_VIEW); - } else if (launchIntent.getAction().equals(Intent.ACTION_MAIN) && - launchIntent.getCategories() != null && - launchIntent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) { - launchIntent.addFlags( - Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); - } - LauncherAppState app = LauncherAppState.getInstance(); - ShortcutInfo info = app.getModel().infoFromShortcutIntent(context, data); - info.title = ensureValidName(context, launchIntent, info.title); - return info; - } - /** * Ensures that we have a valid, non-null name. If the provided name is null, we will return * the application name instead. @@ -374,4 +257,168 @@ public class InstallShortcutReceiver extends BroadcastReceiver { } return name; } + + private static class PendingInstallShortcutInfo { + + final LauncherActivityInfoCompat activityInfo; + + final Intent data; + final Context mContext; + final Intent launchIntent; + final String label; + final UserHandleCompat user; + + /** + * Initializes a PendingInstallShortcutInfo received from a different app. + */ + public PendingInstallShortcutInfo(Intent data, Context context) { + this.data = data; + mContext = context; + + launchIntent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT); + label = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME); + user = UserHandleCompat.myUserHandle(); + activityInfo = null; + } + + /** + * Initializes a PendingInstallShortcutInfo to represent a launcher target. + */ + public PendingInstallShortcutInfo(LauncherActivityInfoCompat info, Context context) { + this.data = null; + mContext = context; + activityInfo = info; + user = info.getUser(); + + launchIntent = AppInfo.makeLaunchIntent(context, info, user); + label = info.getLabel().toString(); + } + + public String encodeToString() { + if (activityInfo != null) { + try { + // If it a launcher target, we only need component name, and user to + // recreate this. + return new JSONStringer() + .object() + .key(LAUNCH_INTENT_KEY).value(launchIntent.toUri(0)) + .key(APP_SHORTCUT_TYPE_KEY).value(true) + .key(USER_HANDLE_KEY).value(UserManagerCompat.getInstance(mContext) + .getSerialNumberForUser(user)) + .endObject().toString(); + } catch (JSONException e) { + Log.d(TAG, "Exception when adding shortcut: " + e); + return null; + } + } + + if (launchIntent.getAction() == null) { + launchIntent.setAction(Intent.ACTION_VIEW); + } else if (launchIntent.getAction().equals(Intent.ACTION_MAIN) && + launchIntent.getCategories() != null && + launchIntent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) { + launchIntent.addFlags( + Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + } + + // This name is only used for comparisons and notifications, so fall back to activity + // name if not supplied + String name = ensureValidName(mContext, launchIntent, label).toString(); + Bitmap icon = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON); + Intent.ShortcutIconResource iconResource = + data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE); + + // Only encode the parameters which are supported by the API. + try { + JSONStringer json = new JSONStringer() + .object() + .key(LAUNCH_INTENT_KEY).value(launchIntent.toUri(0)) + .key(NAME_KEY).value(name); + if (icon != null) { + byte[] iconByteArray = ItemInfo.flattenBitmap(icon); + json = json.key(ICON_KEY).value( + Base64.encodeToString( + iconByteArray, 0, iconByteArray.length, Base64.DEFAULT)); + } + if (iconResource != null) { + json = json.key(ICON_RESOURCE_NAME_KEY).value(iconResource.resourceName); + json = json.key(ICON_RESOURCE_PACKAGE_NAME_KEY) + .value(iconResource.packageName); + } + return json.endObject().toString(); + } catch (JSONException e) { + Log.d(TAG, "Exception when adding shortcut: " + e); + } + return null; + } + + public ShortcutInfo getShortcutInfo() { + if (activityInfo != null) { + final ShortcutInfo info = new ShortcutInfo(); + info.user = user; + info.title = label; + info.contentDescription = label; + info.customIcon = false; + info.intent = launchIntent; + info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; + info.flags = AppInfo.initFlags(activityInfo); + info.firstInstallTime = activityInfo.getFirstInstallTime(); + return info; + } else { + return LauncherAppState.getInstance().getModel().infoFromShortcutIntent(mContext, data); + } + } + + public String getTargetPackage() { + String packageName = launchIntent.getPackage(); + if (packageName == null) { + packageName = launchIntent.getComponent() == null ? null : + launchIntent.getComponent().getPackageName(); + } + return packageName; + } + } + + private static PendingInstallShortcutInfo decode(String encoded, Context context) { + try { + JSONObject object = (JSONObject) new JSONTokener(encoded).nextValue(); + Intent launcherIntent = Intent.parseUri(object.getString(LAUNCH_INTENT_KEY), 0); + + if (object.optBoolean(APP_SHORTCUT_TYPE_KEY)) { + // The is an internal launcher target shortcut. + UserHandleCompat user = UserManagerCompat.getInstance(context) + .getUserForSerialNumber(object.getLong(USER_HANDLE_KEY)); + + LauncherActivityInfoCompat info = LauncherAppsCompat.getInstance(context) + .resolveActivity(launcherIntent, user); + return info == null ? null : new PendingInstallShortcutInfo(info, context); + } + + Intent data = new Intent(); + data.putExtra(Intent.EXTRA_SHORTCUT_INTENT, launcherIntent); + data.putExtra(Intent.EXTRA_SHORTCUT_NAME, object.getString(NAME_KEY)); + + String iconBase64 = object.optString(ICON_KEY); + String iconResourceName = object.optString(ICON_RESOURCE_NAME_KEY); + String iconResourcePackageName = object.optString(ICON_RESOURCE_PACKAGE_NAME_KEY); + if (iconBase64 != null && !iconBase64.isEmpty()) { + byte[] iconArray = Base64.decode(iconBase64, Base64.DEFAULT); + Bitmap b = BitmapFactory.decodeByteArray(iconArray, 0, iconArray.length); + data.putExtra(Intent.EXTRA_SHORTCUT_ICON, b); + } else if (iconResourceName != null && !iconResourceName.isEmpty()) { + Intent.ShortcutIconResource iconResource = + new Intent.ShortcutIconResource(); + iconResource.resourceName = iconResourceName; + iconResource.packageName = iconResourcePackageName; + data.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconResource); + } + + return new PendingInstallShortcutInfo(data, context); + } catch (JSONException e) { + Log.d(TAG, "Exception reading shortcut to add: " + e); + } catch (URISyntaxException e) { + Log.d(TAG, "Exception reading shortcut to add: " + e); + } + return null; + } } diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index 29c88fa79..5b03ad433 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -46,12 +46,14 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks { private final AppFilter mAppFilter; private final BuildInfo mBuildInfo; - private LauncherModel mModel; - private IconCache mIconCache; + private final LauncherModel mModel; + private final IconCache mIconCache; + + private final boolean mIsScreenLarge; + private final float mScreenDensity; + private final int mLongPressTimeout = 300; + private WidgetPreviewLoader.CacheDb mWidgetPreviewCacheDb; - private boolean mIsScreenLarge; - private float mScreenDensity; - private int mLongPressTimeout = 300; private boolean mWallpaperChangedSinceLastCheck; private static WeakReference sLauncherProvider; @@ -158,9 +160,6 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks { }; LauncherModel setLauncher(Launcher launcher) { - if (mModel == null) { - throw new IllegalStateException("setLauncher() called before init()"); - } mModel.initialize(launcher); return mModel; } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 7f7dbc93c..f3da7c49c 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -30,6 +30,7 @@ import android.content.Intent; import android.content.Intent.ShortcutIconResource; import android.content.IntentFilter; import android.content.SharedPreferences; +import android.content.pm.LauncherApps.Callback; import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; @@ -78,7 +79,6 @@ import java.util.List; import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap; -import java.util.concurrent.atomic.AtomicBoolean; /** * Maintains in-memory state of the Launcher. It is expected that there should be only one @@ -115,6 +115,11 @@ public class LauncherModel extends BroadcastReceiver private boolean mIsLoaderTaskRunning; private volatile boolean mFlushingWorkerThread; + /** + * Maintain a set of packages per user, for which we added a shortcut on the workspace. + */ + private static final String INSTALLED_SHORTCUTS_SET_PREFIX = "installed_shortcuts_set_for_user_"; + // Specific runnable types that are run on the main thread deferred handler, this allows us to // clear all queued binding runnables when the Launcher activity is destroyed. private static final int MAIN_THREAD_NORMAL_RUNNABLE = 0; @@ -403,7 +408,7 @@ public class LauncherModel extends BroadcastReceiver // Process the updated package state Runnable r = new Runnable() { public void run() { - Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null; + Callbacks callbacks = getCallback(); if (callbacks != null) { callbacks.updatePackageState(installInfo); } @@ -416,7 +421,7 @@ public class LauncherModel extends BroadcastReceiver // Process the updated package badge Runnable r = new Runnable() { public void run() { - Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null; + Callbacks callbacks = getCallback(); if (callbacks != null) { callbacks.updatePackageBadge(packageName); } @@ -426,7 +431,7 @@ public class LauncherModel extends BroadcastReceiver } public void addAppsToAllApps(final Context ctx, final ArrayList allAppsApps) { - final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null; + final Callbacks callbacks = getCallback(); if (allAppsApps == null) { throw new RuntimeException("allAppsApps must not be null"); @@ -440,7 +445,7 @@ public class LauncherModel extends BroadcastReceiver public void run() { runOnMainThread(new Runnable() { public void run() { - Callbacks cb = mCallbacks != null ? mCallbacks.get() : null; + Callbacks cb = getCallback(); if (callbacks == cb && cb != null) { callbacks.bindAppsAdded(null, null, null, allAppsApps); } @@ -453,7 +458,7 @@ public class LauncherModel extends BroadcastReceiver public void addAndBindAddedWorkspaceApps(final Context context, final ArrayList workspaceApps) { - final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null; + final Callbacks callbacks = getCallback(); if (workspaceApps == null) { throw new RuntimeException("workspaceApps and allAppsApps must not be null"); @@ -485,7 +490,7 @@ public class LauncherModel extends BroadcastReceiver final Intent launchIntent = a.getIntent(); // Short-circuit this logic if the icon exists somewhere on the workspace - if (shortcutExists(context, name, launchIntent)) { + if (shortcutExists(context, name, launchIntent, a.user)) { continue; } @@ -544,7 +549,7 @@ public class LauncherModel extends BroadcastReceiver if (!addedShortcutsFinal.isEmpty()) { runOnMainThread(new Runnable() { public void run() { - Callbacks cb = mCallbacks != null ? mCallbacks.get() : null; + Callbacks cb = getCallback(); if (callbacks == cb && cb != null) { final ArrayList addAnimated = new ArrayList(); final ArrayList addNotAnimated = new ArrayList(); @@ -904,7 +909,8 @@ public class LauncherModel extends BroadcastReceiver * Returns true if the shortcuts already exists in the database. * we identify a shortcut by its title and intent. */ - static boolean shortcutExists(Context context, String title, Intent intent) { + static boolean shortcutExists(Context context, String title, Intent intent, + UserHandleCompat user) { final ContentResolver cr = context.getContentResolver(); final Intent intentWithPkg, intentWithoutPkg; @@ -923,16 +929,18 @@ public class LauncherModel extends BroadcastReceiver intentWithPkg = intent; intentWithoutPkg = intent; } + String userSerial = Long.toString(UserManagerCompat.getInstance(context) + .getSerialNumberForUser(user)); Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, - new String[]{"title", "intent"}, "title=? and (intent=? or intent=?)", - new String[]{title, intentWithPkg.toUri(0), intentWithoutPkg.toUri(0) }, null); - boolean result = false; + new String[] { "title", "intent", "profileId" }, + "title=? and (intent=? or intent=?) and profileId=?", + new String[] { title, intentWithPkg.toUri(0), intentWithoutPkg.toUri(0), userSerial }, + null); try { - result = c.moveToFirst(); + return c.moveToFirst(); } finally { c.close(); } - return result; } /** @@ -1374,11 +1382,9 @@ public class LauncherModel extends BroadcastReceiver mPreviousConfigMcc = currentConfig.mcc; } else if (SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED.equals(action) || SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED.equals(action)) { - if (mCallbacks != null) { - Callbacks callbacks = mCallbacks.get(); - if (callbacks != null) { - callbacks.bindSearchablesChanged(); - } + Callbacks callbacks = getCallback(); + if (callbacks != null) { + callbacks.bindSearchablesChanged(); } } else if (ACTION_UNREAD_CHANGED.equals(action)) { ComponentName componentName = intent.getParcelableExtra("component_name"); @@ -1425,13 +1431,11 @@ public class LauncherModel extends BroadcastReceiver */ public void startLoaderFromBackground() { boolean runLoader = false; - if (mCallbacks != null) { - Callbacks callbacks = mCallbacks.get(); - if (callbacks != null) { - // Only actually run the loader if they're not paused. - if (!callbacks.setLoadOnResume()) { - runLoader = true; - } + Callbacks callbacks = getCallback(); + if (callbacks != null) { + // Only actually run the loader if they're not paused. + if (!callbacks.setLoadOnResume()) { + runLoader = true; } } if (runLoader) { @@ -3149,6 +3153,8 @@ public class LauncherModel extends BroadcastReceiver // Clear the list of apps mBgAllAppsList.clear(); + SharedPreferences prefs = mContext.getSharedPreferences( + LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE); for (UserHandleCompat user : profiles) { // Query for the set of apps final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; @@ -3159,6 +3165,7 @@ public class LauncherModel extends BroadcastReceiver Log.d(TAG, "getActivityList got " + apps.size() + " apps for user " + user); } // Fail if we don't have any apps + // TODO: Fix this. Only fail for the current user. if (apps == null || apps.isEmpty()) { return; } @@ -3177,6 +3184,25 @@ public class LauncherModel extends BroadcastReceiver // This builds the icon bitmaps. mBgAllAppsList.add(new AppInfo(mContext, app, user, mIconCache, mLabelCache)); } + + if (!user.equals(UserHandleCompat.myUserHandle())) { + // Add shortcuts for packages which were installed while launcher was dead. + String shortcutsSetKey = INSTALLED_SHORTCUTS_SET_PREFIX + + mUserManager.getSerialNumberForUser(user); + Set packagesAdded = prefs.getStringSet(shortcutsSetKey, Collections.EMPTY_SET); + HashSet newPackageSet = new HashSet(); + + for (LauncherActivityInfoCompat info : apps) { + String packageName = info.getComponentName().getPackageName(); + if (!packagesAdded.contains(packageName) + && !newPackageSet.contains(packageName)) { + InstallShortcutReceiver.queueInstallShortcut(info, mContext); + } + newPackageSet.add(packageName); + } + + prefs.edit().putStringSet(shortcutsSetKey, newPackageSet).commit(); + } } // Huh? Shouldn't this be inside the Runnable below? final ArrayList added = mBgAllAppsList.added; @@ -3349,6 +3375,30 @@ public class LauncherModel extends BroadcastReceiver mIconCache.remove(packages[i], mUser); mBgAllAppsList.addPackage(context, packages[i], mUser); } + + // Auto add shortcuts for added packages. + if (!UserHandleCompat.myUserHandle().equals(mUser)) { + SharedPreferences prefs = context.getSharedPreferences( + LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE); + String shortcutsSetKey = INSTALLED_SHORTCUTS_SET_PREFIX + + mUserManager.getSerialNumberForUser(mUser); + Set shortcutSet = new HashSet( + prefs.getStringSet(shortcutsSetKey,Collections.EMPTY_SET)); + + for (int i=0; i activities = + mLauncherApps.getActivityList(packages[i], mUser); + if (activities != null && !activities.isEmpty()) { + InstallShortcutReceiver.queueInstallShortcut( + activities.get(0), context); + } + } + } + + prefs.edit().putStringSet(shortcutsSetKey, shortcutSet).commit(); + } break; case OP_UPDATE: for (int i=0; i shortcutSet = new HashSet( + prefs.getStringSet(shortcutsSetKey, Collections.EMPTY_SET)); + shortcutSet.removeAll(Arrays.asList(mPackages)); + prefs.edit().putStringSet(shortcutsSetKey, shortcutSet).commit(); + } + // Fall through case OP_UNAVAILABLE: boolean clearCache = mOp == OP_REMOVE; for (int i=0; i