Home | History | Annotate | Download | only in shortcuts
      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