1 /** 2 * Copyright (C) 2017 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 android.support.v4.content.pm; 18 19 import android.app.Activity; 20 import android.content.BroadcastReceiver; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.IntentSender; 24 import android.content.pm.PackageManager; 25 import android.content.pm.ResolveInfo; 26 import android.content.pm.ShortcutManager; 27 import android.support.annotation.NonNull; 28 import android.support.annotation.Nullable; 29 import android.support.annotation.VisibleForTesting; 30 import android.support.v4.content.ContextCompat; 31 import android.support.v4.os.BuildCompat; 32 import android.text.TextUtils; 33 34 /** 35 * Helper for accessing features in {@link android.content.pm.ShortcutManager} 36 * in a backwards compatible fashion. 37 */ 38 public class ShortcutManagerCompat { 39 40 @VisibleForTesting static final String ACTION_INSTALL_SHORTCUT = 41 "com.android.launcher.action.INSTALL_SHORTCUT"; 42 @VisibleForTesting static final String INSTALL_SHORTCUT_PERMISSION = 43 "com.android.launcher.permission.INSTALL_SHORTCUT"; 44 45 private ShortcutManagerCompat() { 46 /* Hide constructor */ 47 } 48 49 /** 50 * @return {@code true} if the launcher supports {@link #requestPinShortcut}, 51 * {@code false} otherwise 52 */ 53 public static boolean isRequestPinShortcutSupported(@NonNull Context context) { 54 if (BuildCompat.isAtLeastO()) { 55 return context.getSystemService(ShortcutManager.class).isRequestPinShortcutSupported(); 56 } 57 58 if (ContextCompat.checkSelfPermission(context, INSTALL_SHORTCUT_PERMISSION) 59 != PackageManager.PERMISSION_GRANTED) { 60 return false; 61 } 62 for (ResolveInfo info : context.getPackageManager().queryBroadcastReceivers( 63 new Intent(ACTION_INSTALL_SHORTCUT), 0)) { 64 String permission = info.activityInfo.permission; 65 if (TextUtils.isEmpty(permission) || INSTALL_SHORTCUT_PERMISSION.equals(permission)) { 66 return true; 67 } 68 } 69 return false; 70 } 71 72 /** 73 * Request to create a pinned shortcut. 74 * <p>On API <= 25 it creates a legacy shortcut with the provided icon, label and intent. For 75 * newer APIs it will create a {@link android.content.pm.ShortcutInfo} object which can be 76 * updated by the app. 77 * 78 * <p>Use {@link android.app.PendingIntent#getIntentSender()} to create a {@link IntentSender}. 79 * 80 * @param shortcut new shortcut to pin 81 * @param callback if not null, this intent will be sent when the shortcut is pinned 82 * 83 * @return {@code true} if the launcher supports this feature 84 * 85 * @see #isRequestPinShortcutSupported 86 * @see IntentSender 87 * @see android.app.PendingIntent#getIntentSender() 88 */ 89 public static boolean requestPinShortcut(@NonNull final Context context, 90 @NonNull ShortcutInfoCompat shortcut, @Nullable final IntentSender callback) { 91 if (BuildCompat.isAtLeastO()) { 92 return context.getSystemService(ShortcutManager.class).requestPinShortcut( 93 shortcut.toShortcutInfo(), callback); 94 } 95 96 if (!isRequestPinShortcutSupported(context)) { 97 return false; 98 } 99 Intent intent = shortcut.addToIntent(new Intent(ACTION_INSTALL_SHORTCUT)); 100 101 // If the callback is null, just send the broadcast 102 if (callback == null) { 103 context.sendBroadcast(intent); 104 return true; 105 } 106 107 // Otherwise send the callback when the intent has successfully been dispatched. 108 context.sendOrderedBroadcast(intent, null, new BroadcastReceiver() { 109 @Override 110 public void onReceive(Context context, Intent intent) { 111 try { 112 callback.sendIntent(context, 0, null, null, null); 113 } catch (IntentSender.SendIntentException e) { 114 // Ignore 115 } 116 } 117 }, null, Activity.RESULT_OK, null, null); 118 return true; 119 } 120 121 /** 122 * Returns an Intent which can be used by the launcher to pin shortcut. 123 * <p>This should be used by an Activity to set result in response to 124 * {@link Intent#ACTION_CREATE_SHORTCUT}. 125 * 126 * @param shortcut new shortcut to pin 127 * @return the intent that should be set as the result for the calling activity 128 * 129 * @see Intent#ACTION_CREATE_SHORTCUT 130 */ 131 @NonNull 132 public static Intent createShortcutResultIntent(@NonNull Context context, 133 @NonNull ShortcutInfoCompat shortcut) { 134 Intent result = null; 135 if (BuildCompat.isAtLeastO()) { 136 result = context.getSystemService(ShortcutManager.class) 137 .createShortcutResultIntent(shortcut.toShortcutInfo()); 138 } 139 if (result == null) { 140 result = new Intent(); 141 } 142 return shortcut.addToIntent(result); 143 } 144 } 145