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 */ 16package android.content.pm; 17 18import android.annotation.NonNull; 19import android.annotation.TestApi; 20import android.content.Context; 21import android.os.IBinder; 22import android.os.RemoteException; 23import android.os.ServiceManager; 24import android.os.UserHandle; 25 26import com.android.internal.annotations.VisibleForTesting; 27 28import java.util.List; 29 30// TODO Enhance javadoc 31/** 32 * {@link ShortcutManager} manages shortcuts created by applications. 33 * 34 * <h3>Dynamic shortcuts and pinned shortcuts</h3> 35 * 36 * An application can publish shortcuts with {@link #setDynamicShortcuts(List)} and 37 * {@link #addDynamicShortcuts(List)}. There can be at most 38 * {@link #getMaxDynamicShortcutCount()} number of dynamic shortcuts at a time from the same 39 * application. 40 * A dynamic shortcut can be deleted with {@link #removeDynamicShortcuts(List)}, and apps 41 * can also use {@link #removeAllDynamicShortcuts()} to delete all dynamic shortcuts. 42 * 43 * <p>The shortcuts that are currently published by the above APIs are called "dynamic", because 44 * they can be removed by the creator application at any time. The user may "pin" dynamic shortcuts 45 * on Launcher to make "pinned" shortcuts. Pinned shortcuts <b>cannot</b> be removed by the creator 46 * app. An application can obtain all pinned shortcuts from itself with 47 * {@link #getPinnedShortcuts()}. Applications should keep the pinned shortcut information 48 * up-to-date using {@link #updateShortcuts(List)}. 49 * 50 * <p>The number of pinned shortcuts does not affect the number of dynamic shortcuts that can be 51 * published by an application at a time. 52 * No matter how many pinned shortcuts that Launcher has for an application, the 53 * application can still always publish {@link #getMaxDynamicShortcutCount()} number of dynamic 54 * shortcuts. 55 * 56 * <h3>Shortcut IDs</h3> 57 * 58 * Each shortcut must have an ID, which must be unique within each application. When a shortcut is 59 * published, existing shortcuts with the same ID will be updated. Note this may include a 60 * pinned shortcut. 61 * 62 * <h3>Rate limiting</h3> 63 * 64 * Calls to {@link #setDynamicShortcuts(List)}, {@link #addDynamicShortcuts(List)}, 65 * and {@link #updateShortcuts(List)} from <b>background applications</b> will be 66 * rate-limited. An application can call these methods at most 67 * {@link #getRemainingCallCount()} times until the rate-limiting counter is reset, 68 * which happens at a certain time every day. 69 * 70 * <p>An application can use {@link #getRateLimitResetTime()} to get the next reset time. 71 * 72 * <p>Foreground applications (i.e. ones with a foreground activity or a foreground services) 73 * will not be throttled. Also, when an application comes to foreground, 74 * {@link #getRemainingCallCount()} will be reset to the initial value. 75 * 76 * <p>For testing purposes, use "Developer Options" (found in the Settings menu) to reset the 77 * internal rate-limiting counter. Automated tests can use the following ADB shell command to 78 * achieve the same effect:</p> 79 * <pre>adb shell cmd shortcut reset-throttling</pre> 80 * 81 * <h3>Backup and Restore</h3> 82 * 83 * Shortcuts will be backed up and restored across devices. This means all information, including 84 * IDs, must be meaningful on a different device. 85 * 86 * <h3>APIs for launcher</h3> 87 * 88 * Launcher applications should use {@link LauncherApps} to get shortcuts that are published from 89 * applications. Launcher applications can also pin shortcuts with 90 * {@link LauncherApps#pinShortcuts(String, List, UserHandle)}. 91 * 92 * @hide 93 */ 94public class ShortcutManager { 95 private static final String TAG = "ShortcutManager"; 96 97 private final Context mContext; 98 private final IShortcutService mService; 99 100 /** 101 * @hide 102 */ 103 public ShortcutManager(Context context, IShortcutService service) { 104 mContext = context; 105 mService = service; 106 } 107 108 /** 109 * @hide 110 */ 111 @TestApi 112 public ShortcutManager(Context context) { 113 this(context, IShortcutService.Stub.asInterface( 114 ServiceManager.getService(Context.SHORTCUT_SERVICE))); 115 } 116 117 /** 118 * Publish a list of shortcuts. All existing dynamic shortcuts from the caller application 119 * will be replaced. 120 * 121 * <p>This API will be rate-limited. 122 * 123 * @return {@code true} if the call has succeeded. {@code false} if the call is rate-limited. 124 * 125 * @throws IllegalArgumentException if {@code shortcutInfoList} contains more than 126 * {@link #getMaxDynamicShortcutCount()} shortcuts. 127 */ 128 public boolean setDynamicShortcuts(@NonNull List<ShortcutInfo> shortcutInfoList) { 129 try { 130 return mService.setDynamicShortcuts(mContext.getPackageName(), 131 new ParceledListSlice(shortcutInfoList), injectMyUserId()); 132 } catch (RemoteException e) { 133 throw e.rethrowFromSystemServer(); 134 } 135 } 136 137 /** 138 * Return all dynamic shortcuts from the caller application. The number of result items 139 * will not exceed the value returned by {@link #getMaxDynamicShortcutCount()}. 140 */ 141 @NonNull 142 public List<ShortcutInfo> getDynamicShortcuts() { 143 try { 144 return mService.getDynamicShortcuts(mContext.getPackageName(), injectMyUserId()) 145 .getList(); 146 } catch (RemoteException e) { 147 throw e.rethrowFromSystemServer(); 148 } 149 } 150 151 /** 152 * Publish a single dynamic shortcut. If there's already dynamic or pinned shortcuts with 153 * the same ID, they will all be updated. 154 * 155 * <p>This API will be rate-limited. 156 * 157 * @return {@code true} if the call has succeeded. {@code false} if the call is rate-limited. 158 * 159 * @throws IllegalArgumentException if the caller application has already published the 160 * max number of dynamic shortcuts. 161 */ 162 public boolean addDynamicShortcuts(@NonNull List<ShortcutInfo> shortcutInfoList) { 163 try { 164 return mService.addDynamicShortcuts(mContext.getPackageName(), 165 new ParceledListSlice(shortcutInfoList), injectMyUserId()); 166 } catch (RemoteException e) { 167 throw e.rethrowFromSystemServer(); 168 } 169 } 170 171 /** 172 * Delete a single dynamic shortcut by ID. 173 */ 174 public void removeDynamicShortcuts(@NonNull List<String> shortcutIds) { 175 try { 176 mService.removeDynamicShortcuts(mContext.getPackageName(), shortcutIds, 177 injectMyUserId()); 178 } catch (RemoteException e) { 179 throw e.rethrowFromSystemServer(); 180 } 181 } 182 183 /** 184 * Delete all dynamic shortcuts from the caller application. 185 */ 186 public void removeAllDynamicShortcuts() { 187 try { 188 mService.removeAllDynamicShortcuts(mContext.getPackageName(), injectMyUserId()); 189 } catch (RemoteException e) { 190 throw e.rethrowFromSystemServer(); 191 } 192 } 193 194 /** 195 * Return all pinned shortcuts from the caller application. 196 */ 197 @NonNull 198 public List<ShortcutInfo> getPinnedShortcuts() { 199 try { 200 return mService.getPinnedShortcuts(mContext.getPackageName(), injectMyUserId()) 201 .getList(); 202 } catch (RemoteException e) { 203 throw e.rethrowFromSystemServer(); 204 } 205 } 206 207 /** 208 * Update all existing shortcuts with the same IDs. Shortcuts may be pinned and/or dynamic. 209 * 210 * <p>This API will be rate-limited. 211 * 212 * @return {@code true} if the call has succeeded. {@code false} if the call is rate-limited. 213 */ 214 public boolean updateShortcuts(List<ShortcutInfo> shortcutInfoList) { 215 try { 216 return mService.updateShortcuts(mContext.getPackageName(), 217 new ParceledListSlice(shortcutInfoList), injectMyUserId()); 218 } catch (RemoteException e) { 219 throw e.rethrowFromSystemServer(); 220 } 221 } 222 223 /** 224 * Return the max number of dynamic shortcuts that each application can have at a time. 225 */ 226 public int getMaxDynamicShortcutCount() { 227 try { 228 return mService.getMaxDynamicShortcutCount(mContext.getPackageName(), injectMyUserId()); 229 } catch (RemoteException e) { 230 throw e.rethrowFromSystemServer(); 231 } 232 } 233 234 /** 235 * Return the number of times the caller application can call the rate-limited APIs 236 * before the rate limit counter is reset. 237 * 238 * @see #getRateLimitResetTime() 239 */ 240 public int getRemainingCallCount() { 241 try { 242 return mService.getRemainingCallCount(mContext.getPackageName(), injectMyUserId()); 243 } catch (RemoteException e) { 244 throw e.rethrowFromSystemServer(); 245 } 246 } 247 248 /** 249 * Return when the rate limit count will be reset next time, in milliseconds since the epoch. 250 * 251 * @see #getRemainingCallCount() 252 * @see System#currentTimeMillis() 253 */ 254 public long getRateLimitResetTime() { 255 try { 256 return mService.getRateLimitResetTime(mContext.getPackageName(), injectMyUserId()); 257 } catch (RemoteException e) { 258 throw e.rethrowFromSystemServer(); 259 } 260 } 261 262 /** 263 * Return the max width and height for icons, in pixels. 264 */ 265 public int getIconMaxDimensions() { 266 try { 267 return mService.getIconMaxDimensions(mContext.getPackageName(), injectMyUserId()); 268 } catch (RemoteException e) { 269 throw e.rethrowFromSystemServer(); 270 } 271 } 272 273 /** @hide injection point */ 274 @VisibleForTesting 275 protected int injectMyUserId() { 276 return UserHandle.myUserId(); 277 } 278} 279