Home | History | Annotate | Download | only in util
      1 /*
      2  * Copyright (C) 2012 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.contacts.util;
     18 
     19 import android.content.res.Resources;
     20 import android.graphics.Bitmap;
     21 import android.graphics.BitmapFactory;
     22 import android.graphics.drawable.BitmapDrawable;
     23 import android.graphics.drawable.Drawable;
     24 import android.graphics.drawable.TransitionDrawable;
     25 import android.media.ThumbnailUtils;
     26 import android.text.TextUtils;
     27 import android.widget.ImageView;
     28 
     29 import com.android.contacts.common.ContactPhotoManager;
     30 import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
     31 import com.android.contacts.common.lettertiles.LetterTileDrawable;
     32 import com.android.contacts.common.model.Contact;
     33 
     34 import java.util.Arrays;
     35 
     36 /**
     37  * Initialized with a target ImageView. When provided with a compressed image
     38  * (i.e. a byte[]), it appropriately updates the ImageView's Drawable.
     39  */
     40 public class ImageViewDrawableSetter {
     41     private ImageView mTarget;
     42     private byte[] mCompressed;
     43     private Drawable mPreviousDrawable;
     44     private int mDurationInMillis = 0;
     45     private Contact mContact;
     46     private static final String TAG = "ImageViewDrawableSetter";
     47 
     48     public ImageViewDrawableSetter() {
     49     }
     50 
     51     public ImageViewDrawableSetter(ImageView target) {
     52         mTarget = target;
     53     }
     54 
     55     public Bitmap setupContactPhoto(Contact contactData, ImageView photoView) {
     56         mContact = contactData;
     57         setTarget(photoView);
     58         return setCompressedImage(contactData.getPhotoBinaryData());
     59     }
     60 
     61     public void setTransitionDuration(int durationInMillis) {
     62         mDurationInMillis = durationInMillis;
     63     }
     64 
     65     public ImageView getTarget() {
     66         return mTarget;
     67     }
     68 
     69     /**
     70      * Re-initialize to use new target. As a result, the next time a new image
     71      * is set, it will immediately be applied to the target (there will be no
     72      * fade transition).
     73      */
     74     protected void setTarget(ImageView target) {
     75         if (mTarget != target) {
     76             mTarget = target;
     77             mCompressed = null;
     78             mPreviousDrawable = null;
     79         }
     80     }
     81 
     82     protected byte[] getCompressedImage() {
     83         return mCompressed;
     84     }
     85 
     86     protected Bitmap setCompressedImage(byte[] compressed) {
     87         if (mPreviousDrawable == null) {
     88             // If we don't already have a drawable, skip the exit-early test
     89             // below; otherwise we might not end up setting the default image.
     90         } else if (mPreviousDrawable != null
     91                 && mPreviousDrawable instanceof BitmapDrawable
     92                 && Arrays.equals(mCompressed, compressed)) {
     93             // TODO: the worst case is when the arrays are equal but not
     94             // identical. This takes about 1ms (more with high-res photos). A
     95             // possible optimization is to sparsely sample chunks of the arrays
     96             // to compare.
     97             return previousBitmap();
     98         }
     99 
    100         Drawable newDrawable = decodedBitmapDrawable(compressed);
    101         if (newDrawable == null) {
    102             newDrawable = defaultDrawable();
    103         }
    104 
    105         // Remember this for next time, so that we can check if it changed.
    106         mCompressed = compressed;
    107 
    108         // If we don't have a new Drawable, something went wrong... bail out.
    109         if (newDrawable == null) return previousBitmap();
    110 
    111         if (mPreviousDrawable == null || mDurationInMillis == 0) {
    112             // Set the new one immediately.
    113             mTarget.setImageDrawable(newDrawable);
    114         } else {
    115             // Set up a transition from the previous Drawable to the new one.
    116             final Drawable[] beforeAndAfter = new Drawable[2];
    117             beforeAndAfter[0] = mPreviousDrawable;
    118             beforeAndAfter[1] = newDrawable;
    119             final TransitionDrawable transition = new TransitionDrawable(beforeAndAfter);
    120             mTarget.setImageDrawable(transition);
    121             transition.startTransition(mDurationInMillis);
    122         }
    123 
    124         // Remember this for next time, so that we can transition from it to the
    125         // new one.
    126         mPreviousDrawable = newDrawable;
    127 
    128         return previousBitmap();
    129     }
    130 
    131     private Bitmap previousBitmap() {
    132         return (mPreviousDrawable == null) ? null
    133                 : mPreviousDrawable instanceof LetterTileDrawable ? null
    134                 : ((BitmapDrawable) mPreviousDrawable).getBitmap();
    135     }
    136 
    137     /**
    138      * Obtain the default drawable for a contact when no photo is available. If this is a local
    139      * contact, then use the contact's display name and lookup key (as a unique identifier) to
    140      * retrieve a default drawable for this contact. If not, then use the name as the contact
    141      * identifier instead.
    142      */
    143     private Drawable defaultDrawable() {
    144         Resources resources = mTarget.getResources();
    145         DefaultImageRequest request;
    146         int contactType = ContactPhotoManager.TYPE_DEFAULT;
    147 
    148         if (mContact.isDisplayNameFromOrganization()) {
    149             contactType = ContactPhotoManager.TYPE_BUSINESS;
    150         }
    151 
    152         if (TextUtils.isEmpty(mContact.getLookupKey())) {
    153             request = new DefaultImageRequest(null, mContact.getDisplayName(), contactType,
    154                     false /* isCircular */);
    155         } else {
    156             request = new DefaultImageRequest(mContact.getDisplayName(), mContact.getLookupKey(),
    157                     contactType, false /* isCircular */);
    158         }
    159         return ContactPhotoManager.getDefaultAvatarDrawableForContact(resources, true, request);
    160     }
    161 
    162     private BitmapDrawable decodedBitmapDrawable(byte[] compressed) {
    163         if (compressed == null) {
    164             return null;
    165         }
    166         final Resources rsrc = mTarget.getResources();
    167         Bitmap bitmap = BitmapFactory.decodeByteArray(compressed, 0, compressed.length);
    168         if (bitmap == null) {
    169             return null;
    170         }
    171         if (bitmap.getHeight() != bitmap.getWidth()) {
    172             // Crop the bitmap into a square.
    173             final int size = Math.min(bitmap.getWidth(), bitmap.getHeight());
    174             bitmap = ThumbnailUtils.extractThumbnail(bitmap, size, size);
    175         }
    176         return new BitmapDrawable(rsrc, bitmap);
    177     }
    178 }
    179