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