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