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