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.res.Resources;
20 import android.graphics.Bitmap;
21 import android.graphics.BitmapFactory;
22 import android.graphics.Canvas;
23 import android.graphics.Paint.Align;
24 import android.graphics.Rect;
25 import android.graphics.Typeface;
26 import android.text.TextPaint;
27 import android.text.TextUtils;
28 
29 import com.android.mail.R;
30 import com.android.mail.bitmap.ColorPicker;
31 import com.android.mail.ui.ImageCanvas.Dimensions;
32 import com.android.mail.utils.BitmapUtil;
33 import com.android.mail.utils.LogTag;
34 import 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  */
44 public 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 
LetterTileProvider(Resources res)61     public LetterTileProvider(Resources res) {
62         this(res, new ColorPicker.PaletteColorPicker(res));
63     }
64 
LetterTileProvider(Resources res, ColorPicker colorPicker)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 
getLetterTile(final Dimensions dimensions, final String displayName, final String address)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 
isEnglishLetterOrDigit(char c)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 
getBitmap(final Dimensions d, boolean getDefault)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 
getFontSize(float scale)155     private int getFontSize(float scale)  {
156         if (scale == Dimensions.SCALE_ONE) {
157             return mTileLetterFontSize;
158         } else {
159             return mTileLetterFontSizeSmall;
160         }
161     }
162 }
163