1 /* 2 * Copyright (C) 2016 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 17 package com.android.launcher3.shortcuts; 18 19 import android.annotation.TargetApi; 20 import android.content.ComponentName; 21 import android.content.Context; 22 import android.content.pm.LauncherApps; 23 import android.content.pm.LauncherApps.ShortcutQuery; 24 import android.content.pm.ShortcutInfo; 25 import android.graphics.Rect; 26 import android.graphics.drawable.Drawable; 27 import android.os.Bundle; 28 import android.os.UserHandle; 29 import android.util.Log; 30 31 import com.android.launcher3.ItemInfo; 32 import com.android.launcher3.LauncherSettings; 33 import com.android.launcher3.Utilities; 34 35 import java.util.ArrayList; 36 import java.util.Collections; 37 import java.util.List; 38 39 /** 40 * Performs operations related to deep shortcuts, such as querying for them, pinning them, etc. 41 */ 42 public class DeepShortcutManager { 43 private static final String TAG = "DeepShortcutManager"; 44 45 private static final int FLAG_GET_ALL = ShortcutQuery.FLAG_MATCH_DYNAMIC 46 | ShortcutQuery.FLAG_MATCH_MANIFEST | ShortcutQuery.FLAG_MATCH_PINNED; 47 48 private static DeepShortcutManager sInstance; 49 private static final Object sInstanceLock = new Object(); 50 getInstance(Context context)51 public static DeepShortcutManager getInstance(Context context) { 52 synchronized (sInstanceLock) { 53 if (sInstance == null) { 54 sInstance = new DeepShortcutManager(context.getApplicationContext()); 55 } 56 return sInstance; 57 } 58 } 59 60 private final LauncherApps mLauncherApps; 61 private boolean mWasLastCallSuccess; 62 DeepShortcutManager(Context context)63 private DeepShortcutManager(Context context) { 64 mLauncherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE); 65 } 66 supportsShortcuts(ItemInfo info)67 public static boolean supportsShortcuts(ItemInfo info) { 68 boolean isItemPromise = info instanceof com.android.launcher3.ShortcutInfo 69 && ((com.android.launcher3.ShortcutInfo) info).hasPromiseIconUi(); 70 return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION 71 && !info.isDisabled() && !isItemPromise; 72 } 73 wasLastCallSuccess()74 public boolean wasLastCallSuccess() { 75 return mWasLastCallSuccess; 76 } 77 onShortcutsChanged(List<ShortcutInfoCompat> shortcuts)78 public void onShortcutsChanged(List<ShortcutInfoCompat> shortcuts) { 79 // mShortcutCache.removeShortcuts(shortcuts); 80 } 81 82 /** 83 * Queries for the shortcuts with the package name and provided ids. 84 * 85 * This method is intended to get the full details for shortcuts when they are added or updated, 86 * because we only get "key" fields in onShortcutsChanged(). 87 */ queryForFullDetails(String packageName, List<String> shortcutIds, UserHandle user)88 public List<ShortcutInfoCompat> queryForFullDetails(String packageName, 89 List<String> shortcutIds, UserHandle user) { 90 return query(FLAG_GET_ALL, packageName, null, shortcutIds, user); 91 } 92 93 /** 94 * Gets all the manifest and dynamic shortcuts associated with the given package and user, 95 * to be displayed in the shortcuts container on long press. 96 */ queryForShortcutsContainer(ComponentName activity, List<String> ids, UserHandle user)97 public List<ShortcutInfoCompat> queryForShortcutsContainer(ComponentName activity, 98 List<String> ids, UserHandle user) { 99 return query(ShortcutQuery.FLAG_MATCH_MANIFEST | ShortcutQuery.FLAG_MATCH_DYNAMIC, 100 activity.getPackageName(), activity, ids, user); 101 } 102 103 /** 104 * Removes the given shortcut from the current list of pinned shortcuts. 105 * (Runs on background thread) 106 */ 107 @TargetApi(25) unpinShortcut(final ShortcutKey key)108 public void unpinShortcut(final ShortcutKey key) { 109 if (Utilities.ATLEAST_NOUGAT_MR1) { 110 String packageName = key.componentName.getPackageName(); 111 String id = key.getId(); 112 UserHandle user = key.user; 113 List<String> pinnedIds = extractIds(queryForPinnedShortcuts(packageName, user)); 114 pinnedIds.remove(id); 115 try { 116 mLauncherApps.pinShortcuts(packageName, pinnedIds, user); 117 mWasLastCallSuccess = true; 118 } catch (SecurityException|IllegalStateException e) { 119 Log.w(TAG, "Failed to unpin shortcut", e); 120 mWasLastCallSuccess = false; 121 } 122 } 123 } 124 125 /** 126 * Adds the given shortcut to the current list of pinned shortcuts. 127 * (Runs on background thread) 128 */ 129 @TargetApi(25) pinShortcut(final ShortcutKey key)130 public void pinShortcut(final ShortcutKey key) { 131 if (Utilities.ATLEAST_NOUGAT_MR1) { 132 String packageName = key.componentName.getPackageName(); 133 String id = key.getId(); 134 UserHandle user = key.user; 135 List<String> pinnedIds = extractIds(queryForPinnedShortcuts(packageName, user)); 136 pinnedIds.add(id); 137 try { 138 mLauncherApps.pinShortcuts(packageName, pinnedIds, user); 139 mWasLastCallSuccess = true; 140 } catch (SecurityException|IllegalStateException e) { 141 Log.w(TAG, "Failed to pin shortcut", e); 142 mWasLastCallSuccess = false; 143 } 144 } 145 } 146 147 @TargetApi(25) startShortcut(String packageName, String id, Rect sourceBounds, Bundle startActivityOptions, UserHandle user)148 public void startShortcut(String packageName, String id, Rect sourceBounds, 149 Bundle startActivityOptions, UserHandle user) { 150 if (Utilities.ATLEAST_NOUGAT_MR1) { 151 try { 152 mLauncherApps.startShortcut(packageName, id, sourceBounds, 153 startActivityOptions, user); 154 mWasLastCallSuccess = true; 155 } catch (SecurityException|IllegalStateException e) { 156 Log.e(TAG, "Failed to start shortcut", e); 157 mWasLastCallSuccess = false; 158 } 159 } 160 } 161 162 @TargetApi(25) getShortcutIconDrawable(ShortcutInfoCompat shortcutInfo, int density)163 public Drawable getShortcutIconDrawable(ShortcutInfoCompat shortcutInfo, int density) { 164 if (Utilities.ATLEAST_NOUGAT_MR1) { 165 try { 166 Drawable icon = mLauncherApps.getShortcutIconDrawable( 167 shortcutInfo.getShortcutInfo(), density); 168 mWasLastCallSuccess = true; 169 return icon; 170 } catch (SecurityException|IllegalStateException e) { 171 Log.e(TAG, "Failed to get shortcut icon", e); 172 mWasLastCallSuccess = false; 173 } 174 } 175 return null; 176 } 177 178 /** 179 * Returns the id's of pinned shortcuts associated with the given package and user. 180 * 181 * If packageName is null, returns all pinned shortcuts regardless of package. 182 */ queryForPinnedShortcuts(String packageName, UserHandle user)183 public List<ShortcutInfoCompat> queryForPinnedShortcuts(String packageName, UserHandle user) { 184 return query(ShortcutQuery.FLAG_MATCH_PINNED, packageName, null, null, user); 185 } 186 queryForAllShortcuts(UserHandle user)187 public List<ShortcutInfoCompat> queryForAllShortcuts(UserHandle user) { 188 return query(FLAG_GET_ALL, null, null, null, user); 189 } 190 extractIds(List<ShortcutInfoCompat> shortcuts)191 private List<String> extractIds(List<ShortcutInfoCompat> shortcuts) { 192 List<String> shortcutIds = new ArrayList<>(shortcuts.size()); 193 for (ShortcutInfoCompat shortcut : shortcuts) { 194 shortcutIds.add(shortcut.getId()); 195 } 196 return shortcutIds; 197 } 198 199 /** 200 * Query the system server for all the shortcuts matching the given parameters. 201 * If packageName == null, we query for all shortcuts with the passed flags, regardless of app. 202 * 203 * TODO: Use the cache to optimize this so we don't make an RPC every time. 204 */ 205 @TargetApi(25) query(int flags, String packageName, ComponentName activity, List<String> shortcutIds, UserHandle user)206 private List<ShortcutInfoCompat> query(int flags, String packageName, 207 ComponentName activity, List<String> shortcutIds, UserHandle user) { 208 if (Utilities.ATLEAST_NOUGAT_MR1) { 209 ShortcutQuery q = new ShortcutQuery(); 210 q.setQueryFlags(flags); 211 if (packageName != null) { 212 q.setPackage(packageName); 213 q.setActivity(activity); 214 q.setShortcutIds(shortcutIds); 215 } 216 List<ShortcutInfo> shortcutInfos = null; 217 try { 218 shortcutInfos = mLauncherApps.getShortcuts(q, user); 219 mWasLastCallSuccess = true; 220 } catch (SecurityException|IllegalStateException e) { 221 Log.e(TAG, "Failed to query for shortcuts", e); 222 mWasLastCallSuccess = false; 223 } 224 if (shortcutInfos == null) { 225 return Collections.EMPTY_LIST; 226 } 227 List<ShortcutInfoCompat> shortcutInfoCompats = new ArrayList<>(shortcutInfos.size()); 228 for (ShortcutInfo shortcutInfo : shortcutInfos) { 229 shortcutInfoCompats.add(new ShortcutInfoCompat(shortcutInfo)); 230 } 231 return shortcutInfoCompats; 232 } else { 233 return Collections.EMPTY_LIST; 234 } 235 } 236 237 @TargetApi(25) hasHostPermission()238 public boolean hasHostPermission() { 239 if (Utilities.ATLEAST_NOUGAT_MR1) { 240 try { 241 return mLauncherApps.hasShortcutHostPermission(); 242 } catch (SecurityException|IllegalStateException e) { 243 Log.e(TAG, "Failed to make shortcut manager call", e); 244 } 245 } 246 return false; 247 } 248 } 249