Home | History | Annotate | Download | only in bitmap
      1 /*
      2  * Copyright (C) 2014 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 package com.android.mail.bitmap;
     17 
     18 import android.content.res.Resources;
     19 import android.graphics.Bitmap;
     20 import android.graphics.BitmapFactory;
     21 import android.graphics.BitmapShader;
     22 import android.graphics.Canvas;
     23 import android.graphics.Color;
     24 import android.graphics.ColorFilter;
     25 import android.graphics.Matrix;
     26 import android.graphics.Paint;
     27 import android.graphics.Paint.Style;
     28 import android.graphics.Rect;
     29 import android.graphics.Shader.TileMode;
     30 import android.graphics.drawable.Drawable;
     31 
     32 import com.android.bitmap.BitmapCache;
     33 import com.android.bitmap.RequestKey;
     34 import com.android.bitmap.ReusableBitmap;
     35 import com.android.mail.R;
     36 import com.android.mail.bitmap.ContactResolver.ContactDrawableInterface;
     37 
     38 public class AccountAvatarDrawable extends Drawable implements ContactDrawableInterface {
     39 
     40     private final BitmapCache mCache;
     41     private final ContactResolver mContactResolver;
     42 
     43     private ContactRequest mContactRequest;
     44     private ReusableBitmap mBitmap;
     45     private final float mBorderWidth;
     46     private final Paint mBitmapPaint;
     47     private final Paint mBorderPaint;
     48     private final Matrix mMatrix;
     49 
     50     private int mDecodeWidth;
     51     private int mDecodeHeight;
     52 
     53     private static Bitmap DEFAULT_AVATAR = null;
     54 
     55     public AccountAvatarDrawable(final Resources res, final BitmapCache cache,
     56             final ContactResolver contactResolver) {
     57         mCache = cache;
     58         mContactResolver = contactResolver;
     59         mBitmapPaint = new Paint();
     60         mBitmapPaint.setAntiAlias(true);
     61         mBitmapPaint.setFilterBitmap(true);
     62         mBitmapPaint.setDither(true);
     63 
     64         mBorderWidth = res.getDimensionPixelSize(R.dimen.avatar_border_width);
     65 
     66         mBorderPaint = new Paint();
     67         mBorderPaint.setColor(Color.TRANSPARENT);
     68         mBorderPaint.setStyle(Style.STROKE);
     69         mBorderPaint.setStrokeWidth(mBorderWidth);
     70         mBorderPaint.setAntiAlias(true);
     71 
     72         mMatrix = new Matrix();
     73 
     74         if (DEFAULT_AVATAR == null) {
     75             DEFAULT_AVATAR = BitmapFactory.decodeResource(res, R.drawable.avatar_placeholder_gray);
     76         }
     77     }
     78 
     79     @Override
     80     public void draw(final Canvas canvas) {
     81         final Rect bounds = getBounds();
     82         if (!isVisible() || bounds.isEmpty()) {
     83             return;
     84         }
     85 
     86         if (mBitmap != null && mBitmap.bmp != null) {
     87             // Draw sender image.
     88             drawBitmap(mBitmap.bmp, mBitmap.getLogicalWidth(), mBitmap.getLogicalHeight(), canvas);
     89         } else {
     90             // Draw the default image
     91             drawBitmap(DEFAULT_AVATAR, DEFAULT_AVATAR.getWidth(), DEFAULT_AVATAR.getHeight(),
     92                     canvas);
     93         }
     94     }
     95 
     96     /**
     97      * Draw the bitmap onto the canvas at the current bounds taking into account the current scale.
     98      */
     99     private void drawBitmap(final Bitmap bitmap, final int width, final int height,
    100             final Canvas canvas) {
    101         final Rect bounds = getBounds();
    102         // Draw bitmap through shader first.
    103         final BitmapShader shader = new BitmapShader(bitmap, TileMode.CLAMP, TileMode.CLAMP);
    104         mMatrix.reset();
    105 
    106         // Fit bitmap to bounds.
    107         final float boundsWidth = (float) bounds.width();
    108         final float boundsHeight = (float) bounds.height();
    109         final float scale = Math.max(boundsWidth / width, boundsHeight / height);
    110         mMatrix.postScale(scale, scale);
    111 
    112         // Translate bitmap to dst bounds.
    113         mMatrix.postTranslate(bounds.left, bounds.top);
    114 
    115         shader.setLocalMatrix(mMatrix);
    116         mBitmapPaint.setShader(shader);
    117         canvas.drawCircle(bounds.centerX(), bounds.centerY(), bounds.width() / 2, mBitmapPaint);
    118 
    119         // Then draw the border.
    120         final float radius = bounds.width() / 2f - mBorderWidth / 2;
    121         canvas.drawCircle(bounds.centerX(), bounds.centerY(), radius, mBorderPaint);
    122     }
    123 
    124     @Override
    125     public void setAlpha(final int alpha) {
    126         mBitmapPaint.setAlpha(alpha);
    127     }
    128 
    129     @Override
    130     public void setColorFilter(final ColorFilter cf) {
    131         mBitmapPaint.setColorFilter(cf);
    132     }
    133 
    134     @Override
    135     public int getOpacity() {
    136         return 0;
    137     }
    138 
    139     public void setDecodeDimensions(final int decodeWidth, final int decodeHeight) {
    140         mDecodeWidth = decodeWidth;
    141         mDecodeHeight = decodeHeight;
    142     }
    143 
    144     public void unbind() {
    145         setImage(null);
    146     }
    147 
    148     public void bind(final String name, final String email) {
    149         setImage(new ContactRequest(name, email));
    150     }
    151 
    152     private void setImage(final ContactRequest contactRequest) {
    153         if (mContactRequest != null && mContactRequest.equals(contactRequest)) {
    154             return;
    155         }
    156 
    157         if (mBitmap != null) {
    158             mBitmap.releaseReference();
    159             mBitmap = null;
    160         }
    161 
    162         mContactResolver.remove(mContactRequest, this);
    163         mContactRequest = contactRequest;
    164 
    165         if (contactRequest == null) {
    166             invalidateSelf();
    167             return;
    168         }
    169 
    170         final ReusableBitmap cached = mCache.get(contactRequest, true /* incrementRefCount */);
    171         if (cached != null) {
    172             setBitmap(cached);
    173         } else {
    174             decode();
    175         }
    176     }
    177 
    178     private void setBitmap(final ReusableBitmap bmp) {
    179         if (mBitmap != null && mBitmap != bmp) {
    180             mBitmap.releaseReference();
    181         }
    182         mBitmap = bmp;
    183         invalidateSelf();
    184     }
    185 
    186     private void decode() {
    187         if (mContactRequest == null) {
    188             return;
    189         }
    190         // Add to batch.
    191         mContactResolver.add(mContactRequest, this);
    192     }
    193 
    194     @Override
    195     public int getDecodeWidth() {
    196         return mDecodeWidth;
    197     }
    198 
    199     @Override
    200     public int getDecodeHeight() {
    201         return mDecodeHeight;
    202     }
    203 
    204     @Override
    205     public void onDecodeComplete(final RequestKey key, final ReusableBitmap result) {
    206         final ContactRequest request = (ContactRequest) key;
    207         // Remove from batch.
    208         mContactResolver.remove(request, this);
    209         if (request.equals(mContactRequest)) {
    210             setBitmap(result);
    211         } else {
    212             // if the requests don't match (i.e. this request is stale), decrement the
    213             // ref count to allow the bitmap to be pooled
    214             if (result != null) {
    215                 result.releaseReference();
    216             }
    217         }
    218     }
    219 }
    220 
    221