ShortcutView.java revision f27b1ffc67228d73326ec3426fef4c9db75cd6fd
1/* 2 * Copyright 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 17package androidx.slice.widget; 18 19import static android.app.slice.Slice.HINT_LARGE; 20import static android.app.slice.Slice.HINT_NO_TINT; 21import static android.app.slice.Slice.HINT_TITLE; 22import static android.app.slice.Slice.SUBTYPE_COLOR; 23import static android.app.slice.SliceItem.FORMAT_ACTION; 24import static android.app.slice.SliceItem.FORMAT_IMAGE; 25import static android.app.slice.SliceItem.FORMAT_INT; 26import static android.app.slice.SliceItem.FORMAT_SLICE; 27import static android.app.slice.SliceItem.FORMAT_TEXT; 28 29import android.annotation.SuppressLint; 30import android.app.PendingIntent; 31import android.app.PendingIntent.CanceledException; 32import android.content.Context; 33import android.content.Intent; 34import android.content.pm.ApplicationInfo; 35import android.content.pm.PackageManager; 36import android.content.pm.ProviderInfo; 37import android.content.res.Resources; 38import android.graphics.drawable.Drawable; 39import android.graphics.drawable.ShapeDrawable; 40import android.graphics.drawable.shapes.OvalShape; 41import android.net.Uri; 42import android.support.annotation.RestrictTo; 43import android.widget.ImageView; 44 45import androidx.slice.Slice; 46import androidx.slice.SliceItem; 47import androidx.slice.core.SliceQuery; 48import androidx.slice.view.R; 49 50/** 51 * @hide 52 */ 53@RestrictTo(RestrictTo.Scope.LIBRARY) 54public class ShortcutView extends SliceChildView { 55 56 private static final String TAG = "ShortcutView"; 57 58 private Slice mSlice; 59 private Uri mUri; 60 private SliceItem mActionItem; 61 private SliceItem mLabel; 62 private SliceItem mIcon; 63 64 private int mLargeIconSize; 65 private int mSmallIconSize; 66 67 public ShortcutView(Context context) { 68 super(context); 69 final Resources res = getResources(); 70 mSmallIconSize = res.getDimensionPixelSize(R.dimen.abc_slice_icon_size); 71 mLargeIconSize = res.getDimensionPixelSize(R.dimen.abc_slice_shortcut_size); 72 } 73 74 @SuppressLint("NewApi") // mIcon can only be non-null on API 23+ 75 @Override 76 public void setSlice(Slice slice) { 77 resetView(); 78 mSlice = slice; 79 determineShortcutItems(getContext(), slice); 80 SliceItem colorItem = SliceQuery.findSubtype(slice, FORMAT_INT, SUBTYPE_COLOR); 81 if (colorItem == null) { 82 colorItem = SliceQuery.findSubtype(slice, FORMAT_INT, SUBTYPE_COLOR); 83 } 84 final int color = colorItem != null 85 ? colorItem.getInt() 86 : SliceViewUtil.getColorAccent(getContext()); 87 ShapeDrawable circle = new ShapeDrawable(new OvalShape()); 88 circle.setTint(color); 89 ImageView iv = new ImageView(getContext()); 90 if (mIcon != null && !mIcon.hasHint(HINT_NO_TINT)) { 91 // Only set the background if we're tintable 92 iv.setBackground(circle); 93 } 94 addView(iv); 95 if (mIcon != null) { 96 boolean isImage = mIcon.hasHint(HINT_NO_TINT); 97 final int iconSize = isImage ? mLargeIconSize : mSmallIconSize; 98 SliceViewUtil.createCircledIcon(getContext(), iconSize, mIcon.getIcon(), 99 isImage, this /* parent */); 100 mUri = slice.getUri(); 101 setClickable(true); 102 } else { 103 setClickable(false); 104 } 105 } 106 107 @Override 108 public @SliceView.SliceMode int getMode() { 109 return SliceView.MODE_SHORTCUT; 110 } 111 112 @Override 113 public boolean performClick() { 114 if (!callOnClick()) { 115 try { 116 if (mActionItem != null) { 117 mActionItem.getAction().send(); 118 } else { 119 Intent intent = new Intent(Intent.ACTION_VIEW).setData(mUri); 120 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 121 getContext().startActivity(intent); 122 } 123 if (mObserver != null) { 124 EventInfo ei = new EventInfo(SliceView.MODE_SHORTCUT, 125 EventInfo.ACTION_TYPE_BUTTON, 126 EventInfo.ROW_TYPE_SHORTCUT, 0 /* rowIndex */); 127 SliceItem interactedItem = mActionItem != null 128 ? mActionItem 129 : new SliceItem(mSlice, FORMAT_SLICE, null /* subtype */, 130 mSlice.getHints()); 131 mObserver.onSliceAction(ei, interactedItem); 132 } 133 } catch (CanceledException e) { 134 e.printStackTrace(); 135 } 136 } 137 return true; 138 } 139 140 /** 141 * Looks at the slice and determines which items are best to use to compose the shortcut. 142 */ 143 private void determineShortcutItems(Context context, Slice slice) { 144 SliceItem titleItem = SliceQuery.find(slice, FORMAT_ACTION, 145 HINT_TITLE, null); 146 147 if (titleItem != null) { 148 // Preferred case: hinted action containing hinted image and text 149 mActionItem = titleItem; 150 mIcon = SliceQuery.find(titleItem.getSlice(), FORMAT_IMAGE, HINT_TITLE, 151 null); 152 mLabel = SliceQuery.find(titleItem.getSlice(), FORMAT_TEXT, HINT_TITLE, 153 null); 154 } else { 155 // No hinted action; just use the first one 156 mActionItem = SliceQuery.find(slice, FORMAT_ACTION, (String) null, null); 157 } 158 // First fallback: any hinted image and text 159 if (mIcon == null) { 160 mIcon = SliceQuery.find(slice, FORMAT_IMAGE, HINT_TITLE, 161 null); 162 } 163 if (mLabel == null) { 164 mLabel = SliceQuery.find(slice, FORMAT_TEXT, HINT_TITLE, 165 null); 166 } 167 // Second fallback: first image and text 168 if (mIcon == null) { 169 mIcon = SliceQuery.find(slice, FORMAT_IMAGE, (String) null, 170 null); 171 } 172 if (mLabel == null) { 173 mLabel = SliceQuery.find(slice, FORMAT_TEXT, (String) null, 174 null); 175 } 176 // Final fallback: use app info 177 if (mIcon == null || mLabel == null || mActionItem == null) { 178 PackageManager pm = context.getPackageManager(); 179 ProviderInfo providerInfo = pm.resolveContentProvider( 180 slice.getUri().getAuthority(), 0); 181 ApplicationInfo appInfo = providerInfo.applicationInfo; 182 if (appInfo != null) { 183 if (mIcon == null) { 184 Slice.Builder sb = new Slice.Builder(slice.getUri()); 185 Drawable icon = pm.getApplicationIcon(appInfo); 186 sb.addIcon(SliceViewUtil.createIconFromDrawable(icon), HINT_LARGE); 187 mIcon = sb.build().getItems().get(0); 188 } 189 if (mLabel == null) { 190 Slice.Builder sb = new Slice.Builder(slice.getUri()); 191 sb.addText(pm.getApplicationLabel(appInfo), null); 192 mLabel = sb.build().getItems().get(0); 193 } 194 if (mActionItem == null) { 195 mActionItem = new SliceItem(PendingIntent.getActivity(context, 0, 196 pm.getLaunchIntentForPackage(appInfo.packageName), 0), 197 new Slice.Builder(slice.getUri()).build(), FORMAT_SLICE, 198 null /* subtype */, null); 199 } 200 } 201 } 202 } 203 204 @Override 205 public void resetView() { 206 mSlice = null; 207 mUri = null; 208 mActionItem = null; 209 mLabel = null; 210 mIcon = null; 211 setBackground(null); 212 removeAllViews(); 213 } 214} 215