/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.content.pm; import android.annotation.NonNull; import android.annotation.TestApi; import android.content.Context; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; import com.android.internal.annotations.VisibleForTesting; import java.util.List; // TODO Enhance javadoc /** * {@link ShortcutManager} manages shortcuts created by applications. * *

Dynamic shortcuts and pinned shortcuts

* * An application can publish shortcuts with {@link #setDynamicShortcuts(List)} and * {@link #addDynamicShortcuts(List)}. There can be at most * {@link #getMaxDynamicShortcutCount()} number of dynamic shortcuts at a time from the same * application. * A dynamic shortcut can be deleted with {@link #removeDynamicShortcuts(List)}, and apps * can also use {@link #removeAllDynamicShortcuts()} to delete all dynamic shortcuts. * *

The shortcuts that are currently published by the above APIs are called "dynamic", because * they can be removed by the creator application at any time. The user may "pin" dynamic shortcuts * on Launcher to make "pinned" shortcuts. Pinned shortcuts cannot be removed by the creator * app. An application can obtain all pinned shortcuts from itself with * {@link #getPinnedShortcuts()}. Applications should keep the pinned shortcut information * up-to-date using {@link #updateShortcuts(List)}. * *

The number of pinned shortcuts does not affect the number of dynamic shortcuts that can be * published by an application at a time. * No matter how many pinned shortcuts that Launcher has for an application, the * application can still always publish {@link #getMaxDynamicShortcutCount()} number of dynamic * shortcuts. * *

Shortcut IDs

* * Each shortcut must have an ID, which must be unique within each application. When a shortcut is * published, existing shortcuts with the same ID will be updated. Note this may include a * pinned shortcut. * *

Rate limiting

* * Calls to {@link #setDynamicShortcuts(List)}, {@link #addDynamicShortcuts(List)}, * and {@link #updateShortcuts(List)} from background applications will be * rate-limited. An application can call these methods at most * {@link #getRemainingCallCount()} times until the rate-limiting counter is reset, * which happens at a certain time every day. * *

An application can use {@link #getRateLimitResetTime()} to get the next reset time. * *

Foreground applications (i.e. ones with a foreground activity or a foreground services) * will not be throttled. Also, when an application comes to foreground, * {@link #getRemainingCallCount()} will be reset to the initial value. * *

For testing purposes, use "Developer Options" (found in the Settings menu) to reset the * internal rate-limiting counter. Automated tests can use the following ADB shell command to * achieve the same effect:

*
adb shell cmd shortcut reset-throttling
* *

Backup and Restore

* * Shortcuts will be backed up and restored across devices. This means all information, including * IDs, must be meaningful on a different device. * *

APIs for launcher

* * Launcher applications should use {@link LauncherApps} to get shortcuts that are published from * applications. Launcher applications can also pin shortcuts with * {@link LauncherApps#pinShortcuts(String, List, UserHandle)}. * * @hide */ public class ShortcutManager { private static final String TAG = "ShortcutManager"; private final Context mContext; private final IShortcutService mService; /** * @hide */ public ShortcutManager(Context context, IShortcutService service) { mContext = context; mService = service; } /** * @hide */ @TestApi public ShortcutManager(Context context) { this(context, IShortcutService.Stub.asInterface( ServiceManager.getService(Context.SHORTCUT_SERVICE))); } /** * Publish a list of shortcuts. All existing dynamic shortcuts from the caller application * will be replaced. * *

This API will be rate-limited. * * @return {@code true} if the call has succeeded. {@code false} if the call is rate-limited. * * @throws IllegalArgumentException if {@code shortcutInfoList} contains more than * {@link #getMaxDynamicShortcutCount()} shortcuts. */ public boolean setDynamicShortcuts(@NonNull List shortcutInfoList) { try { return mService.setDynamicShortcuts(mContext.getPackageName(), new ParceledListSlice(shortcutInfoList), injectMyUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Return all dynamic shortcuts from the caller application. The number of result items * will not exceed the value returned by {@link #getMaxDynamicShortcutCount()}. */ @NonNull public List getDynamicShortcuts() { try { return mService.getDynamicShortcuts(mContext.getPackageName(), injectMyUserId()) .getList(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Publish a single dynamic shortcut. If there's already dynamic or pinned shortcuts with * the same ID, they will all be updated. * *

This API will be rate-limited. * * @return {@code true} if the call has succeeded. {@code false} if the call is rate-limited. * * @throws IllegalArgumentException if the caller application has already published the * max number of dynamic shortcuts. */ public boolean addDynamicShortcuts(@NonNull List shortcutInfoList) { try { return mService.addDynamicShortcuts(mContext.getPackageName(), new ParceledListSlice(shortcutInfoList), injectMyUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Delete a single dynamic shortcut by ID. */ public void removeDynamicShortcuts(@NonNull List shortcutIds) { try { mService.removeDynamicShortcuts(mContext.getPackageName(), shortcutIds, injectMyUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Delete all dynamic shortcuts from the caller application. */ public void removeAllDynamicShortcuts() { try { mService.removeAllDynamicShortcuts(mContext.getPackageName(), injectMyUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Return all pinned shortcuts from the caller application. */ @NonNull public List getPinnedShortcuts() { try { return mService.getPinnedShortcuts(mContext.getPackageName(), injectMyUserId()) .getList(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Update all existing shortcuts with the same IDs. Shortcuts may be pinned and/or dynamic. * *

This API will be rate-limited. * * @return {@code true} if the call has succeeded. {@code false} if the call is rate-limited. */ public boolean updateShortcuts(List shortcutInfoList) { try { return mService.updateShortcuts(mContext.getPackageName(), new ParceledListSlice(shortcutInfoList), injectMyUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Return the max number of dynamic shortcuts that each application can have at a time. */ public int getMaxDynamicShortcutCount() { try { return mService.getMaxDynamicShortcutCount(mContext.getPackageName(), injectMyUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Return the number of times the caller application can call the rate-limited APIs * before the rate limit counter is reset. * * @see #getRateLimitResetTime() */ public int getRemainingCallCount() { try { return mService.getRemainingCallCount(mContext.getPackageName(), injectMyUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Return when the rate limit count will be reset next time, in milliseconds since the epoch. * * @see #getRemainingCallCount() * @see System#currentTimeMillis() */ public long getRateLimitResetTime() { try { return mService.getRateLimitResetTime(mContext.getPackageName(), injectMyUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Return the max width and height for icons, in pixels. */ public int getIconMaxDimensions() { try { return mService.getIconMaxDimensions(mContext.getPackageName(), injectMyUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** @hide injection point */ @VisibleForTesting protected int injectMyUserId() { return UserHandle.myUserId(); } }