LetterTileProvider.java revision bbe7f92b35c39d80cdd1c0ee88ee12b0ec0564dd
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 = 7; 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_contact_picture); 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 // If its a valid English alphabet letter... 110 if (isEnglishLetterOrDigit(firstChar)) { 111 mFirstChar[0] = Character.toUpperCase(firstChar); 112 bitmap = getBitmap(mDims, false /* getDefault */); 113 if (bitmap == null) { 114 LogUtils.w(TAG, "LetterTileProvider width(%d) or height(%d) is 0" + 115 " for name %s and address %s.", 116 dividedImageView.getWidth(), dividedImageView.getHeight(), displayName, 117 address); 118 return; 119 } 120 final Canvas c = mCanvas; 121 c.setBitmap(bitmap); 122 c.drawColor(pickColor(address)); 123 mPaint.setTextSize(getFontSize(mDims.scale)); 124 mPaint.getTextBounds(mFirstChar, 0, 1, mBounds); 125 c.drawText(mFirstChar, 0, 1, 0 + mDims.width / 2, 126 0 + mDims.height / 2 + (mBounds.bottom - mBounds.top) / 2, mPaint); 127 } else { 128 bitmap = getBitmap(mDims, true /* getDefault */); 129 } 130 dividedImageView.addDivisionImage(bitmap, address); 131 } 132 133 private static boolean isEnglishLetterOrDigit(char c) { 134 return ('A' <= c && c <= 'Z') 135 || ('a' <= c && c <= 'z') 136 || ('0' <= c && c <= '9'); 137 } 138 139 private Bitmap getBitmap(final Dimensions d, boolean getDefault) { 140 if (d.width <= 0 || d.height <= 0) { 141 LogUtils.w(TAG, 142 "LetterTileProvider width(%d) or height(%d) is 0.", d.width, d.height); 143 return null; 144 } 145 final int pos; 146 float scale = d.scale; 147 if (scale == Dimensions.SCALE_ONE) { 148 pos = 0; 149 } else if (scale == Dimensions.SCALE_HALF) { 150 pos = 1; 151 } else { 152 pos = 2; 153 } 154 155 final Bitmap[] cache = (getDefault) ? mDefaultBitmapCache : mBitmapBackgroundCache; 156 157 Bitmap bitmap = cache[pos]; 158 // ensure bitmap is suitable for the desired w/h 159 // (two-pane uses two different sets of dimensions depending on pane width) 160 if (bitmap == null || bitmap.getWidth() != d.width || bitmap.getHeight() != d.height) { 161 // create and place the bitmap 162 if (getDefault) { 163 bitmap = BitmapUtil.centerCrop(mDefaultBitmap, d.width, d.height); 164 } else { 165 bitmap = Bitmap.createBitmap(d.width, d.height, Bitmap.Config.ARGB_8888); 166 } 167 cache[pos] = bitmap; 168 } 169 return bitmap; 170 } 171 172 private int getFontSize(float scale) { 173 if (scale == Dimensions.SCALE_ONE) { 174 return mTileLetterFontSize; 175 } else { 176 return mTileLetterFontSizeSmall; 177 } 178 } 179 180 private int pickColor(String emailAddress) { 181 // String.hashCode() implementation is not supposed to change across java versions, so 182 // this should guarantee the same email address always maps to the same color. 183 int color = Math.abs(emailAddress.hashCode()) % NUM_OF_TILE_COLORS; 184 return mColors.getColor(color, mDefaultColor); 185 } 186} 187