/* * 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 com.android.launcher3.shortcuts; import android.annotation.TargetApi; import android.content.ComponentName; import android.content.Context; import android.content.pm.LauncherApps; import android.content.pm.LauncherApps.ShortcutQuery; import android.content.pm.ShortcutInfo; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.UserHandle; import android.util.Log; import com.android.launcher3.ItemInfo; import com.android.launcher3.LauncherSettings; import com.android.launcher3.Utilities; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * Performs operations related to deep shortcuts, such as querying for them, pinning them, etc. */ public class DeepShortcutManager { private static final String TAG = "DeepShortcutManager"; private static final int FLAG_GET_ALL = ShortcutQuery.FLAG_MATCH_DYNAMIC | ShortcutQuery.FLAG_MATCH_MANIFEST | ShortcutQuery.FLAG_MATCH_PINNED; private static DeepShortcutManager sInstance; private static final Object sInstanceLock = new Object(); public static DeepShortcutManager getInstance(Context context) { synchronized (sInstanceLock) { if (sInstance == null) { sInstance = new DeepShortcutManager(context.getApplicationContext()); } return sInstance; } } private final LauncherApps mLauncherApps; private boolean mWasLastCallSuccess; private DeepShortcutManager(Context context) { mLauncherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE); } public static boolean supportsShortcuts(ItemInfo info) { boolean isItemPromise = info instanceof com.android.launcher3.ShortcutInfo && ((com.android.launcher3.ShortcutInfo) info).hasPromiseIconUi(); return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION && !info.isDisabled() && !isItemPromise; } public boolean wasLastCallSuccess() { return mWasLastCallSuccess; } public void onShortcutsChanged(List shortcuts) { // mShortcutCache.removeShortcuts(shortcuts); } /** * Queries for the shortcuts with the package name and provided ids. * * This method is intended to get the full details for shortcuts when they are added or updated, * because we only get "key" fields in onShortcutsChanged(). */ public List queryForFullDetails(String packageName, List shortcutIds, UserHandle user) { return query(FLAG_GET_ALL, packageName, null, shortcutIds, user); } /** * Gets all the manifest and dynamic shortcuts associated with the given package and user, * to be displayed in the shortcuts container on long press. */ public List queryForShortcutsContainer(ComponentName activity, List ids, UserHandle user) { return query(ShortcutQuery.FLAG_MATCH_MANIFEST | ShortcutQuery.FLAG_MATCH_DYNAMIC, activity.getPackageName(), activity, ids, user); } /** * Removes the given shortcut from the current list of pinned shortcuts. * (Runs on background thread) */ @TargetApi(25) public void unpinShortcut(final ShortcutKey key) { if (Utilities.ATLEAST_NOUGAT_MR1) { String packageName = key.componentName.getPackageName(); String id = key.getId(); UserHandle user = key.user; List pinnedIds = extractIds(queryForPinnedShortcuts(packageName, user)); pinnedIds.remove(id); try { mLauncherApps.pinShortcuts(packageName, pinnedIds, user); mWasLastCallSuccess = true; } catch (SecurityException|IllegalStateException e) { Log.w(TAG, "Failed to unpin shortcut", e); mWasLastCallSuccess = false; } } } /** * Adds the given shortcut to the current list of pinned shortcuts. * (Runs on background thread) */ @TargetApi(25) public void pinShortcut(final ShortcutKey key) { if (Utilities.ATLEAST_NOUGAT_MR1) { String packageName = key.componentName.getPackageName(); String id = key.getId(); UserHandle user = key.user; List pinnedIds = extractIds(queryForPinnedShortcuts(packageName, user)); pinnedIds.add(id); try { mLauncherApps.pinShortcuts(packageName, pinnedIds, user); mWasLastCallSuccess = true; } catch (SecurityException|IllegalStateException e) { Log.w(TAG, "Failed to pin shortcut", e); mWasLastCallSuccess = false; } } } @TargetApi(25) public void startShortcut(String packageName, String id, Rect sourceBounds, Bundle startActivityOptions, UserHandle user) { if (Utilities.ATLEAST_NOUGAT_MR1) { try { mLauncherApps.startShortcut(packageName, id, sourceBounds, startActivityOptions, user); mWasLastCallSuccess = true; } catch (SecurityException|IllegalStateException e) { Log.e(TAG, "Failed to start shortcut", e); mWasLastCallSuccess = false; } } } @TargetApi(25) public Drawable getShortcutIconDrawable(ShortcutInfoCompat shortcutInfo, int density) { if (Utilities.ATLEAST_NOUGAT_MR1) { try { Drawable icon = mLauncherApps.getShortcutIconDrawable( shortcutInfo.getShortcutInfo(), density); mWasLastCallSuccess = true; return icon; } catch (SecurityException|IllegalStateException e) { Log.e(TAG, "Failed to get shortcut icon", e); mWasLastCallSuccess = false; } } return null; } /** * Returns the id's of pinned shortcuts associated with the given package and user. * * If packageName is null, returns all pinned shortcuts regardless of package. */ public List queryForPinnedShortcuts(String packageName, UserHandle user) { return query(ShortcutQuery.FLAG_MATCH_PINNED, packageName, null, null, user); } public List queryForAllShortcuts(UserHandle user) { return query(FLAG_GET_ALL, null, null, null, user); } private List extractIds(List shortcuts) { List shortcutIds = new ArrayList<>(shortcuts.size()); for (ShortcutInfoCompat shortcut : shortcuts) { shortcutIds.add(shortcut.getId()); } return shortcutIds; } /** * Query the system server for all the shortcuts matching the given parameters. * If packageName == null, we query for all shortcuts with the passed flags, regardless of app. * * TODO: Use the cache to optimize this so we don't make an RPC every time. */ @TargetApi(25) private List query(int flags, String packageName, ComponentName activity, List shortcutIds, UserHandle user) { if (Utilities.ATLEAST_NOUGAT_MR1) { ShortcutQuery q = new ShortcutQuery(); q.setQueryFlags(flags); if (packageName != null) { q.setPackage(packageName); q.setActivity(activity); q.setShortcutIds(shortcutIds); } List shortcutInfos = null; try { shortcutInfos = mLauncherApps.getShortcuts(q, user); mWasLastCallSuccess = true; } catch (SecurityException|IllegalStateException e) { Log.e(TAG, "Failed to query for shortcuts", e); mWasLastCallSuccess = false; } if (shortcutInfos == null) { return Collections.EMPTY_LIST; } List shortcutInfoCompats = new ArrayList<>(shortcutInfos.size()); for (ShortcutInfo shortcutInfo : shortcutInfos) { shortcutInfoCompats.add(new ShortcutInfoCompat(shortcutInfo)); } return shortcutInfoCompats; } else { return Collections.EMPTY_LIST; } } @TargetApi(25) public boolean hasHostPermission() { if (Utilities.ATLEAST_NOUGAT_MR1) { try { return mLauncherApps.hasShortcutHostPermission(); } catch (SecurityException|IllegalStateException e) { Log.e(TAG, "Failed to make shortcut manager call", e); } } return false; } }