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.ContactPhotoManager; 30 import com.android.contacts.ContactPhotoManager.DefaultImageRequest; 31 import com.android.contacts.lettertiles.LetterTileDrawable; 32 import com.android.contacts.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 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 previousBitmap()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 */ defaultDrawable()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 decodedBitmapDrawable(byte[] compressed)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