Home | History | Annotate | Download | only in photomanager
      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 
     17 package com.android.mail.photomanager;
     18 
     19 import android.content.Context;
     20 import android.content.res.Resources;
     21 import android.content.res.TypedArray;
     22 import android.graphics.Bitmap;
     23 import android.graphics.BitmapFactory;
     24 import android.graphics.Canvas;
     25 import android.graphics.Paint.Align;
     26 import android.graphics.Rect;
     27 import android.graphics.Typeface;
     28 import android.text.TextPaint;
     29 import android.text.TextUtils;
     30 
     31 import com.android.mail.R;
     32 import com.android.mail.photomanager.ContactPhotoManager.ContactIdentifier;
     33 import com.android.mail.photomanager.PhotoManager.DefaultImageProvider;
     34 import com.android.mail.photomanager.PhotoManager.PhotoIdentifier;
     35 import com.android.mail.ui.DividedImageCanvas;
     36 import com.android.mail.ui.ImageCanvas;
     37 import com.android.mail.ui.ImageCanvas.Dimensions;
     38 import com.android.mail.utils.LogTag;
     39 import 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  */
     49 public 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 = (String) contactIdentifier.getKey();
     99 
    100         // don't apply again if existing letter is there (and valid)
    101         if (dividedImageView.hasImageFor(address)) {
    102             return;
    103         }
    104 
    105         dividedImageView.getDesiredDimensions(address, mDims);
    106 
    107         final Bitmap bitmap = getLetterTile(mDims, displayName, address);
    108 
    109         if (bitmap == null) {
    110             return;
    111         }
    112 
    113         dividedImageView.addDivisionImage(bitmap, address);
    114     }
    115 
    116     public Bitmap getLetterTile(final Dimensions dimensions, final String displayName,
    117             final String address) {
    118         final String display = !TextUtils.isEmpty(displayName) ? displayName : address;
    119         final char firstChar = display.charAt(0);
    120 
    121         // get an empty bitmap
    122         final Bitmap bitmap = getBitmap(dimensions, false /* getDefault */);
    123         if (bitmap == null) {
    124             LogUtils.w(TAG, "LetterTileProvider width(%d) or height(%d) is 0 for name %s and "
    125                     + "address %s.", dimensions.width, dimensions.height, displayName, address);
    126             return null;
    127         }
    128 
    129         final Canvas c = mCanvas;
    130         c.setBitmap(bitmap);
    131         c.drawColor(pickColor(address));
    132 
    133         // If its a valid English alphabet letter,
    134         // draw the letter on top of the color
    135         if (isEnglishLetterOrDigit(firstChar)) {
    136             mFirstChar[0] = Character.toUpperCase(firstChar);
    137             mPaint.setTextSize(getFontSize(dimensions.scale));
    138             mPaint.getTextBounds(mFirstChar, 0, 1, mBounds);
    139             c.drawText(mFirstChar, 0, 1, 0 + dimensions.width / 2,
    140                     0 + dimensions.height / 2 + (mBounds.bottom - mBounds.top) / 2, mPaint);
    141         } else { // draw the generic icon on top
    142             c.drawBitmap(getBitmap(dimensions, true /* getDefault */), 0, 0, null);
    143         }
    144 
    145         return bitmap;
    146     }
    147 
    148     private static boolean isEnglishLetterOrDigit(char c) {
    149         return ('A' <= c && c <= 'Z')
    150                 || ('a' <= c && c <= 'z')
    151                 || ('0' <= c && c <= '9');
    152     }
    153 
    154     private Bitmap getBitmap(final Dimensions d, boolean getDefault) {
    155         if (d.width <= 0 || d.height <= 0) {
    156             LogUtils.w(TAG,
    157                     "LetterTileProvider width(%d) or height(%d) is 0.", d.width, d.height);
    158             return null;
    159         }
    160         final int pos;
    161         float scale = d.scale;
    162         if (scale == Dimensions.SCALE_ONE) {
    163             pos = 0;
    164         } else if (scale == Dimensions.SCALE_HALF) {
    165             pos = 1;
    166         } else {
    167             pos = 2;
    168         }
    169 
    170         final Bitmap[] cache = (getDefault) ? mDefaultBitmapCache : mBitmapBackgroundCache;
    171 
    172         Bitmap bitmap = cache[pos];
    173         // ensure bitmap is suitable for the desired w/h
    174         // (two-pane uses two different sets of dimensions depending on pane width)
    175         if (bitmap == null || bitmap.getWidth() != d.width || bitmap.getHeight() != d.height) {
    176             // create and place the bitmap
    177             if (getDefault) {
    178                 bitmap = BitmapUtil.centerCrop(mDefaultBitmap, d.width, d.height);
    179             } else {
    180                 bitmap = Bitmap.createBitmap(d.width, d.height, Bitmap.Config.ARGB_8888);
    181             }
    182             cache[pos] = bitmap;
    183         }
    184         return bitmap;
    185     }
    186 
    187     private int getFontSize(float scale)  {
    188         if (scale == Dimensions.SCALE_ONE) {
    189             return mTileLetterFontSize;
    190         } else {
    191             return mTileLetterFontSizeSmall;
    192         }
    193     }
    194 
    195     private int pickColor(String emailAddress) {
    196         // String.hashCode() implementation is not supposed to change across java versions, so
    197         // this should guarantee the same email address always maps to the same color.
    198         int color = Math.abs(emailAddress.hashCode()) % NUM_OF_TILE_COLORS;
    199         return mColors.getColor(color, mDefaultColor);
    200     }
    201 }
    202