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.graphics.Bitmap;
21import android.graphics.BitmapFactory;
22import android.graphics.Canvas;
23import android.graphics.Paint.Align;
24import android.graphics.Rect;
25import android.graphics.Typeface;
26import android.text.TextPaint;
27import android.text.TextUtils;
28
29import com.android.mail.R;
30import com.android.mail.bitmap.ColorPicker;
31import com.android.mail.ui.ImageCanvas.Dimensions;
32import com.android.mail.utils.BitmapUtil;
33import com.android.mail.utils.LogTag;
34import com.android.mail.utils.LogUtils;
35
36/**
37 * LetterTileProvider is an implementation of the DefaultImageProvider. When no
38 * matching contact photo is found, and there is a supplied displayName or email
39 * address whose first letter corresponds to an English alphabet letter (or
40 * number), this method creates a bitmap with the letter in the center of a
41 * tile. If there is no English alphabet character (or digit), it creates a
42 * bitmap with the default contact avatar.
43 */
44public class LetterTileProvider {
45    private static final String TAG = LogTag.getLogTag();
46    private final Bitmap mDefaultBitmap;
47    private final Bitmap[] mBitmapBackgroundCache;
48    private final Bitmap[] mDefaultBitmapCache;
49    private final Typeface mSansSerifLight;
50    private final Rect mBounds;
51    private final int mTileLetterFontSize;
52    private final int mTileLetterFontSizeSmall;
53    private final int mTileFontColor;
54    private final TextPaint mPaint = new TextPaint();
55    private final Canvas mCanvas = new Canvas();
56    private final char[] mFirstChar = new char[1];
57
58    private static final int POSSIBLE_BITMAP_SIZES = 3;
59    private final ColorPicker mTileColorPicker;
60
61    public LetterTileProvider(Resources res) {
62        this(res, new ColorPicker.PaletteColorPicker(res));
63    }
64
65    public LetterTileProvider(Resources res, ColorPicker colorPicker) {
66        mTileLetterFontSize = res.getDimensionPixelSize(R.dimen.tile_letter_font_size_small);
67        mTileLetterFontSizeSmall = res.getDimensionPixelSize(R.dimen.tile_letter_font_size_tiny);
68        mTileFontColor = res.getColor(R.color.letter_tile_font_color);
69        mSansSerifLight = Typeface.create("sans-serif-light", Typeface.NORMAL);
70        mBounds = new Rect();
71        mPaint.setTypeface(mSansSerifLight);
72        mPaint.setColor(mTileFontColor);
73        mPaint.setTextAlign(Align.CENTER);
74        mPaint.setAntiAlias(true);
75        mBitmapBackgroundCache = new Bitmap[POSSIBLE_BITMAP_SIZES];
76
77        mDefaultBitmap = BitmapFactory.decodeResource(res, R.drawable.ic_anonymous_avatar_40dp);
78        mDefaultBitmapCache = new Bitmap[POSSIBLE_BITMAP_SIZES];
79
80        mTileColorPicker = colorPicker;
81    }
82
83    public Bitmap getLetterTile(final Dimensions dimensions, final String displayName,
84            final String address) {
85        final String display = !TextUtils.isEmpty(displayName) ? displayName : address;
86        final char firstChar = display.charAt(0);
87
88        // get an empty bitmap
89        final Bitmap bitmap = getBitmap(dimensions, false /* getDefault */);
90        if (bitmap == null) {
91            LogUtils.w(TAG, "LetterTileProvider width(%d) or height(%d) is 0 for name %s and "
92                    + "address %s.", dimensions.width, dimensions.height, displayName, address);
93            return null;
94        }
95
96        final Canvas c = mCanvas;
97        c.setBitmap(bitmap);
98        c.drawColor(mTileColorPicker.pickColor(address));
99
100        // If its a valid English alphabet letter,
101        // draw the letter on top of the color
102        if (isEnglishLetterOrDigit(firstChar)) {
103            mFirstChar[0] = Character.toUpperCase(firstChar);
104            mPaint.setTextSize(
105                    dimensions.fontSize > 0 ? dimensions.fontSize : getFontSize(dimensions.scale));
106            mPaint.getTextBounds(mFirstChar, 0, 1, mBounds);
107            c.drawText(mFirstChar, 0, 1, 0 + dimensions.width / 2,
108                    0 + dimensions.height / 2 + (mBounds.bottom - mBounds.top) / 2, mPaint);
109        } else { // draw the generic icon on top
110            c.drawBitmap(getBitmap(dimensions, true /* getDefault */), 0, 0, null);
111        }
112
113        return bitmap;
114    }
115
116    private static boolean isEnglishLetterOrDigit(char c) {
117        return ('A' <= c && c <= 'Z')
118                || ('a' <= c && c <= 'z')
119                || ('0' <= c && c <= '9');
120    }
121
122    private Bitmap getBitmap(final Dimensions d, boolean getDefault) {
123        if (d.width <= 0 || d.height <= 0) {
124            LogUtils.w(TAG,
125                    "LetterTileProvider width(%d) or height(%d) is 0.", d.width, d.height);
126            return null;
127        }
128        final int pos;
129        float scale = d.scale;
130        if (scale == Dimensions.SCALE_ONE) {
131            pos = 0;
132        } else if (scale == Dimensions.SCALE_HALF) {
133            pos = 1;
134        } else {
135            pos = 2;
136        }
137
138        final Bitmap[] cache = (getDefault) ? mDefaultBitmapCache : mBitmapBackgroundCache;
139
140        Bitmap bitmap = cache[pos];
141        // ensure bitmap is suitable for the desired w/h
142        // (two-pane uses two different sets of dimensions depending on pane width)
143        if (bitmap == null || bitmap.getWidth() != d.width || bitmap.getHeight() != d.height) {
144            // create and place the bitmap
145            if (getDefault) {
146                bitmap = BitmapUtil.centerCrop(mDefaultBitmap, d.width, d.height);
147            } else {
148                bitmap = Bitmap.createBitmap(d.width, d.height, Bitmap.Config.ARGB_8888);
149            }
150            cache[pos] = bitmap;
151        }
152        return bitmap;
153    }
154
155    private int getFontSize(float scale)  {
156        if (scale == Dimensions.SCALE_ONE) {
157            return mTileLetterFontSize;
158        } else {
159            return mTileLetterFontSizeSmall;
160        }
161    }
162}
163