The Android Open Source Project | 31dd503 | 2009-03-03 19:32:27 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2008 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
Daniel Sandler | 325dc23 | 2013-06-05 22:57:57 -0400 | [diff] [blame] | 17 | package com.android.launcher3; |
The Android Open Source Project | 31dd503 | 2009-03-03 19:32:27 -0800 | [diff] [blame] | 18 | |
Sunny Goyal | 3be633b | 2016-12-08 09:59:25 -0800 | [diff] [blame] | 19 | import android.appwidget.AppWidgetManager; |
| 20 | import android.appwidget.AppWidgetProviderInfo; |
The Android Open Source Project | 31dd503 | 2009-03-03 19:32:27 -0800 | [diff] [blame] | 21 | import android.content.BroadcastReceiver; |
| 22 | import android.content.Context; |
| 23 | import android.content.Intent; |
Winson Chung | f0c6ae0 | 2012-03-21 16:10:31 -0700 | [diff] [blame] | 24 | import android.content.SharedPreferences; |
Winson Chung | c3a747a | 2012-02-29 10:56:19 -0800 | [diff] [blame] | 25 | import android.content.pm.ActivityInfo; |
Sunny Goyal | 3e9be43 | 2017-01-05 15:22:41 -0800 | [diff] [blame] | 26 | import android.content.pm.LauncherActivityInfo; |
Winson Chung | c3a747a | 2012-02-29 10:56:19 -0800 | [diff] [blame] | 27 | import android.content.pm.PackageManager; |
Michael Jurka | 48c7a93 | 2013-05-14 20:17:58 +0200 | [diff] [blame] | 28 | import android.graphics.Bitmap; |
| 29 | import android.graphics.BitmapFactory; |
Sunny Goyal | 91498ab | 2017-10-05 15:57:40 -0700 | [diff] [blame] | 30 | import android.os.Handler; |
Sunny Goyal | 1cc1c9a | 2017-01-06 16:32:57 -0800 | [diff] [blame] | 31 | import android.os.Looper; |
Sunny Goyal | 91498ab | 2017-10-05 15:57:40 -0700 | [diff] [blame] | 32 | import android.os.Message; |
Sunny Goyal | aaf86fe | 2017-01-05 21:50:27 -0800 | [diff] [blame] | 33 | import android.os.Parcelable; |
Sunny Goyal | 7c74e4a | 2016-12-15 15:53:17 -0800 | [diff] [blame] | 34 | import android.os.Process; |
| 35 | import android.os.UserHandle; |
Nilesh Agrawal | dff0bfe | 2013-12-17 15:20:50 -0800 | [diff] [blame] | 36 | import android.text.TextUtils; |
Michael Jurka | 48c7a93 | 2013-05-14 20:17:58 +0200 | [diff] [blame] | 37 | import android.util.Base64; |
| 38 | import android.util.Log; |
Sunny Goyal | a474a9b | 2017-05-04 16:47:11 -0700 | [diff] [blame] | 39 | import android.util.Pair; |
The Android Open Source Project | 31dd503 | 2009-03-03 19:32:27 -0800 | [diff] [blame] | 40 | |
Sunny Goyal | e0f58d7 | 2014-11-10 18:05:31 -0800 | [diff] [blame] | 41 | import com.android.launcher3.compat.LauncherAppsCompat; |
Sunny Goyal | e0f58d7 | 2014-11-10 18:05:31 -0800 | [diff] [blame] | 42 | import com.android.launcher3.compat.UserManagerCompat; |
Hyunyoung Song | 48cb7bc | 2018-09-25 17:03:34 -0700 | [diff] [blame] | 43 | import com.android.launcher3.icons.BitmapInfo; |
Sunny Goyal | e62d2bb | 2018-11-06 10:28:37 -0800 | [diff] [blame] | 44 | import com.android.launcher3.icons.GraphicsUtils; |
Hyunyoung Song | 48cb7bc | 2018-09-25 17:03:34 -0700 | [diff] [blame] | 45 | import com.android.launcher3.icons.LauncherIcons; |
Sunny Goyal | f75baa9 | 2016-11-22 03:23:51 +0530 | [diff] [blame] | 46 | import com.android.launcher3.shortcuts.DeepShortcutManager; |
| 47 | import com.android.launcher3.shortcuts.ShortcutInfoCompat; |
| 48 | import com.android.launcher3.shortcuts.ShortcutKey; |
Sunny Goyal | fb5096d | 2016-09-08 14:32:06 -0700 | [diff] [blame] | 49 | import com.android.launcher3.util.PackageManagerHelper; |
Sunny Goyal | 2bcbe13 | 2016-11-16 09:23:42 -0800 | [diff] [blame] | 50 | import com.android.launcher3.util.Preconditions; |
Adam Cohen | 091440a | 2015-03-18 14:16:05 -0700 | [diff] [blame] | 51 | import com.android.launcher3.util.Thunk; |
Adam Cohen | a28b78e | 2014-05-20 17:03:04 -0700 | [diff] [blame] | 52 | |
Sunny Goyal | e0f58d7 | 2014-11-10 18:05:31 -0800 | [diff] [blame] | 53 | import org.json.JSONException; |
Michael Jurka | 34c2e6c | 2013-12-13 16:07:45 +0100 | [diff] [blame] | 54 | import org.json.JSONObject; |
| 55 | import org.json.JSONStringer; |
Michael Jurka | 34c2e6c | 2013-12-13 16:07:45 +0100 | [diff] [blame] | 56 | |
Sunny Goyal | e0f58d7 | 2014-11-10 18:05:31 -0800 | [diff] [blame] | 57 | import java.net.URISyntaxException; |
Winson Chung | e428e29 | 2012-01-10 16:20:33 -0800 | [diff] [blame] | 58 | import java.util.ArrayList; |
Sunny Goyal | f75baa9 | 2016-11-22 03:23:51 +0530 | [diff] [blame] | 59 | import java.util.Arrays; |
Winson Chung | f0c6ae0 | 2012-03-21 16:10:31 -0700 | [diff] [blame] | 60 | import java.util.HashSet; |
Winson Chung | f561bdf | 2012-05-03 11:20:19 -0700 | [diff] [blame] | 61 | import java.util.Iterator; |
Sunny Goyal | 2bcbe13 | 2016-11-16 09:23:42 -0800 | [diff] [blame] | 62 | import java.util.List; |
Winson Chung | f0c6ae0 | 2012-03-21 16:10:31 -0700 | [diff] [blame] | 63 | import java.util.Set; |
Winson Chung | e428e29 | 2012-01-10 16:20:33 -0800 | [diff] [blame] | 64 | |
The Android Open Source Project | 31dd503 | 2009-03-03 19:32:27 -0800 | [diff] [blame] | 65 | public class InstallShortcutReceiver extends BroadcastReceiver { |
Sunny Goyal | a474a9b | 2017-05-04 16:47:11 -0700 | [diff] [blame] | 66 | |
Sunny Goyal | 91498ab | 2017-10-05 15:57:40 -0700 | [diff] [blame] | 67 | private static final int MSG_ADD_TO_QUEUE = 1; |
| 68 | private static final int MSG_FLUSH_QUEUE = 2; |
| 69 | |
Sunny Goyal | a474a9b | 2017-05-04 16:47:11 -0700 | [diff] [blame] | 70 | public static final int FLAG_ACTIVITY_PAUSED = 1; |
| 71 | public static final int FLAG_LOADER_RUNNING = 2; |
| 72 | public static final int FLAG_DRAG_AND_DROP = 4; |
| 73 | public static final int FLAG_BULK_ADD = 4; |
| 74 | |
| 75 | // Determines whether to defer installing shortcuts immediately until |
| 76 | // processAllPendingInstalls() is called. |
| 77 | private static int sInstallQueueDisabledFlags = 0; |
| 78 | |
Bjorn Bringert | 4e871a2 | 2013-10-17 13:50:20 +0100 | [diff] [blame] | 79 | private static final String TAG = "InstallShortcutReceiver"; |
| 80 | private static final boolean DBG = false; |
| 81 | |
Sunny Goyal | 2350bc9 | 2014-10-14 16:42:54 -0700 | [diff] [blame] | 82 | private static final String ACTION_INSTALL_SHORTCUT = |
Winson Chung | 94d6768 | 2013-09-25 16:29:40 -0700 | [diff] [blame] | 83 | "com.android.launcher.action.INSTALL_SHORTCUT"; |
Winson Chung | f0c6ae0 | 2012-03-21 16:10:31 -0700 | [diff] [blame] | 84 | |
Sunny Goyal | 2350bc9 | 2014-10-14 16:42:54 -0700 | [diff] [blame] | 85 | private static final String LAUNCH_INTENT_KEY = "intent.launch"; |
| 86 | private static final String NAME_KEY = "name"; |
| 87 | private static final String ICON_KEY = "icon"; |
| 88 | private static final String ICON_RESOURCE_NAME_KEY = "iconResource"; |
| 89 | private static final String ICON_RESOURCE_PACKAGE_NAME_KEY = "iconResourcePackage"; |
Sunny Goyal | e0f58d7 | 2014-11-10 18:05:31 -0800 | [diff] [blame] | 90 | |
| 91 | private static final String APP_SHORTCUT_TYPE_KEY = "isAppShortcut"; |
Sunny Goyal | f75baa9 | 2016-11-22 03:23:51 +0530 | [diff] [blame] | 92 | private static final String DEEPSHORTCUT_TYPE_KEY = "isDeepShortcut"; |
Sunny Goyal | 3be633b | 2016-12-08 09:59:25 -0800 | [diff] [blame] | 93 | private static final String APP_WIDGET_TYPE_KEY = "isAppWidget"; |
Sunny Goyal | e0f58d7 | 2014-11-10 18:05:31 -0800 | [diff] [blame] | 94 | private static final String USER_HANDLE_KEY = "userHandle"; |
| 95 | |
Michael Jurka | 48c7a93 | 2013-05-14 20:17:58 +0200 | [diff] [blame] | 96 | // The set of shortcuts that are pending install |
Sunny Goyal | 2350bc9 | 2014-10-14 16:42:54 -0700 | [diff] [blame] | 97 | private static final String APPS_PENDING_INSTALL = "apps_to_install"; |
Michael Jurka | 48c7a93 | 2013-05-14 20:17:58 +0200 | [diff] [blame] | 98 | |
Winson Chung | f0c6ae0 | 2012-03-21 16:10:31 -0700 | [diff] [blame] | 99 | public static final int NEW_SHORTCUT_BOUNCE_DURATION = 450; |
Winson Chung | 997a923 | 2013-07-24 15:33:46 -0700 | [diff] [blame] | 100 | public static final int NEW_SHORTCUT_STAGGER_DELAY = 85; |
Winson Chung | f0c6ae0 | 2012-03-21 16:10:31 -0700 | [diff] [blame] | 101 | |
Sunny Goyal | 91498ab | 2017-10-05 15:57:40 -0700 | [diff] [blame] | 102 | private static final Handler sHandler = new Handler(LauncherModel.getWorkerLooper()) { |
Michael Jurka | 48c7a93 | 2013-05-14 20:17:58 +0200 | [diff] [blame] | 103 | |
Sunny Goyal | 91498ab | 2017-10-05 15:57:40 -0700 | [diff] [blame] | 104 | @Override |
| 105 | public void handleMessage(Message msg) { |
| 106 | switch (msg.what) { |
| 107 | case MSG_ADD_TO_QUEUE: { |
| 108 | Pair<Context, PendingInstallShortcutInfo> pair = |
| 109 | (Pair<Context, PendingInstallShortcutInfo>) msg.obj; |
| 110 | String encoded = pair.second.encodeToString(); |
| 111 | SharedPreferences prefs = Utilities.getPrefs(pair.first); |
| 112 | Set<String> strings = prefs.getStringSet(APPS_PENDING_INSTALL, null); |
| 113 | strings = (strings != null) ? new HashSet<>(strings) : new HashSet<String>(1); |
| 114 | strings.add(encoded); |
| 115 | prefs.edit().putStringSet(APPS_PENDING_INSTALL, strings).apply(); |
| 116 | return; |
| 117 | } |
| 118 | case MSG_FLUSH_QUEUE: { |
| 119 | Context context = (Context) msg.obj; |
| 120 | LauncherModel model = LauncherAppState.getInstance(context).getModel(); |
| 121 | if (model.getCallback() == null) { |
| 122 | // Launcher not loaded |
| 123 | return; |
| 124 | } |
| 125 | |
| 126 | ArrayList<Pair<ItemInfo, Object>> installQueue = new ArrayList<>(); |
| 127 | SharedPreferences prefs = Utilities.getPrefs(context); |
| 128 | Set<String> strings = prefs.getStringSet(APPS_PENDING_INSTALL, null); |
| 129 | if (DBG) Log.d(TAG, "Getting and clearing APPS_PENDING_INSTALL: " + strings); |
| 130 | if (strings == null) { |
| 131 | return; |
| 132 | } |
| 133 | |
| 134 | LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context); |
| 135 | for (String encoded : strings) { |
| 136 | PendingInstallShortcutInfo info = decode(encoded, context); |
| 137 | if (info == null) { |
| 138 | continue; |
| 139 | } |
| 140 | |
| 141 | String pkg = getIntentPackage(info.launchIntent); |
| 142 | if (!TextUtils.isEmpty(pkg) |
| 143 | && !launcherApps.isPackageEnabledForProfile(pkg, info.user)) { |
| 144 | if (DBG) Log.d(TAG, "Ignoring shortcut for absent package: " |
| 145 | + info.launchIntent); |
| 146 | continue; |
| 147 | } |
| 148 | |
| 149 | // Generate a shortcut info to add into the model |
| 150 | installQueue.add(info.getItemInfo()); |
| 151 | } |
| 152 | prefs.edit().remove(APPS_PENDING_INSTALL).apply(); |
| 153 | if (!installQueue.isEmpty()) { |
| 154 | model.addAndBindAddedWorkspaceItems(installQueue); |
| 155 | } |
| 156 | return; |
| 157 | } |
Michael Jurka | 48c7a93 | 2013-05-14 20:17:58 +0200 | [diff] [blame] | 158 | } |
| 159 | } |
Sunny Goyal | 91498ab | 2017-10-05 15:57:40 -0700 | [diff] [blame] | 160 | }; |
Michael Jurka | 48c7a93 | 2013-05-14 20:17:58 +0200 | [diff] [blame] | 161 | |
Sunny Goyal | 3bbbabc | 2016-03-15 09:16:30 -0700 | [diff] [blame] | 162 | public static void removeFromInstallQueue(Context context, HashSet<String> packageNames, |
Sunny Goyal | 7c74e4a | 2016-12-15 15:53:17 -0800 | [diff] [blame] | 163 | UserHandle user) { |
Winson Chung | df95eb1 | 2013-10-16 14:57:07 -0700 | [diff] [blame] | 164 | if (packageNames.isEmpty()) { |
| 165 | return; |
| 166 | } |
Sunny Goyal | 91498ab | 2017-10-05 15:57:40 -0700 | [diff] [blame] | 167 | Preconditions.assertWorkerThread(); |
| 168 | |
Sunny Goyal | f725824 | 2015-10-19 16:59:07 -0700 | [diff] [blame] | 169 | SharedPreferences sp = Utilities.getPrefs(context); |
Sunny Goyal | 91498ab | 2017-10-05 15:57:40 -0700 | [diff] [blame] | 170 | Set<String> strings = sp.getStringSet(APPS_PENDING_INSTALL, null); |
| 171 | if (DBG) { |
| 172 | Log.d(TAG, "APPS_PENDING_INSTALL: " + strings |
| 173 | + ", removing packages: " + packageNames); |
| 174 | } |
| 175 | if (Utilities.isEmpty(strings)) { |
| 176 | return; |
| 177 | } |
| 178 | Set<String> newStrings = new HashSet<>(strings); |
| 179 | Iterator<String> newStringsIter = newStrings.iterator(); |
| 180 | while (newStringsIter.hasNext()) { |
| 181 | String encoded = newStringsIter.next(); |
| 182 | try { |
| 183 | Decoder decoder = new Decoder(encoded, context); |
| 184 | if (packageNames.contains(getIntentPackage(decoder.launcherIntent)) && |
| 185 | user.equals(decoder.user)) { |
Sunny Goyal | f75baa9 | 2016-11-22 03:23:51 +0530 | [diff] [blame] | 186 | newStringsIter.remove(); |
Winson Chung | 780fe59 | 2013-09-26 14:48:44 -0700 | [diff] [blame] | 187 | } |
Sunny Goyal | 91498ab | 2017-10-05 15:57:40 -0700 | [diff] [blame] | 188 | } catch (JSONException | URISyntaxException e) { |
| 189 | Log.d(TAG, "Exception reading shortcut to add: " + e); |
| 190 | newStringsIter.remove(); |
Winson Chung | 780fe59 | 2013-09-26 14:48:44 -0700 | [diff] [blame] | 191 | } |
| 192 | } |
Sunny Goyal | 91498ab | 2017-10-05 15:57:40 -0700 | [diff] [blame] | 193 | sp.edit().putStringSet(APPS_PENDING_INSTALL, newStrings).apply(); |
Michael Jurka | 48c7a93 | 2013-05-14 20:17:58 +0200 | [diff] [blame] | 194 | } |
| 195 | |
The Android Open Source Project | 31dd503 | 2009-03-03 19:32:27 -0800 | [diff] [blame] | 196 | public void onReceive(Context context, Intent data) { |
Romain Guy | 51ed5b9 | 2009-06-17 10:20:34 -0700 | [diff] [blame] | 197 | if (!ACTION_INSTALL_SHORTCUT.equals(data.getAction())) { |
| 198 | return; |
| 199 | } |
Sunny Goyal | 7606a41 | 2015-12-07 14:51:23 -0800 | [diff] [blame] | 200 | PendingInstallShortcutInfo info = createPendingInfo(context, data); |
| 201 | if (info != null) { |
Sunny Goyal | fb5096d | 2016-09-08 14:32:06 -0700 | [diff] [blame] | 202 | if (!info.isLauncherActivity()) { |
| 203 | // Since its a custom shortcut, verify that it is safe to launch. |
Sunny Goyal | 342e466 | 2017-02-02 15:21:08 -0800 | [diff] [blame] | 204 | if (!new PackageManagerHelper(context).hasPermissionForActivity( |
| 205 | info.launchIntent, null)) { |
Sunny Goyal | fb5096d | 2016-09-08 14:32:06 -0700 | [diff] [blame] | 206 | // Target cannot be launched, or requires some special permission to launch |
| 207 | Log.e(TAG, "Ignoring malicious intent " + info.launchIntent.toUri(0)); |
| 208 | return; |
| 209 | } |
| 210 | } |
Sunny Goyal | 7606a41 | 2015-12-07 14:51:23 -0800 | [diff] [blame] | 211 | queuePendingShortcutInfo(info, context); |
Sunny Goyal | 3290ec4 | 2015-05-04 13:53:42 -0700 | [diff] [blame] | 212 | } |
Sunny Goyal | e0f58d7 | 2014-11-10 18:05:31 -0800 | [diff] [blame] | 213 | } |
Nilesh Agrawal | dff0bfe | 2013-12-17 15:20:50 -0800 | [diff] [blame] | 214 | |
Sunny Goyal | 7606a41 | 2015-12-07 14:51:23 -0800 | [diff] [blame] | 215 | /** |
| 216 | * @return true is the extra is either null or is of type {@param type} |
| 217 | */ |
| 218 | private static boolean isValidExtraType(Intent intent, String key, Class type) { |
| 219 | Object extra = intent.getParcelableExtra(key); |
| 220 | return extra == null || type.isInstance(extra); |
| 221 | } |
| 222 | |
| 223 | /** |
| 224 | * Verifies the intent and creates a {@link PendingInstallShortcutInfo} |
| 225 | */ |
| 226 | private static PendingInstallShortcutInfo createPendingInfo(Context context, Intent data) { |
| 227 | if (!isValidExtraType(data, Intent.EXTRA_SHORTCUT_INTENT, Intent.class) || |
| 228 | !(isValidExtraType(data, Intent.EXTRA_SHORTCUT_ICON_RESOURCE, |
| 229 | Intent.ShortcutIconResource.class)) || |
| 230 | !(isValidExtraType(data, Intent.EXTRA_SHORTCUT_ICON, Bitmap.class))) { |
| 231 | |
| 232 | if (DBG) Log.e(TAG, "Invalid install shortcut intent"); |
| 233 | return null; |
| 234 | } |
| 235 | |
Sunny Goyal | f75baa9 | 2016-11-22 03:23:51 +0530 | [diff] [blame] | 236 | PendingInstallShortcutInfo info = new PendingInstallShortcutInfo( |
Sunny Goyal | 7c74e4a | 2016-12-15 15:53:17 -0800 | [diff] [blame] | 237 | data, Process.myUserHandle(), context); |
Sunny Goyal | 5c97f51 | 2015-05-19 16:03:28 -0700 | [diff] [blame] | 238 | if (info.launchIntent == null || info.label == null) { |
| 239 | if (DBG) Log.e(TAG, "Invalid install shortcut intent"); |
| 240 | return null; |
| 241 | } |
Sunny Goyal | 7606a41 | 2015-12-07 14:51:23 -0800 | [diff] [blame] | 242 | |
| 243 | return convertToLauncherActivityIfPossible(info); |
Sunny Goyal | 5c97f51 | 2015-05-19 16:03:28 -0700 | [diff] [blame] | 244 | } |
| 245 | |
Sunny Goyal | 7606a41 | 2015-12-07 14:51:23 -0800 | [diff] [blame] | 246 | public static ShortcutInfo fromShortcutIntent(Context context, Intent data) { |
| 247 | PendingInstallShortcutInfo info = createPendingInfo(context, data); |
Sunny Goyal | a474a9b | 2017-05-04 16:47:11 -0700 | [diff] [blame] | 248 | return info == null ? null : (ShortcutInfo) info.getItemInfo().first; |
Sunny Goyal | e0f58d7 | 2014-11-10 18:05:31 -0800 | [diff] [blame] | 249 | } |
Michael Jurka | 48c7a93 | 2013-05-14 20:17:58 +0200 | [diff] [blame] | 250 | |
Sunny Goyal | 027fba3 | 2017-06-19 15:30:19 -0700 | [diff] [blame] | 251 | public static ShortcutInfo fromActivityInfo(LauncherActivityInfo info, Context context) { |
| 252 | return (ShortcutInfo) (new PendingInstallShortcutInfo(info, context).getItemInfo().first); |
| 253 | } |
| 254 | |
Sunny Goyal | f75baa9 | 2016-11-22 03:23:51 +0530 | [diff] [blame] | 255 | public static void queueShortcut(ShortcutInfoCompat info, Context context) { |
| 256 | queuePendingShortcutInfo(new PendingInstallShortcutInfo(info, context), context); |
| 257 | } |
| 258 | |
Sunny Goyal | 3be633b | 2016-12-08 09:59:25 -0800 | [diff] [blame] | 259 | public static void queueWidget(AppWidgetProviderInfo info, int widgetId, Context context) { |
| 260 | queuePendingShortcutInfo(new PendingInstallShortcutInfo(info, widgetId, context), context); |
| 261 | } |
| 262 | |
Sunny Goyal | 4179e9b | 2017-03-08 14:25:09 -0800 | [diff] [blame] | 263 | public static void queueActivityInfo(LauncherActivityInfo activity, Context context) { |
| 264 | queuePendingShortcutInfo(new PendingInstallShortcutInfo(activity, context), context); |
| 265 | } |
| 266 | |
Sunny Goyal | f75baa9 | 2016-11-22 03:23:51 +0530 | [diff] [blame] | 267 | public static HashSet<ShortcutKey> getPendingShortcuts(Context context) { |
| 268 | HashSet<ShortcutKey> result = new HashSet<>(); |
| 269 | |
| 270 | Set<String> strings = Utilities.getPrefs(context).getStringSet(APPS_PENDING_INSTALL, null); |
| 271 | if (Utilities.isEmpty(strings)) { |
| 272 | return result; |
| 273 | } |
| 274 | |
| 275 | for (String encoded : strings) { |
| 276 | try { |
| 277 | Decoder decoder = new Decoder(encoded, context); |
| 278 | if (decoder.optBoolean(DEEPSHORTCUT_TYPE_KEY)) { |
| 279 | result.add(ShortcutKey.fromIntent(decoder.launcherIntent, decoder.user)); |
| 280 | } |
| 281 | } catch (JSONException | URISyntaxException e) { |
| 282 | Log.d(TAG, "Exception reading shortcut to add: " + e); |
| 283 | } |
| 284 | } |
| 285 | return result; |
| 286 | } |
| 287 | |
Sunny Goyal | e0f58d7 | 2014-11-10 18:05:31 -0800 | [diff] [blame] | 288 | private static void queuePendingShortcutInfo(PendingInstallShortcutInfo info, Context context) { |
Winson Chung | de0fb8f | 2012-05-08 14:37:08 -0700 | [diff] [blame] | 289 | // Queue the item up for adding if launcher has not loaded properly yet |
Sunny Goyal | 91498ab | 2017-10-05 15:57:40 -0700 | [diff] [blame] | 290 | Message.obtain(sHandler, MSG_ADD_TO_QUEUE, Pair.create(context, info)).sendToTarget(); |
Sunny Goyal | a474a9b | 2017-05-04 16:47:11 -0700 | [diff] [blame] | 291 | flushInstallQueue(context); |
Winson Chung | f561bdf | 2012-05-03 11:20:19 -0700 | [diff] [blame] | 292 | } |
| 293 | |
Sunny Goyal | a474a9b | 2017-05-04 16:47:11 -0700 | [diff] [blame] | 294 | public static void enableInstallQueue(int flag) { |
| 295 | sInstallQueueDisabledFlags |= flag; |
Winson Chung | f561bdf | 2012-05-03 11:20:19 -0700 | [diff] [blame] | 296 | } |
Sunny Goyal | a474a9b | 2017-05-04 16:47:11 -0700 | [diff] [blame] | 297 | public static void disableAndFlushInstallQueue(int flag, Context context) { |
| 298 | sInstallQueueDisabledFlags &= ~flag; |
Winson Chung | de0fb8f | 2012-05-08 14:37:08 -0700 | [diff] [blame] | 299 | flushInstallQueue(context); |
| 300 | } |
Sunny Goyal | 2bcbe13 | 2016-11-16 09:23:42 -0800 | [diff] [blame] | 301 | |
Winson Chung | de0fb8f | 2012-05-08 14:37:08 -0700 | [diff] [blame] | 302 | static void flushInstallQueue(Context context) { |
Sunny Goyal | 91498ab | 2017-10-05 15:57:40 -0700 | [diff] [blame] | 303 | if (sInstallQueueDisabledFlags != 0) { |
Sunny Goyal | a474a9b | 2017-05-04 16:47:11 -0700 | [diff] [blame] | 304 | return; |
| 305 | } |
Sunny Goyal | 91498ab | 2017-10-05 15:57:40 -0700 | [diff] [blame] | 306 | Message.obtain(sHandler, MSG_FLUSH_QUEUE, context.getApplicationContext()).sendToTarget(); |
Winson Chung | f561bdf | 2012-05-03 11:20:19 -0700 | [diff] [blame] | 307 | } |
| 308 | |
Nilesh Agrawal | dff0bfe | 2013-12-17 15:20:50 -0800 | [diff] [blame] | 309 | /** |
Winson Chung | 2c1afde | 2013-10-17 11:03:24 -0700 | [diff] [blame] | 310 | * Ensures that we have a valid, non-null name. If the provided name is null, we will return |
| 311 | * the application name instead. |
| 312 | */ |
Adam Cohen | 091440a | 2015-03-18 14:16:05 -0700 | [diff] [blame] | 313 | @Thunk static CharSequence ensureValidName(Context context, Intent intent, CharSequence name) { |
Winson Chung | 2c1afde | 2013-10-17 11:03:24 -0700 | [diff] [blame] | 314 | if (name == null) { |
| 315 | try { |
| 316 | PackageManager pm = context.getPackageManager(); |
| 317 | ActivityInfo info = pm.getActivityInfo(intent.getComponent(), 0); |
Winson Chung | 82b016c | 2015-05-08 17:00:10 -0700 | [diff] [blame] | 318 | name = info.loadLabel(pm); |
Winson Chung | 2c1afde | 2013-10-17 11:03:24 -0700 | [diff] [blame] | 319 | } catch (PackageManager.NameNotFoundException nnfe) { |
| 320 | return ""; |
| 321 | } |
| 322 | } |
| 323 | return name; |
The Android Open Source Project | 31dd503 | 2009-03-03 19:32:27 -0800 | [diff] [blame] | 324 | } |
Sunny Goyal | e0f58d7 | 2014-11-10 18:05:31 -0800 | [diff] [blame] | 325 | |
| 326 | private static class PendingInstallShortcutInfo { |
| 327 | |
Sunny Goyal | 3e9be43 | 2017-01-05 15:22:41 -0800 | [diff] [blame] | 328 | final LauncherActivityInfo activityInfo; |
Sunny Goyal | f75baa9 | 2016-11-22 03:23:51 +0530 | [diff] [blame] | 329 | final ShortcutInfoCompat shortcutInfo; |
Sunny Goyal | 3be633b | 2016-12-08 09:59:25 -0800 | [diff] [blame] | 330 | final AppWidgetProviderInfo providerInfo; |
Sunny Goyal | e0f58d7 | 2014-11-10 18:05:31 -0800 | [diff] [blame] | 331 | |
| 332 | final Intent data; |
| 333 | final Context mContext; |
| 334 | final Intent launchIntent; |
| 335 | final String label; |
Sunny Goyal | 7c74e4a | 2016-12-15 15:53:17 -0800 | [diff] [blame] | 336 | final UserHandle user; |
Sunny Goyal | e0f58d7 | 2014-11-10 18:05:31 -0800 | [diff] [blame] | 337 | |
| 338 | /** |
| 339 | * Initializes a PendingInstallShortcutInfo received from a different app. |
| 340 | */ |
Sunny Goyal | 7c74e4a | 2016-12-15 15:53:17 -0800 | [diff] [blame] | 341 | public PendingInstallShortcutInfo(Intent data, UserHandle user, Context context) { |
Sunny Goyal | 3be633b | 2016-12-08 09:59:25 -0800 | [diff] [blame] | 342 | activityInfo = null; |
| 343 | shortcutInfo = null; |
| 344 | providerInfo = null; |
| 345 | |
Sunny Goyal | e0f58d7 | 2014-11-10 18:05:31 -0800 | [diff] [blame] | 346 | this.data = data; |
Sunny Goyal | f75baa9 | 2016-11-22 03:23:51 +0530 | [diff] [blame] | 347 | this.user = user; |
Sunny Goyal | e0f58d7 | 2014-11-10 18:05:31 -0800 | [diff] [blame] | 348 | mContext = context; |
| 349 | |
| 350 | launchIntent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT); |
| 351 | label = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME); |
Sunny Goyal | 3be633b | 2016-12-08 09:59:25 -0800 | [diff] [blame] | 352 | |
Sunny Goyal | e0f58d7 | 2014-11-10 18:05:31 -0800 | [diff] [blame] | 353 | } |
| 354 | |
| 355 | /** |
| 356 | * Initializes a PendingInstallShortcutInfo to represent a launcher target. |
| 357 | */ |
Sunny Goyal | 3e9be43 | 2017-01-05 15:22:41 -0800 | [diff] [blame] | 358 | public PendingInstallShortcutInfo(LauncherActivityInfo info, Context context) { |
Sunny Goyal | e0f58d7 | 2014-11-10 18:05:31 -0800 | [diff] [blame] | 359 | activityInfo = info; |
Sunny Goyal | f75baa9 | 2016-11-22 03:23:51 +0530 | [diff] [blame] | 360 | shortcutInfo = null; |
Sunny Goyal | 3be633b | 2016-12-08 09:59:25 -0800 | [diff] [blame] | 361 | providerInfo = null; |
| 362 | |
| 363 | data = null; |
Sunny Goyal | e0f58d7 | 2014-11-10 18:05:31 -0800 | [diff] [blame] | 364 | user = info.getUser(); |
Sunny Goyal | 3be633b | 2016-12-08 09:59:25 -0800 | [diff] [blame] | 365 | mContext = context; |
Sunny Goyal | e0f58d7 | 2014-11-10 18:05:31 -0800 | [diff] [blame] | 366 | |
Sunny Goyal | 24bb66a | 2017-03-21 15:12:01 -0700 | [diff] [blame] | 367 | launchIntent = AppInfo.makeLaunchIntent(info); |
Sunny Goyal | e0f58d7 | 2014-11-10 18:05:31 -0800 | [diff] [blame] | 368 | label = info.getLabel().toString(); |
| 369 | } |
| 370 | |
Sunny Goyal | f75baa9 | 2016-11-22 03:23:51 +0530 | [diff] [blame] | 371 | /** |
| 372 | * Initializes a PendingInstallShortcutInfo to represent a launcher target. |
| 373 | */ |
| 374 | public PendingInstallShortcutInfo(ShortcutInfoCompat info, Context context) { |
Sunny Goyal | f75baa9 | 2016-11-22 03:23:51 +0530 | [diff] [blame] | 375 | activityInfo = null; |
Sunny Goyal | 3be633b | 2016-12-08 09:59:25 -0800 | [diff] [blame] | 376 | shortcutInfo = info; |
| 377 | providerInfo = null; |
| 378 | |
| 379 | data = null; |
| 380 | mContext = context; |
Sunny Goyal | f75baa9 | 2016-11-22 03:23:51 +0530 | [diff] [blame] | 381 | user = info.getUserHandle(); |
| 382 | |
Sunny Goyal | 24bb66a | 2017-03-21 15:12:01 -0700 | [diff] [blame] | 383 | launchIntent = info.makeIntent(); |
Sunny Goyal | f75baa9 | 2016-11-22 03:23:51 +0530 | [diff] [blame] | 384 | label = info.getShortLabel().toString(); |
| 385 | } |
| 386 | |
Sunny Goyal | 3be633b | 2016-12-08 09:59:25 -0800 | [diff] [blame] | 387 | /** |
| 388 | * Initializes a PendingInstallShortcutInfo to represent a launcher target. |
| 389 | */ |
| 390 | public PendingInstallShortcutInfo( |
| 391 | AppWidgetProviderInfo info, int widgetId, Context context) { |
| 392 | activityInfo = null; |
| 393 | shortcutInfo = null; |
| 394 | providerInfo = info; |
| 395 | |
| 396 | data = null; |
| 397 | mContext = context; |
Sunny Goyal | 7c74e4a | 2016-12-15 15:53:17 -0800 | [diff] [blame] | 398 | user = info.getProfile(); |
Sunny Goyal | 3be633b | 2016-12-08 09:59:25 -0800 | [diff] [blame] | 399 | |
| 400 | launchIntent = new Intent().setComponent(info.provider) |
| 401 | .putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId); |
| 402 | label = info.label; |
| 403 | } |
| 404 | |
Sunny Goyal | e0f58d7 | 2014-11-10 18:05:31 -0800 | [diff] [blame] | 405 | public String encodeToString() { |
Sunny Goyal | f75baa9 | 2016-11-22 03:23:51 +0530 | [diff] [blame] | 406 | try { |
| 407 | if (activityInfo != null) { |
Sunny Goyal | e0f58d7 | 2014-11-10 18:05:31 -0800 | [diff] [blame] | 408 | // If it a launcher target, we only need component name, and user to |
| 409 | // recreate this. |
| 410 | return new JSONStringer() |
| 411 | .object() |
| 412 | .key(LAUNCH_INTENT_KEY).value(launchIntent.toUri(0)) |
| 413 | .key(APP_SHORTCUT_TYPE_KEY).value(true) |
| 414 | .key(USER_HANDLE_KEY).value(UserManagerCompat.getInstance(mContext) |
| 415 | .getSerialNumberForUser(user)) |
| 416 | .endObject().toString(); |
Sunny Goyal | f75baa9 | 2016-11-22 03:23:51 +0530 | [diff] [blame] | 417 | } else if (shortcutInfo != null) { |
| 418 | // If it a launcher target, we only need component name, and user to |
| 419 | // recreate this. |
| 420 | return new JSONStringer() |
| 421 | .object() |
| 422 | .key(LAUNCH_INTENT_KEY).value(launchIntent.toUri(0)) |
| 423 | .key(DEEPSHORTCUT_TYPE_KEY).value(true) |
| 424 | .key(USER_HANDLE_KEY).value(UserManagerCompat.getInstance(mContext) |
| 425 | .getSerialNumberForUser(user)) |
| 426 | .endObject().toString(); |
Sunny Goyal | 3be633b | 2016-12-08 09:59:25 -0800 | [diff] [blame] | 427 | } else if (providerInfo != null) { |
| 428 | // If it a launcher target, we only need component name, and user to |
| 429 | // recreate this. |
| 430 | return new JSONStringer() |
| 431 | .object() |
| 432 | .key(LAUNCH_INTENT_KEY).value(launchIntent.toUri(0)) |
| 433 | .key(APP_WIDGET_TYPE_KEY).value(true) |
| 434 | .key(USER_HANDLE_KEY).value(UserManagerCompat.getInstance(mContext) |
| 435 | .getSerialNumberForUser(user)) |
| 436 | .endObject().toString(); |
Sunny Goyal | e0f58d7 | 2014-11-10 18:05:31 -0800 | [diff] [blame] | 437 | } |
Sunny Goyal | e0f58d7 | 2014-11-10 18:05:31 -0800 | [diff] [blame] | 438 | |
Sunny Goyal | f75baa9 | 2016-11-22 03:23:51 +0530 | [diff] [blame] | 439 | if (launchIntent.getAction() == null) { |
| 440 | launchIntent.setAction(Intent.ACTION_VIEW); |
| 441 | } else if (launchIntent.getAction().equals(Intent.ACTION_MAIN) && |
| 442 | launchIntent.getCategories() != null && |
| 443 | launchIntent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) { |
| 444 | launchIntent.addFlags( |
| 445 | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); |
| 446 | } |
Sunny Goyal | e0f58d7 | 2014-11-10 18:05:31 -0800 | [diff] [blame] | 447 | |
Sunny Goyal | f75baa9 | 2016-11-22 03:23:51 +0530 | [diff] [blame] | 448 | // This name is only used for comparisons and notifications, so fall back to activity |
| 449 | // name if not supplied |
| 450 | String name = ensureValidName(mContext, launchIntent, label).toString(); |
| 451 | Bitmap icon = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON); |
| 452 | Intent.ShortcutIconResource iconResource = |
| 453 | data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE); |
Sunny Goyal | e0f58d7 | 2014-11-10 18:05:31 -0800 | [diff] [blame] | 454 | |
Sunny Goyal | f75baa9 | 2016-11-22 03:23:51 +0530 | [diff] [blame] | 455 | // Only encode the parameters which are supported by the API. |
Sunny Goyal | e0f58d7 | 2014-11-10 18:05:31 -0800 | [diff] [blame] | 456 | JSONStringer json = new JSONStringer() |
| 457 | .object() |
| 458 | .key(LAUNCH_INTENT_KEY).value(launchIntent.toUri(0)) |
| 459 | .key(NAME_KEY).value(name); |
| 460 | if (icon != null) { |
Sunny Goyal | e62d2bb | 2018-11-06 10:28:37 -0800 | [diff] [blame] | 461 | byte[] iconByteArray = GraphicsUtils.flattenBitmap(icon); |
Sunny Goyal | e0f58d7 | 2014-11-10 18:05:31 -0800 | [diff] [blame] | 462 | json = json.key(ICON_KEY).value( |
| 463 | Base64.encodeToString( |
| 464 | iconByteArray, 0, iconByteArray.length, Base64.DEFAULT)); |
| 465 | } |
| 466 | if (iconResource != null) { |
| 467 | json = json.key(ICON_RESOURCE_NAME_KEY).value(iconResource.resourceName); |
| 468 | json = json.key(ICON_RESOURCE_PACKAGE_NAME_KEY) |
| 469 | .value(iconResource.packageName); |
| 470 | } |
| 471 | return json.endObject().toString(); |
| 472 | } catch (JSONException e) { |
| 473 | Log.d(TAG, "Exception when adding shortcut: " + e); |
Sunny Goyal | f75baa9 | 2016-11-22 03:23:51 +0530 | [diff] [blame] | 474 | return null; |
Sunny Goyal | e0f58d7 | 2014-11-10 18:05:31 -0800 | [diff] [blame] | 475 | } |
Sunny Goyal | e0f58d7 | 2014-11-10 18:05:31 -0800 | [diff] [blame] | 476 | } |
| 477 | |
Sunny Goyal | a474a9b | 2017-05-04 16:47:11 -0700 | [diff] [blame] | 478 | public Pair<ItemInfo, Object> getItemInfo() { |
Sunny Goyal | e0f58d7 | 2014-11-10 18:05:31 -0800 | [diff] [blame] | 479 | if (activityInfo != null) { |
Sunny Goyal | 1cc1c9a | 2017-01-06 16:32:57 -0800 | [diff] [blame] | 480 | AppInfo appInfo = new AppInfo(mContext, activityInfo, user); |
Sunny Goyal | 87f784c | 2017-01-11 10:48:34 -0800 | [diff] [blame] | 481 | final LauncherAppState app = LauncherAppState.getInstance(mContext); |
Sunny Goyal | 1cc1c9a | 2017-01-06 16:32:57 -0800 | [diff] [blame] | 482 | // Set default values until proper values is loaded. |
| 483 | appInfo.title = ""; |
Hyunyoung Song | cda96a5 | 2018-10-18 15:05:45 -0700 | [diff] [blame] | 484 | appInfo.applyFrom(app.getIconCache().getDefaultIcon(user)); |
Sunny Goyal | 1cc1c9a | 2017-01-06 16:32:57 -0800 | [diff] [blame] | 485 | final ShortcutInfo si = appInfo.makeShortcut(); |
| 486 | if (Looper.myLooper() == LauncherModel.getWorkerLooper()) { |
| 487 | app.getIconCache().getTitleAndIcon(si, activityInfo, false /* useLowResIcon */); |
| 488 | } else { |
Sunny Goyal | 83fd25e | 2018-07-09 16:47:01 -0700 | [diff] [blame] | 489 | app.getModel().updateAndBindShortcutInfo(() -> { |
| 490 | app.getIconCache().getTitleAndIcon( |
| 491 | si, activityInfo, false /* useLowResIcon */); |
| 492 | return si; |
Sunny Goyal | 1cc1c9a | 2017-01-06 16:32:57 -0800 | [diff] [blame] | 493 | }); |
| 494 | } |
Sunny Goyal | a474a9b | 2017-05-04 16:47:11 -0700 | [diff] [blame] | 495 | return Pair.create((ItemInfo) si, (Object) activityInfo); |
Sunny Goyal | f75baa9 | 2016-11-22 03:23:51 +0530 | [diff] [blame] | 496 | } else if (shortcutInfo != null) { |
Sunny Goyal | 1b07263 | 2017-01-18 11:30:23 -0800 | [diff] [blame] | 497 | ShortcutInfo si = new ShortcutInfo(shortcutInfo, mContext); |
Sunny Goyal | 18a4e5a | 2018-01-09 15:34:38 -0800 | [diff] [blame] | 498 | LauncherIcons li = LauncherIcons.obtain(mContext); |
Hyunyoung Song | cda96a5 | 2018-10-18 15:05:45 -0700 | [diff] [blame] | 499 | si.applyFrom(li.createShortcutIcon(shortcutInfo)); |
Sunny Goyal | 18a4e5a | 2018-01-09 15:34:38 -0800 | [diff] [blame] | 500 | li.recycle(); |
Sunny Goyal | a474a9b | 2017-05-04 16:47:11 -0700 | [diff] [blame] | 501 | return Pair.create((ItemInfo) si, (Object) shortcutInfo); |
Sunny Goyal | 3be633b | 2016-12-08 09:59:25 -0800 | [diff] [blame] | 502 | } else if (providerInfo != null) { |
| 503 | LauncherAppWidgetProviderInfo info = LauncherAppWidgetProviderInfo |
| 504 | .fromProviderInfo(mContext, providerInfo); |
| 505 | LauncherAppWidgetInfo widgetInfo = new LauncherAppWidgetInfo( |
| 506 | launchIntent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, 0), |
| 507 | info.provider); |
Sunny Goyal | 87f784c | 2017-01-11 10:48:34 -0800 | [diff] [blame] | 508 | InvariantDeviceProfile idp = LauncherAppState.getIDP(mContext); |
Sunny Goyal | 3be633b | 2016-12-08 09:59:25 -0800 | [diff] [blame] | 509 | widgetInfo.minSpanX = info.minSpanX; |
| 510 | widgetInfo.minSpanY = info.minSpanY; |
| 511 | widgetInfo.spanX = Math.min(info.spanX, idp.numColumns); |
| 512 | widgetInfo.spanY = Math.min(info.spanY, idp.numRows); |
Sunny Goyal | a474a9b | 2017-05-04 16:47:11 -0700 | [diff] [blame] | 513 | return Pair.create((ItemInfo) widgetInfo, (Object) providerInfo); |
Sunny Goyal | e0f58d7 | 2014-11-10 18:05:31 -0800 | [diff] [blame] | 514 | } else { |
Sunny Goyal | a474a9b | 2017-05-04 16:47:11 -0700 | [diff] [blame] | 515 | ShortcutInfo si = createShortcutInfo(data, LauncherAppState.getInstance(mContext)); |
| 516 | return Pair.create((ItemInfo) si, null); |
Sunny Goyal | e0f58d7 | 2014-11-10 18:05:31 -0800 | [diff] [blame] | 517 | } |
| 518 | } |
| 519 | |
Sunny Goyal | b740f59 | 2015-12-17 23:22:42 -0800 | [diff] [blame] | 520 | public boolean isLauncherActivity() { |
Sunny Goyal | 0b03778 | 2015-04-02 10:27:03 -0700 | [diff] [blame] | 521 | return activityInfo != null; |
| 522 | } |
Sunny Goyal | e0f58d7 | 2014-11-10 18:05:31 -0800 | [diff] [blame] | 523 | } |
| 524 | |
Sunny Goyal | f75baa9 | 2016-11-22 03:23:51 +0530 | [diff] [blame] | 525 | private static String getIntentPackage(Intent intent) { |
| 526 | return intent.getComponent() == null |
| 527 | ? intent.getPackage() : intent.getComponent().getPackageName(); |
| 528 | } |
| 529 | |
Sunny Goyal | e0f58d7 | 2014-11-10 18:05:31 -0800 | [diff] [blame] | 530 | private static PendingInstallShortcutInfo decode(String encoded, Context context) { |
| 531 | try { |
Sunny Goyal | f75baa9 | 2016-11-22 03:23:51 +0530 | [diff] [blame] | 532 | Decoder decoder = new Decoder(encoded, context); |
| 533 | if (decoder.optBoolean(APP_SHORTCUT_TYPE_KEY)) { |
Sunny Goyal | 3e9be43 | 2017-01-05 15:22:41 -0800 | [diff] [blame] | 534 | LauncherActivityInfo info = LauncherAppsCompat.getInstance(context) |
Sunny Goyal | f75baa9 | 2016-11-22 03:23:51 +0530 | [diff] [blame] | 535 | .resolveActivity(decoder.launcherIntent, decoder.user); |
Sunny Goyal | e0f58d7 | 2014-11-10 18:05:31 -0800 | [diff] [blame] | 536 | return info == null ? null : new PendingInstallShortcutInfo(info, context); |
Sunny Goyal | f75baa9 | 2016-11-22 03:23:51 +0530 | [diff] [blame] | 537 | } else if (decoder.optBoolean(DEEPSHORTCUT_TYPE_KEY)) { |
| 538 | DeepShortcutManager sm = DeepShortcutManager.getInstance(context); |
| 539 | List<ShortcutInfoCompat> si = sm.queryForFullDetails( |
| 540 | decoder.launcherIntent.getPackage(), |
Sunny Goyal | 2783595 | 2017-01-13 12:15:53 -0800 | [diff] [blame] | 541 | Arrays.asList(decoder.launcherIntent.getStringExtra( |
| 542 | ShortcutInfoCompat.EXTRA_SHORTCUT_ID)), |
| 543 | decoder.user); |
Sunny Goyal | f75baa9 | 2016-11-22 03:23:51 +0530 | [diff] [blame] | 544 | if (si.isEmpty()) { |
| 545 | return null; |
| 546 | } else { |
| 547 | return new PendingInstallShortcutInfo(si.get(0), context); |
| 548 | } |
Sunny Goyal | 3be633b | 2016-12-08 09:59:25 -0800 | [diff] [blame] | 549 | } else if (decoder.optBoolean(APP_WIDGET_TYPE_KEY)) { |
| 550 | int widgetId = decoder.launcherIntent |
| 551 | .getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, 0); |
| 552 | AppWidgetProviderInfo info = AppWidgetManager.getInstance(context) |
| 553 | .getAppWidgetInfo(widgetId); |
| 554 | if (info == null || !info.provider.equals(decoder.launcherIntent.getComponent()) || |
Sunny Goyal | 7c74e4a | 2016-12-15 15:53:17 -0800 | [diff] [blame] | 555 | !info.getProfile().equals(decoder.user)) { |
Sunny Goyal | 3be633b | 2016-12-08 09:59:25 -0800 | [diff] [blame] | 556 | return null; |
| 557 | } |
| 558 | return new PendingInstallShortcutInfo(info, widgetId, context); |
Sunny Goyal | e0f58d7 | 2014-11-10 18:05:31 -0800 | [diff] [blame] | 559 | } |
| 560 | |
| 561 | Intent data = new Intent(); |
Sunny Goyal | f75baa9 | 2016-11-22 03:23:51 +0530 | [diff] [blame] | 562 | data.putExtra(Intent.EXTRA_SHORTCUT_INTENT, decoder.launcherIntent); |
| 563 | data.putExtra(Intent.EXTRA_SHORTCUT_NAME, decoder.getString(NAME_KEY)); |
Sunny Goyal | e0f58d7 | 2014-11-10 18:05:31 -0800 | [diff] [blame] | 564 | |
Sunny Goyal | f75baa9 | 2016-11-22 03:23:51 +0530 | [diff] [blame] | 565 | String iconBase64 = decoder.optString(ICON_KEY); |
| 566 | String iconResourceName = decoder.optString(ICON_RESOURCE_NAME_KEY); |
| 567 | String iconResourcePackageName = decoder.optString(ICON_RESOURCE_PACKAGE_NAME_KEY); |
Sunny Goyal | e0f58d7 | 2014-11-10 18:05:31 -0800 | [diff] [blame] | 568 | if (iconBase64 != null && !iconBase64.isEmpty()) { |
| 569 | byte[] iconArray = Base64.decode(iconBase64, Base64.DEFAULT); |
| 570 | Bitmap b = BitmapFactory.decodeByteArray(iconArray, 0, iconArray.length); |
| 571 | data.putExtra(Intent.EXTRA_SHORTCUT_ICON, b); |
| 572 | } else if (iconResourceName != null && !iconResourceName.isEmpty()) { |
| 573 | Intent.ShortcutIconResource iconResource = |
| 574 | new Intent.ShortcutIconResource(); |
| 575 | iconResource.resourceName = iconResourceName; |
| 576 | iconResource.packageName = iconResourcePackageName; |
| 577 | data.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconResource); |
| 578 | } |
| 579 | |
Sunny Goyal | f75baa9 | 2016-11-22 03:23:51 +0530 | [diff] [blame] | 580 | return new PendingInstallShortcutInfo(data, decoder.user, context); |
Sunny Goyal | b740f59 | 2015-12-17 23:22:42 -0800 | [diff] [blame] | 581 | } catch (JSONException | URISyntaxException e) { |
Sunny Goyal | e0f58d7 | 2014-11-10 18:05:31 -0800 | [diff] [blame] | 582 | Log.d(TAG, "Exception reading shortcut to add: " + e); |
| 583 | } |
| 584 | return null; |
| 585 | } |
Sunny Goyal | 0b03778 | 2015-04-02 10:27:03 -0700 | [diff] [blame] | 586 | |
Sunny Goyal | f75baa9 | 2016-11-22 03:23:51 +0530 | [diff] [blame] | 587 | private static class Decoder extends JSONObject { |
| 588 | public final Intent launcherIntent; |
Sunny Goyal | 7c74e4a | 2016-12-15 15:53:17 -0800 | [diff] [blame] | 589 | public final UserHandle user; |
Sunny Goyal | f75baa9 | 2016-11-22 03:23:51 +0530 | [diff] [blame] | 590 | |
| 591 | private Decoder(String encoded, Context context) throws JSONException, URISyntaxException { |
| 592 | super(encoded); |
| 593 | launcherIntent = Intent.parseUri(getString(LAUNCH_INTENT_KEY), 0); |
| 594 | user = has(USER_HANDLE_KEY) ? UserManagerCompat.getInstance(context) |
| 595 | .getUserForSerialNumber(getLong(USER_HANDLE_KEY)) |
Sunny Goyal | 7c74e4a | 2016-12-15 15:53:17 -0800 | [diff] [blame] | 596 | : Process.myUserHandle(); |
Sunny Goyal | f75baa9 | 2016-11-22 03:23:51 +0530 | [diff] [blame] | 597 | if (user == null) { |
| 598 | throw new JSONException("Invalid user"); |
| 599 | } |
| 600 | } |
| 601 | } |
| 602 | |
Sunny Goyal | 0b03778 | 2015-04-02 10:27:03 -0700 | [diff] [blame] | 603 | /** |
| 604 | * Tries to create a new PendingInstallShortcutInfo which represents the same target, |
| 605 | * but is an app target and not a shortcut. |
| 606 | * @return the newly created info or the original one. |
| 607 | */ |
| 608 | private static PendingInstallShortcutInfo convertToLauncherActivityIfPossible( |
| 609 | PendingInstallShortcutInfo original) { |
Sunny Goyal | b740f59 | 2015-12-17 23:22:42 -0800 | [diff] [blame] | 610 | if (original.isLauncherActivity()) { |
Sunny Goyal | 0b03778 | 2015-04-02 10:27:03 -0700 | [diff] [blame] | 611 | // Already an activity target |
| 612 | return original; |
| 613 | } |
Sunny Goyal | 1582f16 | 2016-11-08 14:40:53 -0800 | [diff] [blame] | 614 | if (!Utilities.isLauncherAppTarget(original.launchIntent)) { |
Sunny Goyal | 0b03778 | 2015-04-02 10:27:03 -0700 | [diff] [blame] | 615 | return original; |
| 616 | } |
| 617 | |
Sunny Goyal | 3e9be43 | 2017-01-05 15:22:41 -0800 | [diff] [blame] | 618 | LauncherActivityInfo info = LauncherAppsCompat.getInstance(original.mContext) |
Sunny Goyal | 1582f16 | 2016-11-08 14:40:53 -0800 | [diff] [blame] | 619 | .resolveActivity(original.launchIntent, original.user); |
Sunny Goyal | 0b03778 | 2015-04-02 10:27:03 -0700 | [diff] [blame] | 620 | if (info == null) { |
| 621 | return original; |
| 622 | } |
Sunny Goyal | 0b03778 | 2015-04-02 10:27:03 -0700 | [diff] [blame] | 623 | // Ignore any conflicts in the label name, as that can change based on locale. |
Sunny Goyal | 1582f16 | 2016-11-08 14:40:53 -0800 | [diff] [blame] | 624 | return new PendingInstallShortcutInfo(info, original.mContext); |
Sunny Goyal | 0b03778 | 2015-04-02 10:27:03 -0700 | [diff] [blame] | 625 | } |
Sunny Goyal | 2bcbe13 | 2016-11-16 09:23:42 -0800 | [diff] [blame] | 626 | |
Sunny Goyal | aaf86fe | 2017-01-05 21:50:27 -0800 | [diff] [blame] | 627 | private static ShortcutInfo createShortcutInfo(Intent data, LauncherAppState app) { |
| 628 | Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT); |
| 629 | String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME); |
| 630 | Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON); |
| 631 | |
| 632 | if (intent == null) { |
| 633 | // If the intent is null, we can't construct a valid ShortcutInfo, so we return null |
| 634 | Log.e(TAG, "Can't construct ShorcutInfo with null intent"); |
| 635 | return null; |
| 636 | } |
| 637 | |
| 638 | final ShortcutInfo info = new ShortcutInfo(); |
| 639 | |
| 640 | // Only support intents for current user for now. Intents sent from other |
| 641 | // users wouldn't get here without intent forwarding anyway. |
| 642 | info.user = Process.myUserHandle(); |
| 643 | |
Sunny Goyal | 179249d | 2017-12-19 16:49:24 -0800 | [diff] [blame] | 644 | BitmapInfo iconInfo = null; |
Sunny Goyal | 18a4e5a | 2018-01-09 15:34:38 -0800 | [diff] [blame] | 645 | LauncherIcons li = LauncherIcons.obtain(app.getContext()); |
Sunny Goyal | aaf86fe | 2017-01-05 21:50:27 -0800 | [diff] [blame] | 646 | if (bitmap instanceof Bitmap) { |
Sunny Goyal | 18a4e5a | 2018-01-09 15:34:38 -0800 | [diff] [blame] | 647 | iconInfo = li.createIconBitmap((Bitmap) bitmap); |
Sunny Goyal | aaf86fe | 2017-01-05 21:50:27 -0800 | [diff] [blame] | 648 | } else { |
| 649 | Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE); |
| 650 | if (extra instanceof Intent.ShortcutIconResource) { |
| 651 | info.iconResource = (Intent.ShortcutIconResource) extra; |
Sunny Goyal | 18a4e5a | 2018-01-09 15:34:38 -0800 | [diff] [blame] | 652 | iconInfo = li.createIconBitmap(info.iconResource); |
Sunny Goyal | aaf86fe | 2017-01-05 21:50:27 -0800 | [diff] [blame] | 653 | } |
| 654 | } |
Sunny Goyal | 18a4e5a | 2018-01-09 15:34:38 -0800 | [diff] [blame] | 655 | li.recycle(); |
| 656 | |
Sunny Goyal | 179249d | 2017-12-19 16:49:24 -0800 | [diff] [blame] | 657 | if (iconInfo == null) { |
| 658 | iconInfo = app.getIconCache().getDefaultIcon(info.user); |
Sunny Goyal | aaf86fe | 2017-01-05 21:50:27 -0800 | [diff] [blame] | 659 | } |
Hyunyoung Song | cda96a5 | 2018-10-18 15:05:45 -0700 | [diff] [blame] | 660 | info.applyFrom(iconInfo); |
Sunny Goyal | aaf86fe | 2017-01-05 21:50:27 -0800 | [diff] [blame] | 661 | |
| 662 | info.title = Utilities.trim(name); |
Sunny Goyal | d7239fc | 2018-11-05 17:19:45 -0800 | [diff] [blame] | 663 | info.contentDescription = app.getContext().getPackageManager() |
| 664 | .getUserBadgedLabel(info.title, info.user); |
Sunny Goyal | aaf86fe | 2017-01-05 21:50:27 -0800 | [diff] [blame] | 665 | info.intent = intent; |
| 666 | return info; |
| 667 | } |
| 668 | |
The Android Open Source Project | 31dd503 | 2009-03-03 19:32:27 -0800 | [diff] [blame] | 669 | } |