blob: 5ea6b7edd83294ab4f7b3664a13909c1c3d3bb9d [file] [log] [blame]
Chiao Chengbeca8562012-11-28 18:06:44 -08001/*
2 * Copyright (C) 2010 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 */
Gary Mai08d87ee2017-03-15 11:01:28 -070016package com.android.contacts;
Chiao Chengbeca8562012-11-28 18:06:44 -080017
18import android.app.ActivityManager;
19import android.content.ContentResolver;
Gary Mai19186832017-03-17 13:38:43 -070020import android.content.ContentUris;
Chiao Chengbeca8562012-11-28 18:06:44 -080021import android.content.Context;
22import android.content.Intent;
Gary Mai08d87ee2017-03-15 11:01:28 -070023import android.content.pm.ShortcutInfo;
24import android.content.pm.ShortcutManager;
Chiao Chengbeca8562012-11-28 18:06:44 -080025import android.content.res.Resources;
26import android.database.Cursor;
27import android.graphics.Bitmap;
28import android.graphics.BitmapFactory;
29import android.graphics.Canvas;
30import android.graphics.Paint;
31import android.graphics.Paint.FontMetricsInt;
32import android.graphics.Rect;
Gary Maia80b9372017-03-21 18:02:19 -070033import android.graphics.drawable.AdaptiveIconDrawable;
Chiao Chengbeca8562012-11-28 18:06:44 -080034import android.graphics.drawable.BitmapDrawable;
35import android.graphics.drawable.Drawable;
Gary Mai08d87ee2017-03-15 11:01:28 -070036import android.graphics.drawable.Icon;
Chiao Chengbeca8562012-11-28 18:06:44 -080037import android.net.Uri;
38import android.os.AsyncTask;
Chiao Chengbeca8562012-11-28 18:06:44 -080039import android.provider.ContactsContract.CommonDataKinds.Phone;
40import android.provider.ContactsContract.CommonDataKinds.Photo;
41import android.provider.ContactsContract.Contacts;
42import android.provider.ContactsContract.Data;
Aravind Sreekumar71212852018-04-06 15:47:45 -070043import androidx.core.graphics.drawable.IconCompat;
44import androidx.core.graphics.drawable.RoundedBitmapDrawable;
45import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
46import androidx.core.os.BuildCompat;
Tyler Gunn5cf7f012014-09-10 15:20:21 -070047import android.telecom.PhoneAccount;
Chiao Chengbeca8562012-11-28 18:06:44 -080048import android.text.TextPaint;
49import android.text.TextUtils;
50import android.text.TextUtils.TruncateAt;
51
Gary Mai0a49afa2016-12-05 15:53:58 -080052import com.android.contacts.ContactPhotoManager.DefaultImageRequest;
Gary Mai02c3dee2017-05-05 15:49:11 -070053import com.android.contacts.lettertiles.LetterTileDrawable;
Gary Mai6a320a12017-01-25 12:27:15 -080054import com.android.contacts.util.BitmapUtil;
Gary Mai69c182a2016-12-05 13:07:03 -080055import com.android.contacts.util.ImplicitIntentsUtil;
Chiao Chengbeca8562012-11-28 18:06:44 -080056
57/**
58 * Constructs shortcut intents.
59 */
60public class ShortcutIntentBuilder {
61
62 private static final String[] CONTACT_COLUMNS = {
63 Contacts.DISPLAY_NAME,
64 Contacts.PHOTO_ID,
Yorke Lee8baea882014-03-03 11:08:17 -080065 Contacts.LOOKUP_KEY
Chiao Chengbeca8562012-11-28 18:06:44 -080066 };
67
68 private static final int CONTACT_DISPLAY_NAME_COLUMN_INDEX = 0;
69 private static final int CONTACT_PHOTO_ID_COLUMN_INDEX = 1;
Yorke Lee8baea882014-03-03 11:08:17 -080070 private static final int CONTACT_LOOKUP_KEY_COLUMN_INDEX = 2;
Chiao Chengbeca8562012-11-28 18:06:44 -080071
72 private static final String[] PHONE_COLUMNS = {
73 Phone.DISPLAY_NAME,
74 Phone.PHOTO_ID,
75 Phone.NUMBER,
76 Phone.TYPE,
Yorke Lee8baea882014-03-03 11:08:17 -080077 Phone.LABEL,
78 Phone.LOOKUP_KEY
Chiao Chengbeca8562012-11-28 18:06:44 -080079 };
80
81 private static final int PHONE_DISPLAY_NAME_COLUMN_INDEX = 0;
82 private static final int PHONE_PHOTO_ID_COLUMN_INDEX = 1;
83 private static final int PHONE_NUMBER_COLUMN_INDEX = 2;
84 private static final int PHONE_TYPE_COLUMN_INDEX = 3;
85 private static final int PHONE_LABEL_COLUMN_INDEX = 4;
Yorke Lee8baea882014-03-03 11:08:17 -080086 private static final int PHONE_LOOKUP_KEY_COLUMN_INDEX = 5;
Chiao Chengbeca8562012-11-28 18:06:44 -080087
88 private static final String[] PHOTO_COLUMNS = {
89 Photo.PHOTO,
90 };
91
92 private static final int PHOTO_PHOTO_COLUMN_INDEX = 0;
93
94 private static final String PHOTO_SELECTION = Photo._ID + "=?";
95
96 private final OnShortcutIntentCreatedListener mListener;
97 private final Context mContext;
98 private int mIconSize;
99 private final int mIconDensity;
Brian Attwell98771ce2014-09-02 12:54:53 -0700100 private final int mOverlayTextBackgroundColor;
Brian Attwell5b53b8d2014-07-07 22:22:51 -0700101 private final Resources mResources;
Chiao Chengbeca8562012-11-28 18:06:44 -0800102
103 /**
104 * This is a hidden API of the launcher in JellyBean that allows us to disable the animation
Brian Attwell8a0cbec2014-07-07 16:39:01 -0700105 * that it would usually do, because it interferes with our own animation for QuickContact.
106 * This is needed since some versions of the launcher override the intent flags and therefore
107 * ignore Intent.FLAG_ACTIVITY_NO_ANIMATION.
Chiao Chengbeca8562012-11-28 18:06:44 -0800108 */
109 public static final String INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION =
110 "com.android.launcher.intent.extra.shortcut.INGORE_LAUNCH_ANIMATION";
111
112 /**
113 * Listener interface.
114 */
115 public interface OnShortcutIntentCreatedListener {
116
117 /**
118 * Callback for shortcut intent creation.
119 *
120 * @param uri the original URI for which the shortcut intent has been
121 * created.
122 * @param shortcutIntent resulting shortcut intent.
123 */
124 void onShortcutIntentCreated(Uri uri, Intent shortcutIntent);
125 }
126
127 public ShortcutIntentBuilder(Context context, OnShortcutIntentCreatedListener listener) {
128 mContext = context;
129 mListener = listener;
130
Brian Attwell5b53b8d2014-07-07 22:22:51 -0700131 mResources = context.getResources();
Chiao Chengbeca8562012-11-28 18:06:44 -0800132 final ActivityManager am = (ActivityManager) context
133 .getSystemService(Context.ACTIVITY_SERVICE);
Brian Attwell5b53b8d2014-07-07 22:22:51 -0700134 mIconSize = mResources.getDimensionPixelSize(R.dimen.shortcut_icon_size);
Chiao Chengbeca8562012-11-28 18:06:44 -0800135 if (mIconSize == 0) {
136 mIconSize = am.getLauncherLargeIconSize();
137 }
138 mIconDensity = am.getLauncherLargeIconDensity();
Brian Attwell98771ce2014-09-02 12:54:53 -0700139 mOverlayTextBackgroundColor = mResources.getColor(R.color.shortcut_overlay_text_background);
Chiao Chengbeca8562012-11-28 18:06:44 -0800140 }
141
142 public void createContactShortcutIntent(Uri contactUri) {
143 new ContactLoadingAsyncTask(contactUri).execute();
144 }
145
146 public void createPhoneNumberShortcutIntent(Uri dataUri, String shortcutAction) {
147 new PhoneNumberLoadingAsyncTask(dataUri, shortcutAction).execute();
148 }
149
150 /**
151 * An asynchronous task that loads name, photo and other data from the database.
152 */
153 private abstract class LoadingAsyncTask extends AsyncTask<Void, Void, Void> {
154 protected Uri mUri;
155 protected String mContentType;
156 protected String mDisplayName;
Yorke Lee8baea882014-03-03 11:08:17 -0800157 protected String mLookupKey;
Chiao Chengbeca8562012-11-28 18:06:44 -0800158 protected byte[] mBitmapData;
159 protected long mPhotoId;
160
161 public LoadingAsyncTask(Uri uri) {
162 mUri = uri;
163 }
164
165 @Override
166 protected Void doInBackground(Void... params) {
167 mContentType = mContext.getContentResolver().getType(mUri);
168 loadData();
169 loadPhoto();
170 return null;
171 }
172
173 protected abstract void loadData();
174
175 private void loadPhoto() {
176 if (mPhotoId == 0) {
177 return;
178 }
179
180 ContentResolver resolver = mContext.getContentResolver();
181 Cursor cursor = resolver.query(Data.CONTENT_URI, PHOTO_COLUMNS, PHOTO_SELECTION,
182 new String[] { String.valueOf(mPhotoId) }, null);
183 if (cursor != null) {
184 try {
185 if (cursor.moveToFirst()) {
186 mBitmapData = cursor.getBlob(PHOTO_PHOTO_COLUMN_INDEX);
187 }
188 } finally {
189 cursor.close();
190 }
191 }
192 }
193 }
194
195 private final class ContactLoadingAsyncTask extends LoadingAsyncTask {
196 public ContactLoadingAsyncTask(Uri uri) {
197 super(uri);
198 }
199
200 @Override
201 protected void loadData() {
202 ContentResolver resolver = mContext.getContentResolver();
203 Cursor cursor = resolver.query(mUri, CONTACT_COLUMNS, null, null, null);
204 if (cursor != null) {
205 try {
206 if (cursor.moveToFirst()) {
207 mDisplayName = cursor.getString(CONTACT_DISPLAY_NAME_COLUMN_INDEX);
208 mPhotoId = cursor.getLong(CONTACT_PHOTO_ID_COLUMN_INDEX);
Yorke Lee8baea882014-03-03 11:08:17 -0800209 mLookupKey = cursor.getString(CONTACT_LOOKUP_KEY_COLUMN_INDEX);
Chiao Chengbeca8562012-11-28 18:06:44 -0800210 }
211 } finally {
212 cursor.close();
213 }
214 }
215 }
216 @Override
217 protected void onPostExecute(Void result) {
Yorke Lee8baea882014-03-03 11:08:17 -0800218 createContactShortcutIntent(mUri, mContentType, mDisplayName, mLookupKey, mBitmapData);
Chiao Chengbeca8562012-11-28 18:06:44 -0800219 }
220 }
221
222 private final class PhoneNumberLoadingAsyncTask extends LoadingAsyncTask {
223 private final String mShortcutAction;
224 private String mPhoneNumber;
225 private int mPhoneType;
226 private String mPhoneLabel;
227
228 public PhoneNumberLoadingAsyncTask(Uri uri, String shortcutAction) {
229 super(uri);
230 mShortcutAction = shortcutAction;
231 }
232
233 @Override
234 protected void loadData() {
235 ContentResolver resolver = mContext.getContentResolver();
236 Cursor cursor = resolver.query(mUri, PHONE_COLUMNS, null, null, null);
237 if (cursor != null) {
238 try {
239 if (cursor.moveToFirst()) {
240 mDisplayName = cursor.getString(PHONE_DISPLAY_NAME_COLUMN_INDEX);
241 mPhotoId = cursor.getLong(PHONE_PHOTO_ID_COLUMN_INDEX);
242 mPhoneNumber = cursor.getString(PHONE_NUMBER_COLUMN_INDEX);
243 mPhoneType = cursor.getInt(PHONE_TYPE_COLUMN_INDEX);
244 mPhoneLabel = cursor.getString(PHONE_LABEL_COLUMN_INDEX);
Yorke Lee8baea882014-03-03 11:08:17 -0800245 mLookupKey = cursor.getString(PHONE_LOOKUP_KEY_COLUMN_INDEX);
Chiao Chengbeca8562012-11-28 18:06:44 -0800246 }
247 } finally {
248 cursor.close();
249 }
250 }
251 }
252
253 @Override
254 protected void onPostExecute(Void result) {
Yorke Lee8baea882014-03-03 11:08:17 -0800255 createPhoneNumberShortcutIntent(mUri, mDisplayName, mLookupKey, mBitmapData,
256 mPhoneNumber, mPhoneType, mPhoneLabel, mShortcutAction);
Chiao Chengbeca8562012-11-28 18:06:44 -0800257 }
258 }
259
Yorke Lee8baea882014-03-03 11:08:17 -0800260 private Drawable getPhotoDrawable(byte[] bitmapData, String displayName, String lookupKey) {
Chiao Chengbeca8562012-11-28 18:06:44 -0800261 if (bitmapData != null) {
Yorke Lee8baea882014-03-03 11:08:17 -0800262 Bitmap bitmap = BitmapFactory.decodeByteArray(bitmapData, 0, bitmapData.length, null);
263 return new BitmapDrawable(mContext.getResources(), bitmap);
Chiao Chengbeca8562012-11-28 18:06:44 -0800264 } else {
Gary Mai02c3dee2017-05-05 15:49:11 -0700265 final DefaultImageRequest request = new DefaultImageRequest(displayName, lookupKey,
266 false);
267 if (BuildCompat.isAtLeastO()) {
268 // On O, scale the image down to add the padding needed by AdaptiveIcons.
269 request.scale = LetterTileDrawable.getAdaptiveIconScale();
270 }
Yorke Lee8baea882014-03-03 11:08:17 -0800271 return ContactPhotoManager.getDefaultAvatarDrawableForContact(mContext.getResources(),
Gary Mai02c3dee2017-05-05 15:49:11 -0700272 false, request);
Chiao Chengbeca8562012-11-28 18:06:44 -0800273 }
Chiao Chengbeca8562012-11-28 18:06:44 -0800274 }
275
276 private void createContactShortcutIntent(Uri contactUri, String contentType, String displayName,
Yorke Lee8baea882014-03-03 11:08:17 -0800277 String lookupKey, byte[] bitmapData) {
Gary Mai19186832017-03-17 13:38:43 -0700278 Intent intent = null;
Gary Mai9b1b9372017-06-13 17:43:39 -0700279 if (TextUtils.isEmpty(displayName)) {
280 displayName = mContext.getResources().getString(R.string.missing_name);
281 }
Gary Mai19186832017-03-17 13:38:43 -0700282 if (BuildCompat.isAtLeastO()) {
283 final long contactId = ContentUris.parseId(contactUri);
284 final ShortcutManager sm = (ShortcutManager)
285 mContext.getSystemService(Context.SHORTCUT_SERVICE);
286 final DynamicShortcuts dynamicShortcuts = new DynamicShortcuts(mContext);
287 final ShortcutInfo shortcutInfo = dynamicShortcuts.getQuickContactShortcutInfo(
288 contactId, lookupKey, displayName);
Gary Mai9b1b9372017-06-13 17:43:39 -0700289 if (shortcutInfo != null) {
290 intent = sm.createShortcutResultIntent(shortcutInfo);
291 }
Gary Mai19186832017-03-17 13:38:43 -0700292 }
Gary Mai08d87ee2017-03-15 11:01:28 -0700293 final Drawable drawable = getPhotoDrawable(bitmapData, displayName, lookupKey);
Chiao Chengbeca8562012-11-28 18:06:44 -0800294
Marcus Hagerottc5083f92016-09-14 08:34:29 -0700295 final Intent shortcutIntent = ImplicitIntentsUtil.getIntentForQuickContactLauncherShortcut(
296 mContext, contactUri);
Chiao Chengbeca8562012-11-28 18:06:44 -0800297
Gary Mai08d87ee2017-03-15 11:01:28 -0700298 intent = intent == null ? new Intent() : intent;
Gary Maidbf3e832017-06-01 12:12:51 -0700299
300 final Bitmap icon = generateQuickContactIcon(drawable);
301 if (BuildCompat.isAtLeastO()) {
302 final IconCompat compatIcon = IconCompat.createWithAdaptiveBitmap(icon);
Jeff Gastonaee02cd2018-04-04 00:55:38 -0400303 compatIcon.addToShortcutIntent(intent, null, mContext);
Gary Maidbf3e832017-06-01 12:12:51 -0700304 } else {
305 intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, icon);
306 }
Chiao Chengbeca8562012-11-28 18:06:44 -0800307 intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
Gary Mai08d87ee2017-03-15 11:01:28 -0700308 intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, displayName);
Chiao Chengbeca8562012-11-28 18:06:44 -0800309
310 mListener.onShortcutIntentCreated(contactUri, intent);
311 }
312
Yorke Lee8baea882014-03-03 11:08:17 -0800313 private void createPhoneNumberShortcutIntent(Uri uri, String displayName, String lookupKey,
314 byte[] bitmapData, String phoneNumber, int phoneType, String phoneLabel,
315 String shortcutAction) {
Gary Mai08d87ee2017-03-15 11:01:28 -0700316 final Drawable drawable = getPhotoDrawable(bitmapData, displayName, lookupKey);
317 final Bitmap icon;
318 final Uri phoneUri;
Gary Mai19186832017-03-17 13:38:43 -0700319 final String shortcutName;
guanxiongliu4f715ff2016-03-10 14:14:54 -0800320 if (TextUtils.isEmpty(displayName)) {
321 displayName = mContext.getResources().getString(R.string.missing_name);
322 }
Gary Mai08d87ee2017-03-15 11:01:28 -0700323
324 if (Intent.ACTION_CALL.equals(shortcutAction)) {
325 // Make the URI a direct tel: URI so that it will always continue to work
326 phoneUri = Uri.fromParts(PhoneAccount.SCHEME_TEL, phoneNumber, null);
327 icon = generatePhoneNumberIcon(drawable, phoneType, phoneLabel,
328 R.drawable.quantum_ic_phone_vd_theme_24);
Gary Mai19186832017-03-17 13:38:43 -0700329 shortcutName = mContext.getResources()
330 .getString(R.string.call_by_shortcut, displayName);
Gary Mai08d87ee2017-03-15 11:01:28 -0700331 } else {
332 phoneUri = Uri.fromParts(ContactsUtils.SCHEME_SMSTO, phoneNumber, null);
333 icon = generatePhoneNumberIcon(drawable, phoneType, phoneLabel,
334 R.drawable.quantum_ic_message_vd_theme_24);
Gary Mai19186832017-03-17 13:38:43 -0700335 shortcutName = mContext.getResources().getString(R.string.sms_by_shortcut, displayName);
Gary Mai08d87ee2017-03-15 11:01:28 -0700336 }
337
338 final Intent shortcutIntent = new Intent(shortcutAction, phoneUri);
339 shortcutIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
340
341 Intent intent = null;
Gary Maidbf3e832017-06-01 12:12:51 -0700342 IconCompat compatAdaptiveIcon = null;
Gary Mai08d87ee2017-03-15 11:01:28 -0700343 if (BuildCompat.isAtLeastO()) {
Gary Maidbf3e832017-06-01 12:12:51 -0700344 compatAdaptiveIcon = IconCompat.createWithAdaptiveBitmap(icon);
Gary Mai08d87ee2017-03-15 11:01:28 -0700345 final ShortcutManager sm = (ShortcutManager)
346 mContext.getSystemService(Context.SHORTCUT_SERVICE);
Gary Maife7bae52017-07-25 17:35:48 -0700347 final String id = shortcutAction + lookupKey + phoneUri.toString().hashCode();
Gary Mai08d87ee2017-03-15 11:01:28 -0700348 final DynamicShortcuts dynamicShortcuts = new DynamicShortcuts(mContext);
349 final ShortcutInfo shortcutInfo = dynamicShortcuts.getActionShortcutInfo(
Gary Maidbf3e832017-06-01 12:12:51 -0700350 id, displayName, shortcutIntent, compatAdaptiveIcon.toIcon());
Gary Mai9b1b9372017-06-13 17:43:39 -0700351 if (shortcutInfo != null) {
352 intent = sm.createShortcutResultIntent(shortcutInfo);
353 }
Gary Mai08d87ee2017-03-15 11:01:28 -0700354 }
355
356 intent = intent == null ? new Intent() : intent;
Gary Maidbf3e832017-06-01 12:12:51 -0700357 // This will be non-null in O and above.
358 if (compatAdaptiveIcon != null) {
Jeff Gastonaee02cd2018-04-04 00:55:38 -0400359 compatAdaptiveIcon.addToShortcutIntent(intent, null, mContext);
Gary Maidbf3e832017-06-01 12:12:51 -0700360 } else {
361 intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, icon);
362 }
Gary Mai08d87ee2017-03-15 11:01:28 -0700363 intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
Gary Mai19186832017-03-17 13:38:43 -0700364 intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, shortcutName);
Chiao Chengbeca8562012-11-28 18:06:44 -0800365
366 mListener.onShortcutIntentCreated(uri, intent);
367 }
368
Yorke Lee8baea882014-03-03 11:08:17 -0800369 private Bitmap generateQuickContactIcon(Drawable photo) {
Chiao Chengbeca8562012-11-28 18:06:44 -0800370 // Setup the drawing classes
Brian Attwell5b53b8d2014-07-07 22:22:51 -0700371 Bitmap bitmap = Bitmap.createBitmap(mIconSize, mIconSize, Bitmap.Config.ARGB_8888);
372 Canvas canvas = new Canvas(bitmap);
Chiao Chengbeca8562012-11-28 18:06:44 -0800373
374 // Copy in the photo
Chiao Chengbeca8562012-11-28 18:06:44 -0800375 Rect dst = new Rect(0,0, mIconSize, mIconSize);
Yorke Lee8baea882014-03-03 11:08:17 -0800376 photo.setBounds(dst);
377 photo.draw(canvas);
Chiao Chengbeca8562012-11-28 18:06:44 -0800378
Gary Mai02c3dee2017-05-05 15:49:11 -0700379 // Don't put a rounded border on an icon for O
380 if (BuildCompat.isAtLeastO()) {
381 return bitmap;
382 }
383
Brian Attwell5b53b8d2014-07-07 22:22:51 -0700384 // Draw the icon with a rounded border
Chris Craik93fb4352014-08-05 18:51:49 -0700385 RoundedBitmapDrawable roundedDrawable =
386 RoundedBitmapDrawableFactory.create(mResources, bitmap);
Brian Attwell5b53b8d2014-07-07 22:22:51 -0700387 roundedDrawable.setAntiAlias(true);
388 roundedDrawable.setCornerRadius(mIconSize / 2);
389 Bitmap roundedBitmap = Bitmap.createBitmap(mIconSize, mIconSize, Bitmap.Config.ARGB_8888);
390 canvas.setBitmap(roundedBitmap);
391 roundedDrawable.setBounds(dst);
392 roundedDrawable.draw(canvas);
Chiao Chengbeca8562012-11-28 18:06:44 -0800393 canvas.setBitmap(null);
394
Brian Attwell5b53b8d2014-07-07 22:22:51 -0700395 return roundedBitmap;
Chiao Chengbeca8562012-11-28 18:06:44 -0800396 }
397
398 /**
399 * Generates a phone number shortcut icon. Adds an overlay describing the type of the phone
400 * number, and if there is a photo also adds the call action icon.
401 */
Yorke Lee8baea882014-03-03 11:08:17 -0800402 private Bitmap generatePhoneNumberIcon(Drawable photo, int phoneType, String phoneLabel,
Chiao Chengbeca8562012-11-28 18:06:44 -0800403 int actionResId) {
404 final Resources r = mContext.getResources();
405 final float density = r.getDisplayMetrics().density;
406
Gary Mai6a320a12017-01-25 12:27:15 -0800407 final Drawable phoneDrawable = r.getDrawableForDensity(actionResId, mIconDensity);
408 // These icons have the same height and width so either is fine for the size.
409 final Bitmap phoneIcon =
410 BitmapUtil.drawableToBitmap(phoneDrawable, phoneDrawable.getIntrinsicHeight());
Chiao Chengbeca8562012-11-28 18:06:44 -0800411
Brian Attwell98771ce2014-09-02 12:54:53 -0700412 Bitmap icon = generateQuickContactIcon(photo);
Chiao Chengbeca8562012-11-28 18:06:44 -0800413 Canvas canvas = new Canvas(icon);
414
415 // Copy in the photo
416 Paint photoPaint = new Paint();
417 photoPaint.setDither(true);
418 photoPaint.setFilterBitmap(true);
Chiao Chengbeca8562012-11-28 18:06:44 -0800419 Rect dst = new Rect(0, 0, mIconSize, mIconSize);
Yorke Lee8baea882014-03-03 11:08:17 -0800420
Gary Mai19186832017-03-17 13:38:43 -0700421 // Create an overlay for the phone number type if we're pre-O. O created shortcuts have the
422 // app badge which overlaps the type overlay.
423 CharSequence overlay = Phone.getTypeLabel(r, phoneType, phoneLabel);
424 if (!BuildCompat.isAtLeastO() && overlay != null) {
425 TextPaint textPaint = new TextPaint(
426 Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG);
427 textPaint.setTextSize(r.getDimension(R.dimen.shortcut_overlay_text_size));
428 textPaint.setColor(r.getColor(R.color.textColorIconOverlay));
429 textPaint.setShadowLayer(4f, 0, 2f, r.getColor(R.color.textColorIconOverlayShadow));
Chiao Chengbeca8562012-11-28 18:06:44 -0800430
Gary Mai19186832017-03-17 13:38:43 -0700431 final FontMetricsInt fmi = textPaint.getFontMetricsInt();
Chiao Chengbeca8562012-11-28 18:06:44 -0800432
Gary Mai19186832017-03-17 13:38:43 -0700433 // First fill in a darker background around the text to be drawn
434 final Paint workPaint = new Paint();
435 workPaint.setColor(mOverlayTextBackgroundColor);
436 workPaint.setStyle(Paint.Style.FILL);
437 final int textPadding = r
438 .getDimensionPixelOffset(R.dimen.shortcut_overlay_text_background_padding);
439 final int textBandHeight = (fmi.descent - fmi.ascent) + textPadding * 2;
440 dst.set(0, mIconSize - textBandHeight, mIconSize, mIconSize);
441 canvas.drawRect(dst, workPaint);
Chiao Chengbeca8562012-11-28 18:06:44 -0800442
Gary Mai19186832017-03-17 13:38:43 -0700443 overlay = TextUtils.ellipsize(overlay, textPaint, mIconSize, TruncateAt.END);
444 final float textWidth = textPaint.measureText(overlay, 0, overlay.length());
445 canvas.drawText(overlay, 0, overlay.length(), (mIconSize - textWidth) / 2, mIconSize
446 - fmi.descent - textPadding, textPaint);
Chiao Chengbeca8562012-11-28 18:06:44 -0800447 }
448
449 // Draw the phone action icon as an overlay
Chiao Chengbeca8562012-11-28 18:06:44 -0800450 int iconWidth = icon.getWidth();
Gary Mai02c3dee2017-05-05 15:49:11 -0700451 if (BuildCompat.isAtLeastO()) {
452 // On O we need to calculate where the phone icon goes slightly differently. The whole
453 // canvas area is 108dp, a centered circle with a diameter of 66dp is the "safe zone".
454 // So we start the drawing the phone icon at
455 // 108dp - 21 dp (distance from right edge of safe zone to the edge of the canvas)
456 // - 24 dp (size of the phone icon) on the x axis (left)
457 // The y axis is simply 21dp for the distance to the safe zone (top).
458 // See go/o-icons-eng for more details and a handy picture.
459 final int left = (int) (mIconSize - (45 * density));
460 final int top = (int) (21 * density);
461 canvas.drawBitmap(phoneIcon, left, top, photoPaint);
462 } else {
463 dst.set(iconWidth - ((int) (20 * density)), -1,
464 iconWidth, ((int) (19 * density)));
465 canvas.drawBitmap(phoneIcon, null, dst, photoPaint);
Gary Maia80b9372017-03-21 18:02:19 -0700466 }
Chiao Chengbeca8562012-11-28 18:06:44 -0800467
Gary Mai02c3dee2017-05-05 15:49:11 -0700468 canvas.setBitmap(null);
469 return icon;
Chiao Chengbeca8562012-11-28 18:06:44 -0800470 }
471}