| /* |
| * Copyright (C) 2012 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.contacts.util; |
| |
| import android.content.res.Resources; |
| import android.graphics.Bitmap; |
| import android.graphics.BitmapFactory; |
| import android.graphics.drawable.BitmapDrawable; |
| import android.graphics.drawable.Drawable; |
| import android.graphics.drawable.TransitionDrawable; |
| import android.media.ThumbnailUtils; |
| import android.text.TextUtils; |
| import android.widget.ImageView; |
| |
| import com.android.contacts.ContactPhotoManager; |
| import com.android.contacts.ContactPhotoManager.DefaultImageRequest; |
| import com.android.contacts.lettertiles.LetterTileDrawable; |
| import com.android.contacts.model.Contact; |
| |
| import java.util.Arrays; |
| |
| /** |
| * Initialized with a target ImageView. When provided with a compressed image |
| * (i.e. a byte[]), it appropriately updates the ImageView's Drawable. |
| */ |
| public class ImageViewDrawableSetter { |
| private ImageView mTarget; |
| private byte[] mCompressed; |
| private Drawable mPreviousDrawable; |
| private int mDurationInMillis = 0; |
| private Contact mContact; |
| private static final String TAG = "ImageViewDrawableSetter"; |
| |
| public ImageViewDrawableSetter() { |
| } |
| |
| public ImageViewDrawableSetter(ImageView target) { |
| mTarget = target; |
| } |
| |
| public Bitmap setupContactPhoto(Contact contactData, ImageView photoView) { |
| mContact = contactData; |
| setTarget(photoView); |
| return setCompressedImage(contactData.getPhotoBinaryData()); |
| } |
| |
| public void setTransitionDuration(int durationInMillis) { |
| mDurationInMillis = durationInMillis; |
| } |
| |
| public ImageView getTarget() { |
| return mTarget; |
| } |
| |
| /** |
| * Re-initialize to use new target. As a result, the next time a new image |
| * is set, it will immediately be applied to the target (there will be no |
| * fade transition). |
| */ |
| protected void setTarget(ImageView target) { |
| if (mTarget != target) { |
| mTarget = target; |
| mCompressed = null; |
| mPreviousDrawable = null; |
| } |
| } |
| |
| protected byte[] getCompressedImage() { |
| return mCompressed; |
| } |
| |
| protected Bitmap setCompressedImage(byte[] compressed) { |
| if (mPreviousDrawable == null) { |
| // If we don't already have a drawable, skip the exit-early test |
| // below; otherwise we might not end up setting the default image. |
| } else if (mPreviousDrawable != null |
| && mPreviousDrawable instanceof BitmapDrawable |
| && Arrays.equals(mCompressed, compressed)) { |
| // TODO: the worst case is when the arrays are equal but not |
| // identical. This takes about 1ms (more with high-res photos). A |
| // possible optimization is to sparsely sample chunks of the arrays |
| // to compare. |
| return previousBitmap(); |
| } |
| |
| Drawable newDrawable = decodedBitmapDrawable(compressed); |
| if (newDrawable == null) { |
| newDrawable = defaultDrawable(); |
| } |
| |
| // Remember this for next time, so that we can check if it changed. |
| mCompressed = compressed; |
| |
| // If we don't have a new Drawable, something went wrong... bail out. |
| if (newDrawable == null) return previousBitmap(); |
| |
| if (mPreviousDrawable == null || mDurationInMillis == 0) { |
| // Set the new one immediately. |
| mTarget.setImageDrawable(newDrawable); |
| } else { |
| // Set up a transition from the previous Drawable to the new one. |
| final Drawable[] beforeAndAfter = new Drawable[2]; |
| beforeAndAfter[0] = mPreviousDrawable; |
| beforeAndAfter[1] = newDrawable; |
| final TransitionDrawable transition = new TransitionDrawable(beforeAndAfter); |
| mTarget.setImageDrawable(transition); |
| transition.startTransition(mDurationInMillis); |
| } |
| |
| // Remember this for next time, so that we can transition from it to the |
| // new one. |
| mPreviousDrawable = newDrawable; |
| |
| return previousBitmap(); |
| } |
| |
| private Bitmap previousBitmap() { |
| return (mPreviousDrawable == null) ? null |
| : mPreviousDrawable instanceof LetterTileDrawable ? null |
| : ((BitmapDrawable) mPreviousDrawable).getBitmap(); |
| } |
| |
| /** |
| * Obtain the default drawable for a contact when no photo is available. If this is a local |
| * contact, then use the contact's display name and lookup key (as a unique identifier) to |
| * retrieve a default drawable for this contact. If not, then use the name as the contact |
| * identifier instead. |
| */ |
| private Drawable defaultDrawable() { |
| Resources resources = mTarget.getResources(); |
| DefaultImageRequest request; |
| int contactType = ContactPhotoManager.TYPE_DEFAULT; |
| |
| if (mContact.isDisplayNameFromOrganization()) { |
| contactType = ContactPhotoManager.TYPE_BUSINESS; |
| } |
| |
| if (TextUtils.isEmpty(mContact.getLookupKey())) { |
| request = new DefaultImageRequest(null, mContact.getDisplayName(), contactType, |
| false /* isCircular */); |
| } else { |
| request = new DefaultImageRequest(mContact.getDisplayName(), mContact.getLookupKey(), |
| contactType, false /* isCircular */); |
| } |
| return ContactPhotoManager.getDefaultAvatarDrawableForContact(resources, true, request); |
| } |
| |
| private BitmapDrawable decodedBitmapDrawable(byte[] compressed) { |
| if (compressed == null) { |
| return null; |
| } |
| final Resources rsrc = mTarget.getResources(); |
| Bitmap bitmap = BitmapFactory.decodeByteArray(compressed, 0, compressed.length); |
| if (bitmap == null) { |
| return null; |
| } |
| if (bitmap.getHeight() != bitmap.getWidth()) { |
| // Crop the bitmap into a square. |
| final int size = Math.min(bitmap.getWidth(), bitmap.getHeight()); |
| bitmap = ThumbnailUtils.extractThumbnail(bitmap, size, size); |
| } |
| return new BitmapDrawable(rsrc, bitmap); |
| } |
| } |