1 /*
2  * Copyright (C) 2015 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 package com.android.messaging.datamodel.media;
17 
18 import android.content.Context;
19 import android.content.res.Resources;
20 import android.graphics.Bitmap;
21 import android.graphics.Canvas;
22 import android.graphics.Matrix;
23 import android.graphics.Paint;
24 import android.graphics.Rect;
25 import android.graphics.RectF;
26 import android.graphics.Typeface;
27 import android.graphics.drawable.BitmapDrawable;
28 import android.media.ExifInterface;
29 import android.net.Uri;
30 
31 import com.android.messaging.R;
32 import com.android.messaging.util.Assert;
33 import com.android.messaging.util.AvatarUriUtil;
34 import com.android.messaging.util.LogUtil;
35 import com.android.messaging.util.UriUtil;
36 
37 import java.io.FileNotFoundException;
38 import java.io.IOException;
39 import java.io.InputStream;
40 import java.util.List;
41 
42 public class AvatarRequest extends UriImageRequest<AvatarRequestDescriptor> {
43     private static Bitmap sDefaultPersonBitmap;
44     private static Bitmap sDefaultPersonBitmapLarge;
45 
AvatarRequest(final Context context, final AvatarRequestDescriptor descriptor)46     public AvatarRequest(final Context context,
47             final AvatarRequestDescriptor descriptor) {
48         super(context, descriptor);
49     }
50 
51     @Override
getInputStreamForResource()52     protected InputStream getInputStreamForResource() throws FileNotFoundException {
53         if (UriUtil.isLocalResourceUri(mDescriptor.uri)) {
54             return super.getInputStreamForResource();
55         } else {
56             final Uri primaryUri = AvatarUriUtil.getPrimaryUri(mDescriptor.uri);
57             Assert.isTrue(UriUtil.isLocalResourceUri(primaryUri));
58             return mContext.getContentResolver().openInputStream(primaryUri);
59         }
60     }
61 
62     /**
63      * We can load multiple types of images for avatars depending on the uri. The uri should be
64      * built by {@link com.android.messaging.util.AvatarUriUtil} which will decide on
65      * what uri to build based on the available profile photo and name. Here we will check if the
66      * image is a local resource (ie profile photo uri), if the resource isn't a local one we will
67      * generate a tile with the first letter of the name.
68      */
69     @Override
loadMediaInternal(List<MediaRequest<ImageResource>> chainedTasks)70     protected ImageResource loadMediaInternal(List<MediaRequest<ImageResource>> chainedTasks)
71             throws IOException {
72         Assert.isNotMainThread();
73         String avatarType = AvatarUriUtil.getAvatarType(mDescriptor.uri);
74         Bitmap bitmap = null;
75         int orientation = ExifInterface.ORIENTATION_NORMAL;
76         final boolean isLocalResourceUri = UriUtil.isLocalResourceUri(mDescriptor.uri) ||
77                 AvatarUriUtil.TYPE_LOCAL_RESOURCE_URI.equals(avatarType);
78         if (isLocalResourceUri) {
79             try {
80                 ImageResource imageResource = super.loadMediaInternal(chainedTasks);
81                 bitmap = imageResource.getBitmap();
82                 orientation = imageResource.mOrientation;
83             } catch (Exception ex) {
84                 // If we encountered any exceptions trying to load the local avatar resource,
85                 // fall back to generated avatar.
86                 LogUtil.w(LogUtil.BUGLE_IMAGE_TAG, "AvatarRequest: failed to load local avatar " +
87                         "resource, switching to fallback rendering", ex);
88             }
89         }
90 
91         final int width = mDescriptor.desiredWidth;
92         final int height = mDescriptor.desiredHeight;
93         // Check to see if we already got the bitmap. If not get a fallback avatar
94         if (bitmap == null) {
95             Uri generatedUri = mDescriptor.uri;
96             if (isLocalResourceUri) {
97                 // If we are here, we just failed to load the local resource. Use the fallback Uri
98                 // if possible.
99                 generatedUri = AvatarUriUtil.getFallbackUri(mDescriptor.uri);
100                 if (generatedUri == null) {
101                     // No fallback Uri was provided, use the default avatar.
102                     generatedUri = AvatarUriUtil.DEFAULT_BACKGROUND_AVATAR;
103                 }
104             }
105 
106             avatarType = AvatarUriUtil.getAvatarType(generatedUri);
107             if (AvatarUriUtil.TYPE_LETTER_TILE_URI.equals(avatarType)) {
108                 final String name = AvatarUriUtil.getName(generatedUri);
109                 bitmap = renderLetterTile(name, width, height);
110             } else {
111                 bitmap = renderDefaultAvatar(width, height);
112             }
113         }
114         return new DecodedImageResource(getKey(), bitmap, orientation);
115     }
116 
renderDefaultAvatar(final int width, final int height)117     private Bitmap renderDefaultAvatar(final int width, final int height) {
118         final Bitmap bitmap = getBitmapPool().createOrReuseBitmap(width, height,
119                 getBackgroundColor());
120         final Canvas canvas = new Canvas(bitmap);
121 
122         if (sDefaultPersonBitmap == null) {
123             final BitmapDrawable defaultPerson = (BitmapDrawable) mContext.getResources()
124                     .getDrawable(R.drawable.ic_person_light);
125             sDefaultPersonBitmap = defaultPerson.getBitmap();
126         }
127         if (sDefaultPersonBitmapLarge == null) {
128             final BitmapDrawable largeDefaultPerson = (BitmapDrawable) mContext.getResources()
129                     .getDrawable(R.drawable.ic_person_light_large);
130             sDefaultPersonBitmapLarge = largeDefaultPerson.getBitmap();
131         }
132 
133         Bitmap defaultPerson = null;
134         if (mDescriptor.isWearBackground) {
135             final BitmapDrawable wearDefaultPerson = (BitmapDrawable) mContext.getResources()
136                     .getDrawable(R.drawable.ic_person_wear);
137             defaultPerson = wearDefaultPerson.getBitmap();
138         } else {
139             final boolean isLargeDefault = (width > sDefaultPersonBitmap.getWidth()) ||
140                     (height > sDefaultPersonBitmap.getHeight());
141             defaultPerson =
142                     isLargeDefault ? sDefaultPersonBitmapLarge : sDefaultPersonBitmap;
143         }
144 
145         final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
146         final Matrix matrix = new Matrix();
147         final RectF source = new RectF(0, 0, defaultPerson.getWidth(), defaultPerson.getHeight());
148         final RectF dest = new RectF(0, 0, width, height);
149         matrix.setRectToRect(source, dest, Matrix.ScaleToFit.FILL);
150 
151         canvas.drawBitmap(defaultPerson, matrix, paint);
152 
153         return bitmap;
154     }
155 
renderLetterTile(final String name, final int width, final int height)156     private Bitmap renderLetterTile(final String name, final int width, final int height) {
157         final float halfWidth = width / 2;
158         final float halfHeight = height / 2;
159         final int minOfWidthAndHeight = Math.min(width, height);
160         final Bitmap bitmap = getBitmapPool().createOrReuseBitmap(width, height,
161                 getBackgroundColor());
162         final Resources resources = mContext.getResources();
163         final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
164         paint.setTypeface(Typeface.create("sans-serif-thin", Typeface.NORMAL));
165         paint.setColor(resources.getColor(R.color.letter_tile_font_color));
166         final float letterToTileRatio = resources.getFraction(R.dimen.letter_to_tile_ratio, 1, 1);
167         paint.setTextSize(letterToTileRatio * minOfWidthAndHeight);
168 
169         final String firstCharString = name.substring(0, 1).toUpperCase();
170         final Rect textBound = new Rect();
171         paint.getTextBounds(firstCharString, 0, 1, textBound);
172 
173         final Canvas canvas = new Canvas(bitmap);
174         final float xOffset = halfWidth - textBound.centerX();
175         final float yOffset = halfHeight - textBound.centerY();
176         canvas.drawText(firstCharString, xOffset, yOffset, paint);
177 
178         return bitmap;
179     }
180 
getBackgroundColor()181     private int getBackgroundColor() {
182         return mContext.getResources().getColor(R.color.primary_color);
183     }
184 
185     @Override
getCacheId()186     public int getCacheId() {
187         return BugleMediaCacheManager.AVATAR_IMAGE_CACHE;
188     }
189 }
190