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 package com.example.android.pm.shortcutlauncherdemo;
17 
18 import android.content.ComponentName;
19 import android.content.Context;
20 import android.content.pm.LauncherApps;
21 import android.content.pm.LauncherApps.ShortcutQuery;
22 import android.content.pm.ShortcutInfo;
23 import android.os.Bundle;
24 import android.os.UserHandle;
25 import android.os.UserManager;
26 import android.util.Log;
27 import android.widget.Toast;
28 
29 import com.example.android.pm.shortcutdemo.ShortcutAdapter;
30 
31 import java.util.ArrayList;
32 import java.util.Collections;
33 import java.util.Comparator;
34 import java.util.List;
35 
36 public class ShortcutListFragment extends MyBaseListFragment {
37     private static final String TAG = "ShortcutListFragment";
38 
39     private static final String ARG_TARGET_PACKAGE = "target_package";
40     private static final String ARG_TARGET_ACTIVITY = "target_activity";
41     private static final String ARG_INCLUDE_DYNAMIC = "include_dynamic";
42     private static final String ARG_INCLUDE_MANIFEST = "include_manifest";
43     private static final String ARG_INCLUDE_PINNED = "include_pinned";
44     private static final String ARG_USER = "user";
45     private static final String ARG_SHOW_DETAILS = "show_details";
46 
47     private MyAdapter mAdapter;
48 
setArguments(String targetPackage, ComponentName targetActivity, boolean includeDynamic, boolean includeManifest, boolean includePinned, UserHandle user, boolean showDetails)49     public ShortcutListFragment setArguments(String targetPackage, ComponentName targetActivity,
50             boolean includeDynamic, boolean includeManifest,
51             boolean includePinned, UserHandle user, boolean showDetails) {
52         final Bundle b = new Bundle();
53         b.putString(ARG_TARGET_PACKAGE, targetPackage);
54         b.putParcelable(ARG_TARGET_ACTIVITY, targetActivity);
55         b.putBoolean(ARG_INCLUDE_DYNAMIC, includeDynamic);
56         b.putBoolean(ARG_INCLUDE_MANIFEST, includeManifest);
57         b.putBoolean(ARG_INCLUDE_PINNED, includePinned);
58         b.putParcelable(ARG_USER, user);
59         b.putBoolean(ARG_SHOW_DETAILS, showDetails);
60 
61         setArguments(b);
62 
63         return this;
64     }
65 
66     @Override
onCreate(Bundle savedInstanceState)67     public void onCreate(Bundle savedInstanceState) {
68         super.onCreate(savedInstanceState);
69 
70         mUserManager = getActivity().getSystemService(UserManager.class);
71         mLauncherApps = getActivity().getSystemService(LauncherApps.class);
72 
73         mAdapter = new MyAdapter(getActivity(), getArguments().getBoolean(ARG_SHOW_DETAILS));
74         setListAdapter(mAdapter);
75 
76         if (!mLauncherApps.hasShortcutHostPermission()) {
77             Toast.makeText(getActivity(), "App doesn't have the shortcut permissions",
78                     Toast.LENGTH_LONG).show();
79         }
80     }
81 
getTargetUsers()82     private List<UserHandle> getTargetUsers() {
83         final UserHandle arg = getArguments().getParcelable(ARG_USER);
84         if (arg == null) {
85             return Compat.getProfiles(getActivity());
86         } else {
87             final List<UserHandle> ret = new ArrayList<>();
88             ret.add(arg);
89             return ret;
90         }
91     }
92 
togglePin(ShortcutInfo selected)93     private void togglePin(ShortcutInfo selected) {
94         final String packageName = selected.getPackage();
95         final UserHandle user = selected.getUserHandle();
96 
97         try {
98             final ShortcutQuery q = new ShortcutQuery()
99                     .setPackage(packageName)
100                     .setQueryFlags(ShortcutQuery.FLAG_MATCH_PINNED)
101                     ;
102 
103             final List<String> pinned = new ArrayList<>();
104             for (ShortcutInfo si : mLauncherApps.getShortcuts(q, user)) {
105                 pinned.add(si.getId());
106             }
107             if (selected.isPinned()) {
108                 pinned.remove(selected.getId());
109             } else {
110                 pinned.add(selected.getId());
111             }
112             mLauncherApps.pinShortcuts(packageName, pinned, selected.getUserHandle());
113         } catch (Exception e) {
114             Global.showToast(getContext(), e.getMessage());
115         }
116     }
117 
launch(ShortcutInfo si)118     private void launch(ShortcutInfo si) {
119         try {
120             mLauncherApps.startShortcut(si.getPackage(), si.getId(), null, null,
121                     si.getUserHandle());
122         } catch (Exception e) {
123             Global.showToast(getContext(), e.getMessage());
124         }
125     }
126 
127     @Override
refreshList()128     protected void refreshList() {
129         Log.i(TAG, "Refreshing shortcuts");
130         try {
131             if (!mLauncherApps.hasShortcutHostPermission()) {
132                 return;
133             }
134 
135             final List<ShortcutInfo> list = new ArrayList<>();
136 
137             for (UserHandle user : getTargetUsers()) {
138                 if (!mUserManager.isUserUnlocked(user)) {
139                     continue;
140                 }
141 
142                 // To detect a race condition, first fetch all shortcuts and report if none found.
143                 mQuery.setQueryFlags(
144                         ShortcutQuery.FLAG_MATCH_PINNED | ShortcutQuery.FLAG_MATCH_DYNAMIC
145                                 | ShortcutQuery.FLAG_MATCH_MANIFEST
146                                 | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY);
147                 mQuery.setPackage(null);
148                 mQuery.setActivity(null);
149                 mQuery.setChangedSince(0);
150                 final int numShortcuts = mLauncherApps.getShortcuts(mQuery, user).size();
151                 if (numShortcuts == 0) {
152                     final String message = "No shortcut found for " + user;
153                     Log.e(TAG, message);
154                     Global.showToast(getContext(), message);
155                 }
156 
157                 final Bundle b = getArguments();
158                 mQuery.setQueryFlags(
159                         (b.getBoolean(ARG_INCLUDE_DYNAMIC) ? ShortcutQuery.FLAG_MATCH_DYNAMIC : 0) |
160                         (b.getBoolean(ARG_INCLUDE_MANIFEST) ? ShortcutQuery.FLAG_MATCH_MANIFEST : 0) |
161                         (b.getBoolean(ARG_INCLUDE_PINNED) ? ShortcutQuery.FLAG_MATCH_PINNED : 0));
162                 mQuery.setPackage(b.getString(ARG_TARGET_PACKAGE));
163                 mQuery.setActivity(b.getParcelable(ARG_TARGET_ACTIVITY));
164 
165                 list.addAll(mLauncherApps.getShortcuts(mQuery, user));
166             }
167             Collections.sort(list, mShortcutComparator);
168 
169             mAdapter.setShortcuts(list);
170         } catch (Exception e) {
171             Log.w(Global.TAG, "Caught exception", e);
172         }
173     }
174 
175     private final Comparator<ShortcutInfo> mShortcutComparator =
176             (ShortcutInfo s1, ShortcutInfo s2) -> {
177                 int ret = 0;
178                 ret = getAppLabel(s1.getPackage()).compareTo(getAppLabel(s2.getPackage()));
179                 if (ret != 0) return ret;
180 
181                 ret = s1.getUserHandle().hashCode() - s2.getUserHandle().hashCode();
182                 if (ret != 0) return ret;
183 
184                 ret = s1.getId().compareTo(s2.getId());
185                 if (ret != 0) return ret;
186 
187                 return 0;
188             };
189 
190     class MyAdapter extends ShortcutAdapter {
191         private final boolean mShowLine2;
192 
MyAdapter(Context context, boolean showLine2)193         public MyAdapter(Context context, boolean showLine2) {
194             super(context);
195             mShowLine2 = showLine2;
196         }
197 
198         @Override
getLayoutId()199         protected int getLayoutId() {
200             return R.layout.list_item;
201         }
202 
203         @Override
getText1Id()204         protected int getText1Id() {
205             return R.id.line1;
206         }
207 
208         @Override
getText2Id()209         protected int getText2Id() {
210             return R.id.line2;
211         }
212 
213         @Override
getImageId()214         protected int getImageId() {
215             return R.id.image;
216         }
217 
218         @Override
getLaunchId()219         protected int getLaunchId() {
220             return R.id.launch;
221         }
222 
223         @Override
getAction2Id()224         protected int getAction2Id() {
225             return R.id.action2;
226         }
227 
228         @Override
showLaunch(ShortcutInfo si)229         protected boolean showLaunch(ShortcutInfo si) {
230             return true;
231         }
232 
233         @Override
showAction2(ShortcutInfo si)234         protected boolean showAction2(ShortcutInfo si) {
235             return true;
236         }
237 
238         @Override
getAction2Text(ShortcutInfo si)239         protected String getAction2Text(ShortcutInfo si) {
240             return si.isPinned() ? "Unpin" : "Pin";
241         }
242 
243         @Override
onLaunchClicked(ShortcutInfo si)244         protected void onLaunchClicked(ShortcutInfo si) {
245             launch(si);
246         }
247 
248         @Override
onAction2Clicked(ShortcutInfo si)249         protected void onAction2Clicked(ShortcutInfo si) {
250             togglePin(si);
251         }
252 
253         @Override
showLine2()254         protected boolean showLine2() {
255             return mShowLine2;
256         }
257     }
258 }
259