LetterTileProvider.java revision bd390369b0b4e503463b92a0bbffdbaf5c7d0b9a
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.res.Resources; 20import android.content.res.TypedArray; 21import android.graphics.Bitmap; 22import android.graphics.BitmapFactory; 23import android.graphics.Canvas; 24import android.graphics.Paint.Align; 25import android.graphics.Rect; 26import android.graphics.Typeface; 27import android.text.TextPaint; 28import android.text.TextUtils; 29 30import com.android.mail.R; 31import com.android.mail.photomanager.ContactPhotoManager.ContactIdentifier; 32import com.android.mail.photomanager.PhotoManager.DefaultImageProvider; 33import com.android.mail.photomanager.PhotoManager.PhotoIdentifier; 34import com.android.mail.ui.DividedImageCanvas; 35import com.android.mail.ui.ImageCanvas; 36import com.android.mail.utils.LogTag; 37import com.android.mail.utils.LogUtils; 38 39import java.util.regex.Matcher; 40import java.util.regex.Pattern; 41 42/** 43 * LetterTileProvider is an implementation of the DefaultImageProvider. When no 44 * matching contact photo is found, and there is a supplied displayName or email 45 * address whose first letter corresponds to an English alphabet letter (or 46 * number), this method creates a bitmap with the letter in the center of a 47 * tile. If there is no English alphabet character (or digit), it creates a 48 * bitmap with the default contact avatar. 49 */ 50public class LetterTileProvider implements DefaultImageProvider { 51 private static final String TAG = LogTag.getLogTag(); 52 private Bitmap mDefaultBitmap; 53 private static Bitmap[] sBitmapBackgroundCache; 54 private static Typeface sSansSerifLight; 55 private static Rect sBounds; 56 private static int sTileLetterFontSize = -1; 57 private static int sTileLetterFontSizeSmall; 58 private static int sTileFontColor; 59 private static TextPaint sPaint = new TextPaint(); 60 private static int DEFAULT_AVATAR_DRAWABLE = R.drawable.ic_contact_picture; 61 private static final Pattern ALPHABET = Pattern.compile("^[a-zA-Z0-9]+$"); 62 private static final int POSSIBLE_BITMAP_SIZES = 3; 63 64 // This should match the total number of colors defined in colors.xml for letter_tile_color 65 private static final int NUM_OF_TILE_COLORS = 8; 66 67 public LetterTileProvider() { 68 super(); 69 } 70 71 @Override 72 public void applyDefaultImage(PhotoIdentifier id, ImageCanvas view, int extent) { 73 ContactIdentifier contactIdentifier = (ContactIdentifier) id; 74 DividedImageCanvas dividedImageView = (DividedImageCanvas) view; 75 76 String displayName = contactIdentifier.name; 77 String address = contactIdentifier.emailAddress; 78 79 Bitmap bitmap = null; 80 final String display = !TextUtils.isEmpty(displayName) ? displayName : address; 81 final String firstChar = display.substring(0, 1); 82 // If its a valid english alphabet letter... 83 if (isLetter(firstChar)) { 84 final Resources res = dividedImageView.getContext().getResources(); 85 if (sTileLetterFontSize == -1) { 86 sTileLetterFontSize = res.getDimensionPixelSize(R.dimen.tile_letter_font_size); 87 sTileLetterFontSizeSmall = res 88 .getDimensionPixelSize(R.dimen.tile_letter_font_size_small); 89 sTileFontColor = res.getColor(R.color.letter_tile_font_color); 90 sSansSerifLight = Typeface.create("sans-serif-light", Typeface.NORMAL); 91 sBounds = new Rect(); 92 sPaint.setTypeface(sSansSerifLight); 93 sPaint.setColor(sTileFontColor); 94 sPaint.setTextAlign(Align.CENTER); 95 sPaint.setAntiAlias(true); 96 sBitmapBackgroundCache = new Bitmap[POSSIBLE_BITMAP_SIZES]; 97 } 98 final String first = firstChar.toUpperCase(); 99 DividedImageCanvas.Dimensions d = dividedImageView.getDesiredDimensions(address); 100 bitmap = getBitmap(d); 101 if (bitmap == null) { 102 LogUtils.w(TAG, 103 "LetterTileProvider width(%d) or height(%d) is 0 for name %s and address %s.", 104 dividedImageView.getWidth(), dividedImageView.getHeight(), displayName, 105 address); 106 return; 107 } 108 Canvas c = new Canvas(bitmap); 109 c.drawColor(pickColor(res, address)); 110 sPaint.setTextSize(getFontSize(d.scale)); 111 sPaint.getTextBounds(first, 0, first.length(), sBounds); 112 c.drawText(first, 0 + d.width / 2, 0 + d.height / 2 + (sBounds.bottom - sBounds.top) 113 / 2, sPaint); 114 } else { 115 if (mDefaultBitmap == null) { 116 BitmapFactory.Options options = new BitmapFactory.Options(); 117 options.inMutable = true; 118 mDefaultBitmap = BitmapFactory.decodeResource(dividedImageView.getContext().getResources(), 119 DEFAULT_AVATAR_DRAWABLE, options); 120 } 121 bitmap = mDefaultBitmap; 122 } 123 dividedImageView.addDivisionImage(bitmap, address); 124 } 125 126 private static Bitmap getBitmap(final DividedImageCanvas.Dimensions d) { 127 if (d.width <= 0 || d.height <= 0) { 128 LogUtils.w(TAG, 129 "LetterTileProvider width(%d) or height(%d) is 0.", d.width, d.height); 130 return null; 131 } 132 final int pos; 133 float scale = d.scale; 134 if (scale == DividedImageCanvas.ONE) { 135 pos = 0; 136 } else if (scale == DividedImageCanvas.HALF) { 137 pos = 1; 138 } else { 139 pos = 2; 140 } 141 Bitmap bitmap = sBitmapBackgroundCache[pos]; 142 if (bitmap == null) { 143 // create and place the bitmap 144 bitmap = Bitmap.createBitmap(d.width, d.height, Bitmap.Config.ARGB_8888); 145 sBitmapBackgroundCache[pos] = bitmap; 146 } 147 return bitmap; 148 } 149 150 private static int getFontSize(float scale) { 151 if (scale == DividedImageCanvas.ONE) { 152 return sTileLetterFontSize; 153 } else { 154 return sTileLetterFontSizeSmall; 155 } 156 } 157 158 private static boolean isLetter(String letter) { 159 Matcher m = ALPHABET.matcher(letter); 160 return m.matches(); 161 } 162 163 private static int pickColor(Resources res, String emailAddress) { 164 // String.hashCode() implementation is not supposed to change across java versions, so 165 // this should guarantee the same email address always maps to the same color. 166 int color = Math.abs(emailAddress.hashCode()) % NUM_OF_TILE_COLORS; 167 TypedArray colors = res.obtainTypedArray(R.array.letter_tile_colors); 168 return colors.getColor(color, R.color.letter_tile_default_color); 169 } 170} 171