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