blob: 7aad1129afd985eefb2797ce5897493efaa9ad6c [file] [log] [blame]
Eric Erfanianccca3152017-02-22 16:32:36 -08001/*
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
17package com.android.dialer.shortcuts;
18
19import android.content.Context;
20import android.content.pm.ShortcutInfo;
21import android.graphics.Bitmap;
22import android.graphics.BitmapFactory;
keyboardrd53c83f2017-06-22 13:53:44 -070023import android.graphics.drawable.AdaptiveIconDrawable;
Eric Erfanianccca3152017-02-22 16:32:36 -080024import android.graphics.drawable.Drawable;
25import android.graphics.drawable.Icon;
26import android.net.Uri;
keyboardrd53c83f2017-06-22 13:53:44 -070027import android.os.Build.VERSION;
28import android.os.Build.VERSION_CODES;
Eric Erfanianccca3152017-02-22 16:32:36 -080029import android.provider.ContactsContract;
30import android.support.annotation.NonNull;
keyboardrd53c83f2017-06-22 13:53:44 -070031import android.support.annotation.Nullable;
32import android.support.annotation.RequiresApi;
Eric Erfanianccca3152017-02-22 16:32:36 -080033import android.support.annotation.WorkerThread;
34import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
35import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
Eric Erfanianccca3152017-02-22 16:32:36 -080036import com.android.dialer.common.Assert;
Eric Erfanianaf2a1a22017-07-24 11:01:58 -070037import com.android.dialer.lettertile.LetterTileDrawable;
Eric Erfanianccca3152017-02-22 16:32:36 -080038import com.android.dialer.util.DrawableConverter;
39import java.io.InputStream;
40
41/** Constructs the icons for dialer shortcuts. */
42class IconFactory {
43
44 private final Context context;
45
46 IconFactory(@NonNull Context context) {
47 this.context = context;
48 }
49
50 /**
51 * Creates an icon for the provided {@link DialerShortcut}.
52 *
53 * <p>The icon is a circle which contains a photo of the contact associated with the shortcut, if
54 * available. If a photo is not available, a circular colored icon with a single letter is instead
55 * created, where the letter is the first letter of the contact's name. If the contact has no
56 * name, a default colored "anonymous" avatar is used.
57 *
58 * <p>These icons should match exactly the favorites tiles in the starred tab of the dialer
59 * application, except that they are circular instead of rectangular.
60 */
61 @WorkerThread
62 @NonNull
63 public Icon create(@NonNull DialerShortcut shortcut) {
64 Assert.isWorkerThread();
65
66 return create(shortcut.getLookupUri(), shortcut.getDisplayName(), shortcut.getLookupKey());
67 }
68
69 /** Same as {@link #create(DialerShortcut)}, but accepts a {@link ShortcutInfo}. */
70 @WorkerThread
71 @NonNull
72 public Icon create(@NonNull ShortcutInfo shortcutInfo) {
73 Assert.isWorkerThread();
74 return create(
75 DialerShortcut.getLookupUriFromShortcutInfo(shortcutInfo),
76 DialerShortcut.getDisplayNameFromShortcutInfo(shortcutInfo),
77 DialerShortcut.getLookupKeyFromShortcutInfo(shortcutInfo));
78 }
79
80 @WorkerThread
81 @NonNull
82 private Icon create(
83 @NonNull Uri lookupUri, @NonNull String displayName, @NonNull String lookupKey) {
84 Assert.isWorkerThread();
85
86 // In testing, there was no difference between high-res and thumbnail.
87 InputStream inputStream =
88 ContactsContract.Contacts.openContactPhotoInputStream(
89 context.getContentResolver(), lookupUri, false /* preferHighres */);
90
keyboardrd53c83f2017-06-22 13:53:44 -070091 return VERSION.SDK_INT >= VERSION_CODES.O
92 ? createAdaptiveIcon(displayName, lookupKey, inputStream)
93 : createFlatIcon(displayName, lookupKey, inputStream);
94 }
95
96 @RequiresApi(VERSION_CODES.O)
97 private Icon createAdaptiveIcon(
98 @NonNull String displayName, @NonNull String lookupKey, @Nullable InputStream inputStream) {
99 if (inputStream == null) {
100 LetterTileDrawable letterTileDrawable = new LetterTileDrawable(context.getResources());
101 // The adaptive icons clip the drawable to a safe area inside the drawable. Scale the letter
102 // so it fits inside the safe area.
103 letterTileDrawable.setScale(1f / (1f + AdaptiveIconDrawable.getExtraInsetFraction()));
104 letterTileDrawable.setCanonicalDialerLetterTileDetails(
105 displayName,
106 lookupKey,
107 LetterTileDrawable.SHAPE_RECTANGLE,
108 LetterTileDrawable.TYPE_DEFAULT);
109
110 int iconSize =
111 context
112 .getResources()
113 .getDimensionPixelSize(R.dimen.launcher_shortcut_adaptive_icon_size);
114 return Icon.createWithAdaptiveBitmap(
115 DrawableConverter.drawableToBitmap(letterTileDrawable, iconSize, iconSize));
116 }
117 Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
118 return Icon.createWithAdaptiveBitmap(bitmap);
119 }
120
121 private Icon createFlatIcon(
122 @NonNull String displayName, @NonNull String lookupKey, @Nullable InputStream inputStream) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800123 Drawable drawable;
124 if (inputStream == null) {
125 // No photo for contact; use a letter tile.
126 LetterTileDrawable letterTileDrawable = new LetterTileDrawable(context.getResources());
127 letterTileDrawable.setCanonicalDialerLetterTileDetails(
128 displayName, lookupKey, LetterTileDrawable.SHAPE_CIRCLE, LetterTileDrawable.TYPE_DEFAULT);
129 drawable = letterTileDrawable;
130 } else {
131 // There's a photo, create a circular drawable from it.
132 Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
133 drawable = createCircularDrawable(bitmap);
134 }
135 int iconSize =
136 context.getResources().getDimensionPixelSize(R.dimen.launcher_shortcut_icon_size);
137 return Icon.createWithBitmap(
138 DrawableConverter.drawableToBitmap(drawable, iconSize /* width */, iconSize /* height */));
139 }
140
141 @NonNull
142 private Drawable createCircularDrawable(@NonNull Bitmap bitmap) {
143 RoundedBitmapDrawable roundedBitmapDrawable =
144 RoundedBitmapDrawableFactory.create(context.getResources(), bitmap);
145 roundedBitmapDrawable.setCircular(true);
146 roundedBitmapDrawable.setAntiAlias(true);
147 return roundedBitmapDrawable;
148 }
149}