• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.dialer.shortcuts;
18 
19 import android.content.Context;
20 import android.content.pm.ShortcutInfo;
21 import android.graphics.Bitmap;
22 import android.graphics.BitmapFactory;
23 import android.graphics.drawable.AdaptiveIconDrawable;
24 import android.graphics.drawable.Drawable;
25 import android.graphics.drawable.Icon;
26 import android.net.Uri;
27 import android.os.Build.VERSION;
28 import android.os.Build.VERSION_CODES;
29 import android.provider.ContactsContract;
30 import android.support.annotation.NonNull;
31 import android.support.annotation.Nullable;
32 import android.support.annotation.RequiresApi;
33 import android.support.annotation.WorkerThread;
34 import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
35 import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
36 import com.android.contacts.common.lettertiles.LetterTileDrawable;
37 import com.android.dialer.common.Assert;
38 import com.android.dialer.util.DrawableConverter;
39 import java.io.InputStream;
40 
41 /** Constructs the icons for dialer shortcuts. */
42 class IconFactory {
43 
44   private final Context context;
45 
IconFactory(@onNull Context context)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
create(@onNull DialerShortcut shortcut)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
create(@onNull ShortcutInfo shortcutInfo)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
create( @onNull Uri lookupUri, @NonNull String displayName, @NonNull String lookupKey)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 
91     return VERSION.SDK_INT >= VERSION_CODES.O
92         ? createAdaptiveIcon(displayName, lookupKey, inputStream)
93         : createFlatIcon(displayName, lookupKey, inputStream);
94   }
95 
96   @RequiresApi(VERSION_CODES.O)
createAdaptiveIcon( @onNull String displayName, @NonNull String lookupKey, @Nullable InputStream inputStream)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 
createFlatIcon( @onNull String displayName, @NonNull String lookupKey, @Nullable InputStream inputStream)121   private Icon createFlatIcon(
122       @NonNull String displayName, @NonNull String lookupKey, @Nullable InputStream inputStream) {
123     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
createCircularDrawable(@onNull Bitmap bitmap)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 }
150