• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ImageViewDrawableSetter()48     public ImageViewDrawableSetter() {
49     }
50 
ImageViewDrawableSetter(ImageView target)51     public ImageViewDrawableSetter(ImageView target) {
52         mTarget = target;
53     }
54 
setupContactPhoto(Contact contactData, ImageView photoView)55     public Bitmap setupContactPhoto(Contact contactData, ImageView photoView) {
56         mContact = contactData;
57         setTarget(photoView);
58         return setCompressedImage(contactData.getPhotoBinaryData());
59     }
60 
setTransitionDuration(int durationInMillis)61     public void setTransitionDuration(int durationInMillis) {
62         mDurationInMillis = durationInMillis;
63     }
64 
getTarget()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      */
setTarget(ImageView target)74     protected void setTarget(ImageView target) {
75         if (mTarget != target) {
76             mTarget = target;
77             mCompressed = null;
78             mPreviousDrawable = null;
79         }
80     }
81 
getCompressedImage()82     protected byte[] getCompressedImage() {
83         return mCompressed;
84     }
85 
setCompressedImage(byte[] compressed)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         final Drawable newDrawable = (compressed == null)
101                 ? defaultDrawable()
102                 : decodedBitmapDrawable(compressed);
103 
104         // Remember this for next time, so that we can check if it changed.
105         mCompressed = compressed;
106 
107         // If we don't have a new Drawable, something went wrong... bail out.
108         if (newDrawable == null) return previousBitmap();
109 
110         if (mPreviousDrawable == null || mDurationInMillis == 0) {
111             // Set the new one immediately.
112             mTarget.setImageDrawable(newDrawable);
113         } else {
114             // Set up a transition from the previous Drawable to the new one.
115             final Drawable[] beforeAndAfter = new Drawable[2];
116             beforeAndAfter[0] = mPreviousDrawable;
117             beforeAndAfter[1] = newDrawable;
118             final TransitionDrawable transition = new TransitionDrawable(beforeAndAfter);
119             mTarget.setImageDrawable(transition);
120             transition.startTransition(mDurationInMillis);
121         }
122 
123         // Remember this for next time, so that we can transition from it to the
124         // new one.
125         mPreviousDrawable = newDrawable;
126 
127         return previousBitmap();
128     }
129 
previousBitmap()130     private Bitmap previousBitmap() {
131         return (mPreviousDrawable == null) ? null
132                 : mPreviousDrawable instanceof LetterTileDrawable ? null
133                 : ((BitmapDrawable) mPreviousDrawable).getBitmap();
134     }
135 
136     /**
137      * Obtain the default drawable for a contact when no photo is available. If this is a local
138      * contact, then use the contact's display name and lookup key (as a unique identifier) to
139      * retrieve a default drawable for this contact. If not, then use the name as the contact
140      * identifier instead.
141      */
defaultDrawable()142     private Drawable defaultDrawable() {
143         Resources resources = mTarget.getResources();
144         DefaultImageRequest request;
145         int contactType = ContactPhotoManager.TYPE_DEFAULT;
146 
147         if (mContact.isDisplayNameFromOrganization()) {
148             contactType = ContactPhotoManager.TYPE_BUSINESS;
149         }
150 
151         if (TextUtils.isEmpty(mContact.getLookupKey())) {
152             request = new DefaultImageRequest(null, mContact.getDisplayName(), contactType,
153                     false /* isCircular */);
154         } else {
155             request = new DefaultImageRequest(mContact.getDisplayName(), mContact.getLookupKey(),
156                     contactType, false /* isCircular */);
157         }
158         return ContactPhotoManager.getDefaultAvatarDrawableForContact(resources, true, request);
159     }
160 
decodedBitmapDrawable(byte[] compressed)161     private BitmapDrawable decodedBitmapDrawable(byte[] compressed) {
162         final Resources rsrc = mTarget.getResources();
163         Bitmap bitmap = BitmapFactory.decodeByteArray(compressed, 0, compressed.length);
164         if (bitmap.getHeight() != bitmap.getWidth()) {
165             // Crop the bitmap into a square.
166             final int size = Math.min(bitmap.getWidth(), bitmap.getHeight());
167             bitmap = ThumbnailUtils.extractThumbnail(bitmap, size, size);
168         }
169         return new BitmapDrawable(rsrc, bitmap);
170     }
171 }
172