LetterTileProvider.java revision 3b965d78774a42358ce6bbdcc43b4c8df130a60e
1/* 2 * Copyright (C) 2013 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.mail.photomanager; 18 19import android.content.Context; 20import android.content.res.Resources; 21import android.content.res.TypedArray; 22import android.graphics.Bitmap; 23import android.graphics.BitmapFactory; 24import android.graphics.Canvas; 25import android.graphics.Paint.Align; 26import android.graphics.Rect; 27import android.graphics.Typeface; 28import android.text.TextPaint; 29import android.text.TextUtils; 30 31import com.android.mail.R; 32import com.android.mail.photomanager.ContactPhotoManager.ContactIdentifier; 33import com.android.mail.photomanager.PhotoManager.DefaultImageProvider; 34import com.android.mail.photomanager.PhotoManager.PhotoIdentifier; 35import com.android.mail.ui.DividedImageCanvas; 36import com.android.mail.ui.ImageCanvas; 37import com.android.mail.ui.ImageCanvas.Dimensions; 38import com.android.mail.utils.LogTag; 39import com.android.mail.utils.LogUtils; 40 41/** 42 * LetterTileProvider is an implementation of the DefaultImageProvider. When no 43 * matching contact photo is found, and there is a supplied displayName or email 44 * address whose first letter corresponds to an English alphabet letter (or 45 * number), this method creates a bitmap with the letter in the center of a 46 * tile. If there is no English alphabet character (or digit), it creates a 47 * bitmap with the default contact avatar. 48 */ 49public class LetterTileProvider implements DefaultImageProvider { 50 private static final String TAG = LogTag.getLogTag(); 51 private final Bitmap mDefaultBitmap; 52 private final Bitmap[] mBitmapBackgroundCache; 53 private final Bitmap[] mDefaultBitmapCache; 54 private final Typeface mSansSerifLight; 55 private final Rect mBounds; 56 private final int mTileLetterFontSize; 57 private final int mTileLetterFontSizeSmall; 58 private final int mTileFontColor; 59 private final TextPaint mPaint = new TextPaint(); 60 private final TypedArray mColors; 61 private final int mDefaultColor; 62 private final Canvas mCanvas = new Canvas(); 63 private final Dimensions mDims = new Dimensions(); 64 private final char[] mFirstChar = new char[1]; 65 66 private static final int POSSIBLE_BITMAP_SIZES = 3; 67 68 // This should match the total number of colors defined in colors.xml for letter_tile_color 69 private static final int NUM_OF_TILE_COLORS = 8; 70 71 public LetterTileProvider(Context context) { 72 final Resources res = context.getResources(); 73 mTileLetterFontSize = res.getDimensionPixelSize(R.dimen.tile_letter_font_size); 74 mTileLetterFontSizeSmall = res 75 .getDimensionPixelSize(R.dimen.tile_letter_font_size_small); 76 mTileFontColor = res.getColor(R.color.letter_tile_font_color); 77 mSansSerifLight = Typeface.create("sans-serif-light", Typeface.NORMAL); 78 mBounds = new Rect(); 79 mPaint.setTypeface(mSansSerifLight); 80 mPaint.setColor(mTileFontColor); 81 mPaint.setTextAlign(Align.CENTER); 82 mPaint.setAntiAlias(true); 83 mBitmapBackgroundCache = new Bitmap[POSSIBLE_BITMAP_SIZES]; 84 85 mDefaultBitmap = BitmapFactory.decodeResource(res, R.drawable.ic_generic_man); 86 mDefaultBitmapCache = new Bitmap[POSSIBLE_BITMAP_SIZES]; 87 88 mColors = res.obtainTypedArray(R.array.letter_tile_colors); 89 mDefaultColor = res.getColor(R.color.letter_tile_default_color); 90 } 91 92 @Override 93 public void applyDefaultImage(PhotoIdentifier id, ImageCanvas view, int extent) { 94 ContactIdentifier contactIdentifier = (ContactIdentifier) id; 95 DividedImageCanvas dividedImageView = (DividedImageCanvas) view; 96 97 final String displayName = contactIdentifier.name; 98 final String address = contactIdentifier.emailAddress; 99 100 // don't apply again if existing letter is there (and valid) 101 if (dividedImageView.hasImageFor(address)) { 102 return; 103 } 104 105 Bitmap bitmap = null; 106 final String display = !TextUtils.isEmpty(displayName) ? displayName : address; 107 final char firstChar = display.charAt(0); 108 dividedImageView.getDesiredDimensions(address, mDims); 109 110 // get an empty bitmap 111 bitmap = getBitmap(mDims, false /* getDefault */); 112 if (bitmap == null) { 113 LogUtils.w(TAG, "LetterTileProvider width(%d) or height(%d) is 0" + 114 " for name %s and address %s.", 115 dividedImageView.getWidth(), dividedImageView.getHeight(), displayName, 116 address); 117 return; 118 } 119 120 final Canvas c = mCanvas; 121 c.setBitmap(bitmap); 122 c.drawColor(pickColor(address)); 123 124 // If its a valid English alphabet letter, 125 // draw the letter on top of the color 126 if (isEnglishLetterOrDigit(firstChar)) { 127 mFirstChar[0] = Character.toUpperCase(firstChar); 128 mPaint.setTextSize(getFontSize(mDims.scale)); 129 mPaint.getTextBounds(mFirstChar, 0, 1, mBounds); 130 c.drawText(mFirstChar, 0, 1, 0 + mDims.width / 2, 131 0 + mDims.height / 2 + (mBounds.bottom - mBounds.top) / 2, mPaint); 132 } else { // draw the generic icon on top 133 c.drawBitmap(getBitmap(mDims, true /* getDefault */), 0, 0, null); 134 } 135 136 dividedImageView.addDivisionImage(bitmap, address); 137 } 138 139 private static boolean isEnglishLetterOrDigit(char c) { 140 return ('A' <= c && c <= 'Z') 141 || ('a' <= c && c <= 'z') 142 || ('0' <= c && c <= '9'); 143 } 144 145 private Bitmap getBitmap(final Dimensions d, boolean getDefault) { 146 if (d.width <= 0 || d.height <= 0) { 147 LogUtils.w(TAG, 148 "LetterTileProvider width(%d) or height(%d) is 0.", d.width, d.height); 149 return null; 150 } 151 final int pos; 152 float scale = d.scale; 153 if (scale == Dimensions.SCALE_ONE) { 154 pos = 0; 155 } else if (scale == Dimensions.SCALE_HALF) { 156 pos = 1; 157 } else { 158 pos = 2; 159 } 160 161 final Bitmap[] cache = (getDefault) ? mDefaultBitmapCache : mBitmapBackgroundCache; 162 163 Bitmap bitmap = cache[pos]; 164 // ensure bitmap is suitable for the desired w/h 165 // (two-pane uses two different sets of dimensions depending on pane width) 166 if (bitmap == null || bitmap.getWidth() != d.width || bitmap.getHeight() != d.height) { 167 // create and place the bitmap 168 if (getDefault) { 169 bitmap = BitmapUtil.centerCrop(mDefaultBitmap, d.width, d.height); 170 } else { 171 bitmap = Bitmap.createBitmap(d.width, d.height, Bitmap.Config.ARGB_8888); 172 } 173 cache[pos] = bitmap; 174 } 175 return bitmap; 176 } 177 178 private int getFontSize(float scale) { 179 if (scale == Dimensions.SCALE_ONE) { 180 return mTileLetterFontSize; 181 } else { 182 return mTileLetterFontSizeSmall; 183 } 184 } 185 186 private int pickColor(String emailAddress) { 187 // String.hashCode() implementation is not supposed to change across java versions, so 188 // this should guarantee the same email address always maps to the same color. 189 int color = Math.abs(emailAddress.hashCode()) % NUM_OF_TILE_COLORS; 190 return mColors.getColor(color, mDefaultColor); 191 } 192} 193