1ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian/* 2ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * Copyright (C) 2016 The Android Open Source Project 3ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * 4ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * Licensed under the Apache License, Version 2.0 (the "License"); 5ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * you may not use this file except in compliance with the License. 6ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * You may obtain a copy of the License at 7ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * 8ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * http://www.apache.org/licenses/LICENSE-2.0 9ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * 10ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * Unless required by applicable law or agreed to in writing, software 11ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * distributed under the License is distributed on an "AS IS" BASIS, 12ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * See the License for the specific language governing permissions and 14ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * limitations under the License. 15ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian */ 16ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 17ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianpackage com.android.dialer.shortcuts; 18ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 19ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.content.Context; 20ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.content.pm.ShortcutInfo; 21ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.graphics.Bitmap; 22ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.graphics.BitmapFactory; 23d53c83faca6b8b501d961528f917314d0c817969keyboardrimport android.graphics.drawable.AdaptiveIconDrawable; 24ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.graphics.drawable.Drawable; 25ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.graphics.drawable.Icon; 26ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.net.Uri; 27d53c83faca6b8b501d961528f917314d0c817969keyboardrimport android.os.Build.VERSION; 28d53c83faca6b8b501d961528f917314d0c817969keyboardrimport android.os.Build.VERSION_CODES; 29ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.provider.ContactsContract; 30ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.support.annotation.NonNull; 31d53c83faca6b8b501d961528f917314d0c817969keyboardrimport android.support.annotation.Nullable; 32d53c83faca6b8b501d961528f917314d0c817969keyboardrimport android.support.annotation.RequiresApi; 33ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.support.annotation.WorkerThread; 34ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.support.v4.graphics.drawable.RoundedBitmapDrawable; 35ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory; 36ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.contacts.common.lettertiles.LetterTileDrawable; 37ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.dialer.common.Assert; 38ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.dialer.util.DrawableConverter; 39ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport java.io.InputStream; 40ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 41ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian/** Constructs the icons for dialer shortcuts. */ 42ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianclass IconFactory { 43ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 44ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private final Context context; 45ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 46ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian IconFactory(@NonNull Context context) { 47ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian this.context = context; 48ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 49ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 50ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian /** 51ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * Creates an icon for the provided {@link DialerShortcut}. 52ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * 53ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * <p>The icon is a circle which contains a photo of the contact associated with the shortcut, if 54ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * available. If a photo is not available, a circular colored icon with a single letter is instead 55ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * created, where the letter is the first letter of the contact's name. If the contact has no 56ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * name, a default colored "anonymous" avatar is used. 57ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * 58ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * <p>These icons should match exactly the favorites tiles in the starred tab of the dialer 59ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * application, except that they are circular instead of rectangular. 60ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian */ 61ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @WorkerThread 62ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @NonNull 63ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian public Icon create(@NonNull DialerShortcut shortcut) { 64ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian Assert.isWorkerThread(); 65ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 66ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian return create(shortcut.getLookupUri(), shortcut.getDisplayName(), shortcut.getLookupKey()); 67ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 68ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 69ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian /** Same as {@link #create(DialerShortcut)}, but accepts a {@link ShortcutInfo}. */ 70ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @WorkerThread 71ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @NonNull 72ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian public Icon create(@NonNull ShortcutInfo shortcutInfo) { 73ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian Assert.isWorkerThread(); 74ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian return create( 75ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian DialerShortcut.getLookupUriFromShortcutInfo(shortcutInfo), 76ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian DialerShortcut.getDisplayNameFromShortcutInfo(shortcutInfo), 77ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian DialerShortcut.getLookupKeyFromShortcutInfo(shortcutInfo)); 78ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 79ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 80ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @WorkerThread 81ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @NonNull 82ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private Icon create( 83ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @NonNull Uri lookupUri, @NonNull String displayName, @NonNull String lookupKey) { 84ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian Assert.isWorkerThread(); 85ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 86ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // In testing, there was no difference between high-res and thumbnail. 87ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian InputStream inputStream = 88ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian ContactsContract.Contacts.openContactPhotoInputStream( 89ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian context.getContentResolver(), lookupUri, false /* preferHighres */); 90ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 91d53c83faca6b8b501d961528f917314d0c817969keyboardr return VERSION.SDK_INT >= VERSION_CODES.O 92d53c83faca6b8b501d961528f917314d0c817969keyboardr ? createAdaptiveIcon(displayName, lookupKey, inputStream) 93d53c83faca6b8b501d961528f917314d0c817969keyboardr : createFlatIcon(displayName, lookupKey, inputStream); 94d53c83faca6b8b501d961528f917314d0c817969keyboardr } 95d53c83faca6b8b501d961528f917314d0c817969keyboardr 96d53c83faca6b8b501d961528f917314d0c817969keyboardr @RequiresApi(VERSION_CODES.O) 97d53c83faca6b8b501d961528f917314d0c817969keyboardr private Icon createAdaptiveIcon( 98d53c83faca6b8b501d961528f917314d0c817969keyboardr @NonNull String displayName, @NonNull String lookupKey, @Nullable InputStream inputStream) { 99d53c83faca6b8b501d961528f917314d0c817969keyboardr if (inputStream == null) { 100d53c83faca6b8b501d961528f917314d0c817969keyboardr LetterTileDrawable letterTileDrawable = new LetterTileDrawable(context.getResources()); 101d53c83faca6b8b501d961528f917314d0c817969keyboardr // The adaptive icons clip the drawable to a safe area inside the drawable. Scale the letter 102d53c83faca6b8b501d961528f917314d0c817969keyboardr // so it fits inside the safe area. 103d53c83faca6b8b501d961528f917314d0c817969keyboardr letterTileDrawable.setScale(1f / (1f + AdaptiveIconDrawable.getExtraInsetFraction())); 104d53c83faca6b8b501d961528f917314d0c817969keyboardr letterTileDrawable.setCanonicalDialerLetterTileDetails( 105d53c83faca6b8b501d961528f917314d0c817969keyboardr displayName, 106d53c83faca6b8b501d961528f917314d0c817969keyboardr lookupKey, 107d53c83faca6b8b501d961528f917314d0c817969keyboardr LetterTileDrawable.SHAPE_RECTANGLE, 108d53c83faca6b8b501d961528f917314d0c817969keyboardr LetterTileDrawable.TYPE_DEFAULT); 109d53c83faca6b8b501d961528f917314d0c817969keyboardr 110d53c83faca6b8b501d961528f917314d0c817969keyboardr int iconSize = 111d53c83faca6b8b501d961528f917314d0c817969keyboardr context 112d53c83faca6b8b501d961528f917314d0c817969keyboardr .getResources() 113d53c83faca6b8b501d961528f917314d0c817969keyboardr .getDimensionPixelSize(R.dimen.launcher_shortcut_adaptive_icon_size); 114d53c83faca6b8b501d961528f917314d0c817969keyboardr return Icon.createWithAdaptiveBitmap( 115d53c83faca6b8b501d961528f917314d0c817969keyboardr DrawableConverter.drawableToBitmap(letterTileDrawable, iconSize, iconSize)); 116d53c83faca6b8b501d961528f917314d0c817969keyboardr } 117d53c83faca6b8b501d961528f917314d0c817969keyboardr Bitmap bitmap = BitmapFactory.decodeStream(inputStream); 118d53c83faca6b8b501d961528f917314d0c817969keyboardr return Icon.createWithAdaptiveBitmap(bitmap); 119d53c83faca6b8b501d961528f917314d0c817969keyboardr } 120d53c83faca6b8b501d961528f917314d0c817969keyboardr 121d53c83faca6b8b501d961528f917314d0c817969keyboardr private Icon createFlatIcon( 122d53c83faca6b8b501d961528f917314d0c817969keyboardr @NonNull String displayName, @NonNull String lookupKey, @Nullable InputStream inputStream) { 123ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian Drawable drawable; 124ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian if (inputStream == null) { 125ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // No photo for contact; use a letter tile. 126ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian LetterTileDrawable letterTileDrawable = new LetterTileDrawable(context.getResources()); 127ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian letterTileDrawable.setCanonicalDialerLetterTileDetails( 128ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian displayName, lookupKey, LetterTileDrawable.SHAPE_CIRCLE, LetterTileDrawable.TYPE_DEFAULT); 129ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian drawable = letterTileDrawable; 130ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } else { 131ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // There's a photo, create a circular drawable from it. 132ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian Bitmap bitmap = BitmapFactory.decodeStream(inputStream); 133ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian drawable = createCircularDrawable(bitmap); 134ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 135ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian int iconSize = 136ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian context.getResources().getDimensionPixelSize(R.dimen.launcher_shortcut_icon_size); 137ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian return Icon.createWithBitmap( 138ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian DrawableConverter.drawableToBitmap(drawable, iconSize /* width */, iconSize /* height */)); 139ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 140ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 141ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @NonNull 142ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private Drawable createCircularDrawable(@NonNull Bitmap bitmap) { 143ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian RoundedBitmapDrawable roundedBitmapDrawable = 144ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian RoundedBitmapDrawableFactory.create(context.getResources(), bitmap); 145ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian roundedBitmapDrawable.setCircular(true); 146ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian roundedBitmapDrawable.setAntiAlias(true); 147ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian return roundedBitmapDrawable; 148ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 149ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian} 150