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.util.Log; 27 import android.widget.ImageView; 28 29 import com.android.contacts.common.ContactPhotoManager; 30 import com.android.contacts.model.Contact; 31 32 import java.util.Arrays; 33 34 /** 35 * Initialized with a target ImageView. When provided with a compressed image 36 * (i.e. a byte[]), it appropriately updates the ImageView's Drawable. 37 */ 38 public class ImageViewDrawableSetter { 39 private ImageView mTarget; 40 private byte[] mCompressed; 41 private Drawable mPreviousDrawable; 42 private int mDurationInMillis = 0; 43 private static final String TAG = "ImageViewDrawableSetter"; 44 45 public ImageViewDrawableSetter() { 46 } 47 48 public ImageViewDrawableSetter(ImageView target) { 49 mTarget = target; 50 } 51 52 public void setupContactPhoto(Contact contactData, ImageView photoView) { 53 setTarget(photoView); 54 setCompressedImage(contactData.getPhotoBinaryData()); 55 } 56 57 public void setTransitionDuration(int durationInMillis) { 58 mDurationInMillis = durationInMillis; 59 } 60 61 public ImageView getTarget() { 62 return mTarget; 63 } 64 65 /** 66 * Re-initialize to use new target. As a result, the next time a new image 67 * is set, it will immediately be applied to the target (there will be no 68 * fade transition). 69 */ 70 protected void setTarget(ImageView target) { 71 if (mTarget != target) { 72 mTarget = target; 73 mCompressed = null; 74 mPreviousDrawable = null; 75 } 76 } 77 78 protected byte[] getCompressedImage() { 79 return mCompressed; 80 } 81 82 protected Bitmap setCompressedImage(byte[] compressed) { 83 if (mPreviousDrawable == null) { 84 // If we don't already have a drawable, skip the exit-early test 85 // below; otherwise we might not end up setting the default image. 86 } else if (mPreviousDrawable != null && Arrays.equals(mCompressed, compressed)) { 87 // TODO: the worst case is when the arrays are equal but not 88 // identical. This takes about 1ms (more with high-res photos). A 89 // possible optimization is to sparsely sample chunks of the arrays 90 // to compare. 91 return previousBitmap(); 92 } 93 94 final Drawable newDrawable = (compressed == null) 95 ? defaultDrawable() 96 : decodedBitmapDrawable(compressed); 97 98 // Remember this for next time, so that we can check if it changed. 99 mCompressed = compressed; 100 101 // If we don't have a new Drawable, something went wrong... bail out. 102 if (newDrawable == null) return previousBitmap(); 103 104 if (mPreviousDrawable == null || mDurationInMillis == 0) { 105 // Set the new one immediately. 106 mTarget.setImageDrawable(newDrawable); 107 } else { 108 // Set up a transition from the previous Drawable to the new one. 109 final Drawable[] beforeAndAfter = new Drawable[2]; 110 beforeAndAfter[0] = mPreviousDrawable; 111 beforeAndAfter[1] = newDrawable; 112 final TransitionDrawable transition = new TransitionDrawable(beforeAndAfter); 113 mTarget.setImageDrawable(transition); 114 transition.startTransition(mDurationInMillis); 115 } 116 117 // Remember this for next time, so that we can transition from it to the 118 // new one. 119 mPreviousDrawable = newDrawable; 120 121 return previousBitmap(); 122 } 123 124 private Bitmap previousBitmap() { 125 return (mPreviousDrawable == null) 126 ? null 127 : ((BitmapDrawable) mPreviousDrawable).getBitmap(); 128 } 129 130 /** 131 * Obtain the default drawable for a contact when no photo is available. 132 */ 133 private Drawable defaultDrawable() { 134 Resources resources = mTarget.getResources(); 135 final int resId = ContactPhotoManager.getDefaultAvatarResId(true, false); 136 try { 137 return resources.getDrawable(resId); 138 } catch (NotFoundException e) { 139 Log.wtf(TAG, "Cannot load default avatar resource."); 140 return null; 141 } 142 } 143 144 private BitmapDrawable decodedBitmapDrawable(byte[] compressed) { 145 Resources rsrc = mTarget.getResources(); 146 Bitmap bitmap = BitmapFactory.decodeByteArray(compressed, 0, compressed.length); 147 return new BitmapDrawable(rsrc, bitmap); 148 } 149 150 } 151