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