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.app.contactinfo;
18 
19 import android.content.Context;
20 import android.graphics.Bitmap;
21 import android.graphics.BitmapFactory;
22 import android.graphics.Canvas;
23 import android.graphics.drawable.Drawable;
24 import android.support.annotation.Nullable;
25 import android.support.annotation.VisibleForTesting;
26 import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
27 import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
28 import com.android.dialer.app.R;
29 import com.android.dialer.common.Assert;
30 import com.android.dialer.common.LogUtil;
31 import com.android.dialer.lettertile.LetterTileDrawable;
32 import com.android.dialer.location.GeoUtil;
33 import com.android.dialer.phonenumbercache.ContactInfo;
34 import com.android.dialer.phonenumbercache.ContactInfoHelper;
35 import java.io.IOException;
36 import java.io.InputStream;
37 import java.util.Objects;
38 
39 /**
40  * Class to create the appropriate contact icon from a ContactInfo. This class is for synchronous,
41  * blocking calls to generate bitmaps, while ContactCommons.ContactPhotoManager is to cache, manage
42  * and update a ImageView asynchronously.
43  */
44 public class ContactPhotoLoader {
45 
46   private final Context context;
47   private final ContactInfo contactInfo;
48 
ContactPhotoLoader(Context context, ContactInfo contactInfo)49   public ContactPhotoLoader(Context context, ContactInfo contactInfo) {
50     this.context = Objects.requireNonNull(context);
51     this.contactInfo = Objects.requireNonNull(contactInfo);
52   }
53 
drawableToBitmap(Drawable drawable, int width, int height)54   private static Bitmap drawableToBitmap(Drawable drawable, int width, int height) {
55     Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
56     Canvas canvas = new Canvas(bitmap);
57     drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
58     drawable.draw(canvas);
59     return bitmap;
60   }
61 
62   /** Create a contact photo icon bitmap appropriate for the ContactInfo. */
loadPhotoIcon()63   public Bitmap loadPhotoIcon() {
64     Assert.isWorkerThread();
65     int photoSize = context.getResources().getDimensionPixelSize(R.dimen.contact_photo_size);
66     return drawableToBitmap(getIcon(), photoSize, photoSize);
67   }
68 
69   @VisibleForTesting
getIcon()70   Drawable getIcon() {
71     Drawable drawable = createPhotoIconDrawable();
72     if (drawable == null) {
73       drawable = createLetterTileDrawable();
74     }
75     return drawable;
76   }
77 
78   /**
79    * @return a {@link Drawable} of circular photo icon if the photo can be loaded, {@code null}
80    *     otherwise.
81    */
82   @Nullable
createPhotoIconDrawable()83   private Drawable createPhotoIconDrawable() {
84     if (contactInfo.photoUri == null) {
85       return null;
86     }
87     try {
88       InputStream input = context.getContentResolver().openInputStream(contactInfo.photoUri);
89       if (input == null) {
90         LogUtil.w(
91             "ContactPhotoLoader.createPhotoIconDrawable",
92             "createPhotoIconDrawable: InputStream is null");
93         return null;
94       }
95       Bitmap bitmap = BitmapFactory.decodeStream(input);
96       input.close();
97 
98       if (bitmap == null) {
99         LogUtil.w(
100             "ContactPhotoLoader.createPhotoIconDrawable",
101             "createPhotoIconDrawable: Bitmap is null");
102         return null;
103       }
104       final RoundedBitmapDrawable drawable =
105           RoundedBitmapDrawableFactory.create(context.getResources(), bitmap);
106       drawable.setAntiAlias(true);
107       drawable.setCircular(true);
108       return drawable;
109     } catch (IOException e) {
110       LogUtil.e("ContactPhotoLoader.createPhotoIconDrawable", e.toString());
111       return null;
112     }
113   }
114 
115   /** @return a {@link LetterTileDrawable} based on the ContactInfo. */
createLetterTileDrawable()116   private Drawable createLetterTileDrawable() {
117     ContactInfoHelper helper =
118         new ContactInfoHelper(context, GeoUtil.getCurrentCountryIso(context));
119     LetterTileDrawable drawable = new LetterTileDrawable(context.getResources());
120     drawable.setCanonicalDialerLetterTileDetails(
121         contactInfo.name,
122         contactInfo.lookupKey,
123         LetterTileDrawable.SHAPE_CIRCLE,
124         helper.isBusiness(contactInfo.sourceType)
125             ? LetterTileDrawable.TYPE_BUSINESS
126             : LetterTileDrawable.TYPE_DEFAULT);
127     return drawable;
128   }
129 }
130