diff options
Diffstat (limited to 'src/com/android/launcher3/InstallShortcutReceiver.java')
-rw-r--r-- | src/com/android/launcher3/InstallShortcutReceiver.java | 396 |
1 files changed, 222 insertions, 174 deletions
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java index 2edde4fae..1ab308558 100644 --- a/src/com/android/launcher3/InstallShortcutReceiver.java +++ b/src/com/android/launcher3/InstallShortcutReceiver.java @@ -17,7 +17,6 @@ package com.android.launcher3; import android.content.BroadcastReceiver; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; @@ -28,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; @@ -45,80 +48,52 @@ public class InstallShortcutReceiver extends BroadcastReceiver { private static final String TAG = "InstallShortcutReceiver"; private static final boolean DBG = false; - public static final String ACTION_INSTALL_SHORTCUT = + private static final String ACTION_INSTALL_SHORTCUT = "com.android.launcher.action.INSTALL_SHORTCUT"; - public static final String DATA_INTENT_KEY = "intent.data"; - public static final String LAUNCH_INTENT_KEY = "intent.launch"; - public static final String NAME_KEY = "name"; - public static final String ICON_KEY = "icon"; - public static final String ICON_RESOURCE_NAME_KEY = "iconResource"; - public static final String ICON_RESOURCE_PACKAGE_NAME_KEY = "iconResourcePackage"; + 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 - public static final String APPS_PENDING_INSTALL = "apps_to_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; - - // A mime-type representing shortcut data - public static final String SHORTCUT_MIMETYPE = - "com.android.launcher3/shortcut"; - - private static Object sLock = new Object(); - - private static void addToStringSet(SharedPreferences sharedPrefs, - SharedPreferences.Editor editor, String key, String value) { - Set<String> strings = sharedPrefs.getStringSet(key, null); - if (strings == null) { - strings = new HashSet<String>(0); - } else { - strings = new HashSet<String>(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)); - } - 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); + String encoded = info.encodeToString(); + if (encoded != null) { + Set<String> strings = sharedPrefs.getStringSet(APPS_PENDING_INSTALL, null); + if (strings == null) { + strings = new HashSet<String>(1); + } else { + strings = new HashSet<String>(strings); } - 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<String> packageNames) { + public static void removeFromInstallQueue(Context context, ArrayList<String> packageNames, + UserHandleCompat user) { if (packageNames.isEmpty()) { return; } + String spKey = LauncherAppState.getSharedPreferencesKey(); + SharedPreferences sp = context.getSharedPreferences(spKey, Context.MODE_PRIVATE); synchronized(sLock) { - Set<String> strings = sharedPrefs.getStringSet(APPS_PENDING_INSTALL, null); + Set<String> strings = sp.getStringSet(APPS_PENDING_INSTALL, null); if (DBG) { Log.d(TAG, "APPS_PENDING_INSTALL: " + strings + ", removing packages: " + packageNames); @@ -127,31 +102,20 @@ public class InstallShortcutReceiver extends BroadcastReceiver { Set<String> newStrings = new HashSet<String>(strings); Iterator<String> 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) { - 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<String>(newStrings)).commit(); + sp.edit().putStringSet(APPS_PENDING_INSTALL, newStrings).commit(); } } } private static ArrayList<PendingInstallShortcutInfo> getAndClearInstallQueue( - SharedPreferences sharedPrefs) { + SharedPreferences sharedPrefs, Context context) { synchronized(sLock) { Set<String> strings = sharedPrefs.getStringSet(APPS_PENDING_INSTALL, null); if (DBG) Log.d(TAG, "Getting and clearing APPS_PENDING_INSTALL: " + strings); @@ -160,36 +124,10 @@ public class InstallShortcutReceiver extends BroadcastReceiver { } ArrayList<PendingInstallShortcutInfo> infos = new ArrayList<PendingInstallShortcutInfo>(); - 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<String>()).commit(); @@ -201,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); @@ -263,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<PendingInstallShortcutInfo> installQueue = getAndClearInstallQueue(sp); + ArrayList<PendingInstallShortcutInfo> installQueue = getAndClearInstallQueue(sp, context); if (!installQueue.isEmpty()) { Iterator<PendingInstallShortcutInfo> iter = installQueue.iterator(); ArrayList<ItemInfo> addShortcuts = new ArrayList<ItemInfo>(); - 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); @@ -297,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 @@ -344,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, null); - 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. @@ -376,4 +257,171 @@ 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)); + if (user == null) { + return null; + } + + 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; + } } |