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.content.res.Resources.NotFoundException; 21 import android.graphics.Bitmap; 22 import android.graphics.BitmapFactory; 23 import android.graphics.drawable.BitmapDrawable; 24 import android.graphics.drawable.Drawable; 25 import android.graphics.drawable.TransitionDrawable; 26 import android.provider.ContactsContract.DisplayNameSources; 27 import android.text.TextUtils; 28 import android.util.Log; 29 import android.widget.ImageView; 30 31 import com.android.contacts.common.ContactPhotoManager; 32 import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest; 33 import com.android.contacts.common.lettertiles.LetterTileDrawable; 34 import com.android.contacts.common.model.Contact; 35 36 import java.util.Arrays; 37 38 /** 39 * Initialized with a target ImageView. When provided with a compressed image 40 * (i.e. a byte[]), it appropriately updates the ImageView's Drawable. 41 */ 42 public class ImageViewDrawableSetter { 43 private ImageView mTarget; 44 private byte[] mCompressed; 45 private Drawable mPreviousDrawable; 46 private int mDurationInMillis = 0; 47 private Contact mContact; 48 private static final String TAG = "ImageViewDrawableSetter"; 49 50 public ImageViewDrawableSetter() { 51 } 52 53 public ImageViewDrawableSetter(ImageView target) { 54 mTarget = target; 55 } 56 57 public Bitmap setupContactPhoto(Contact contactData, ImageView photoView) { 58 mContact = contactData; 59 setTarget(photoView); 60 return setCompressedImage(contactData.getPhotoBinaryData()); 61 } 62 63 public void setTransitionDuration(int durationInMillis) { 64 mDurationInMillis = durationInMillis; 65 } 66 67 public ImageView getTarget() { 68 return mTarget; 69 } 70 71 /** 72 * Re-initialize to use new target. As a result, the next time a new image 73 * is set, it will immediately be applied to the target (there will be no 74 * fade transition). 75 */ 76 protected void setTarget(ImageView target) { 77 if (mTarget != target) { 78 mTarget = target; 79 mCompressed = null; 80 mPreviousDrawable = null; 81 } 82 } 83 84 protected byte[] getCompressedImage() { 85 return mCompressed; 86 } 87 88 protected Bitmap setCompressedImage(byte[] compressed) { 89 if (mPreviousDrawable == null) { 90 // If we don't already have a drawable, skip the exit-early test 91 // below; otherwise we might not end up setting the default image. 92 } else if (mPreviousDrawable != null && 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 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 */ 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 } else { 154 request = new DefaultImageRequest(mContact.getDisplayName(), mContact.getLookupKey(), 155 contactType); 156 } 157 return ContactPhotoManager.getDefaultAvatarDrawableForContact(resources, true, request); 158 } 159 160 private BitmapDrawable decodedBitmapDrawable(byte[] compressed) { 161 Resources rsrc = mTarget.getResources(); 162 Bitmap bitmap = BitmapFactory.decodeByteArray(compressed, 0, compressed.length); 163 return new BitmapDrawable(rsrc, bitmap); 164 } 165 } 166