Josh Gargus | 9758a92 | 2012-03-08 17:12:42 -0800 | [diff] [blame] | 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 | |
Josh Gargus | 9758a92 | 2012-03-08 17:12:42 -0800 | [diff] [blame] | 19 | import android.content.res.Resources; |
Josh Gargus | 9758a92 | 2012-03-08 17:12:42 -0800 | [diff] [blame] | 20 | import android.graphics.Bitmap; |
| 21 | import android.graphics.BitmapFactory; |
Josh Gargus | 9758a92 | 2012-03-08 17:12:42 -0800 | [diff] [blame] | 22 | import android.graphics.drawable.BitmapDrawable; |
| 23 | import android.graphics.drawable.Drawable; |
| 24 | import android.graphics.drawable.TransitionDrawable; |
Brian Attwell | 8d4304c | 2014-08-20 21:23:43 -0700 | [diff] [blame] | 25 | import android.media.ThumbnailUtils; |
Yorke Lee | f677450 | 2014-02-20 10:21:26 -0800 | [diff] [blame] | 26 | import android.text.TextUtils; |
Josh Gargus | 9758a92 | 2012-03-08 17:12:42 -0800 | [diff] [blame] | 27 | import android.widget.ImageView; |
Josh Gargus | 9758a92 | 2012-03-08 17:12:42 -0800 | [diff] [blame] | 28 | |
Chiao Cheng | 79a6b5d | 2012-10-16 13:17:28 -0700 | [diff] [blame] | 29 | import com.android.contacts.common.ContactPhotoManager; |
Yorke Lee | f677450 | 2014-02-20 10:21:26 -0800 | [diff] [blame] | 30 | import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest; |
| 31 | import com.android.contacts.common.lettertiles.LetterTileDrawable; |
Yorke Lee | cd321f6 | 2013-10-28 15:20:15 -0700 | [diff] [blame] | 32 | import com.android.contacts.common.model.Contact; |
Chiao Cheng | e0b2f1e | 2012-06-12 13:07:56 -0700 | [diff] [blame] | 33 | |
Josh Gargus | 9758a92 | 2012-03-08 17:12:42 -0800 | [diff] [blame] | 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; |
Maurice Chu | 6e607d5 | 2012-05-15 16:57:26 -0700 | [diff] [blame] | 44 | private int mDurationInMillis = 0; |
Yorke Lee | f677450 | 2014-02-20 10:21:26 -0800 | [diff] [blame] | 45 | private Contact mContact; |
Josh Gargus | 9758a92 | 2012-03-08 17:12:42 -0800 | [diff] [blame] | 46 | private static final String TAG = "ImageViewDrawableSetter"; |
| 47 | |
| 48 | public ImageViewDrawableSetter() { |
Josh Gargus | 9758a92 | 2012-03-08 17:12:42 -0800 | [diff] [blame] | 49 | } |
| 50 | |
| 51 | public ImageViewDrawableSetter(ImageView target) { |
| 52 | mTarget = target; |
| 53 | } |
| 54 | |
Yorke Lee | f677450 | 2014-02-20 10:21:26 -0800 | [diff] [blame] | 55 | public Bitmap setupContactPhoto(Contact contactData, ImageView photoView) { |
| 56 | mContact = contactData; |
Josh Gargus | 9758a92 | 2012-03-08 17:12:42 -0800 | [diff] [blame] | 57 | setTarget(photoView); |
Yorke Lee | f677450 | 2014-02-20 10:21:26 -0800 | [diff] [blame] | 58 | return setCompressedImage(contactData.getPhotoBinaryData()); |
Josh Gargus | 9758a92 | 2012-03-08 17:12:42 -0800 | [diff] [blame] | 59 | } |
| 60 | |
Maurice Chu | 6e607d5 | 2012-05-15 16:57:26 -0700 | [diff] [blame] | 61 | public void setTransitionDuration(int durationInMillis) { |
| 62 | mDurationInMillis = durationInMillis; |
| 63 | } |
| 64 | |
Josh Gargus | 187c816 | 2012-03-13 17:06:53 -0700 | [diff] [blame] | 65 | public ImageView getTarget() { |
| 66 | return mTarget; |
Josh Gargus | 9758a92 | 2012-03-08 17:12:42 -0800 | [diff] [blame] | 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 | */ |
Josh Gargus | 187c816 | 2012-03-13 17:06:53 -0700 | [diff] [blame] | 74 | protected void setTarget(ImageView target) { |
Josh Gargus | 9758a92 | 2012-03-08 17:12:42 -0800 | [diff] [blame] | 75 | if (mTarget != target) { |
| 76 | mTarget = target; |
| 77 | mCompressed = null; |
| 78 | mPreviousDrawable = null; |
| 79 | } |
| 80 | } |
| 81 | |
Josh Gargus | 187c816 | 2012-03-13 17:06:53 -0700 | [diff] [blame] | 82 | protected byte[] getCompressedImage() { |
| 83 | return mCompressed; |
| 84 | } |
| 85 | |
| 86 | protected Bitmap setCompressedImage(byte[] compressed) { |
Josh Gargus | 9758a92 | 2012-03-08 17:12:42 -0800 | [diff] [blame] | 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. |
Yorke Lee | 9cb2693 | 2014-03-26 11:03:13 -0700 | [diff] [blame] | 90 | } else if (mPreviousDrawable != null |
| 91 | && mPreviousDrawable instanceof BitmapDrawable |
| 92 | && Arrays.equals(mCompressed, compressed)) { |
Josh Gargus | 9758a92 | 2012-03-08 17:12:42 -0800 | [diff] [blame] | 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 | |
Jay Shrauner | 884a4ca | 2014-11-24 14:44:24 -0800 | [diff] [blame] | 100 | Drawable newDrawable = decodedBitmapDrawable(compressed); |
| 101 | if (newDrawable == null) { |
| 102 | newDrawable = defaultDrawable(); |
| 103 | } |
Josh Gargus | 9758a92 | 2012-03-08 17:12:42 -0800 | [diff] [blame] | 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 | |
Maurice Chu | 6e607d5 | 2012-05-15 16:57:26 -0700 | [diff] [blame] | 111 | if (mPreviousDrawable == null || mDurationInMillis == 0) { |
Josh Gargus | 9758a92 | 2012-03-08 17:12:42 -0800 | [diff] [blame] | 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); |
Maurice Chu | 6e607d5 | 2012-05-15 16:57:26 -0700 | [diff] [blame] | 121 | transition.startTransition(mDurationInMillis); |
Josh Gargus | 9758a92 | 2012-03-08 17:12:42 -0800 | [diff] [blame] | 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 | |
| 131 | private Bitmap previousBitmap() { |
Yorke Lee | f677450 | 2014-02-20 10:21:26 -0800 | [diff] [blame] | 132 | return (mPreviousDrawable == null) ? null |
| 133 | : mPreviousDrawable instanceof LetterTileDrawable ? null |
Josh Gargus | 9758a92 | 2012-03-08 17:12:42 -0800 | [diff] [blame] | 134 | : ((BitmapDrawable) mPreviousDrawable).getBitmap(); |
| 135 | } |
| 136 | |
Josh Gargus | 9758a92 | 2012-03-08 17:12:42 -0800 | [diff] [blame] | 137 | /** |
Yorke Lee | f677450 | 2014-02-20 10:21:26 -0800 | [diff] [blame] | 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. |
Josh Gargus | 9758a92 | 2012-03-08 17:12:42 -0800 | [diff] [blame] | 142 | */ |
| 143 | private Drawable defaultDrawable() { |
| 144 | Resources resources = mTarget.getResources(); |
Yorke Lee | f677450 | 2014-02-20 10:21:26 -0800 | [diff] [blame] | 145 | DefaultImageRequest request; |
| 146 | int contactType = ContactPhotoManager.TYPE_DEFAULT; |
| 147 | |
| 148 | if (mContact.isDisplayNameFromOrganization()) { |
| 149 | contactType = ContactPhotoManager.TYPE_BUSINESS; |
Josh Gargus | 9758a92 | 2012-03-08 17:12:42 -0800 | [diff] [blame] | 150 | } |
Yorke Lee | f677450 | 2014-02-20 10:21:26 -0800 | [diff] [blame] | 151 | |
| 152 | if (TextUtils.isEmpty(mContact.getLookupKey())) { |
Yorke Lee | 41369d7 | 2014-04-28 18:17:51 -0700 | [diff] [blame] | 153 | request = new DefaultImageRequest(null, mContact.getDisplayName(), contactType, |
| 154 | false /* isCircular */); |
Yorke Lee | f677450 | 2014-02-20 10:21:26 -0800 | [diff] [blame] | 155 | } else { |
| 156 | request = new DefaultImageRequest(mContact.getDisplayName(), mContact.getLookupKey(), |
Yorke Lee | 41369d7 | 2014-04-28 18:17:51 -0700 | [diff] [blame] | 157 | contactType, false /* isCircular */); |
Yorke Lee | f677450 | 2014-02-20 10:21:26 -0800 | [diff] [blame] | 158 | } |
| 159 | return ContactPhotoManager.getDefaultAvatarDrawableForContact(resources, true, request); |
Josh Gargus | 9758a92 | 2012-03-08 17:12:42 -0800 | [diff] [blame] | 160 | } |
| 161 | |
| 162 | private BitmapDrawable decodedBitmapDrawable(byte[] compressed) { |
Jay Shrauner | 884a4ca | 2014-11-24 14:44:24 -0800 | [diff] [blame] | 163 | if (compressed == null) { |
| 164 | return null; |
| 165 | } |
Brian Attwell | 8d4304c | 2014-08-20 21:23:43 -0700 | [diff] [blame] | 166 | final Resources rsrc = mTarget.getResources(); |
Josh Gargus | 9758a92 | 2012-03-08 17:12:42 -0800 | [diff] [blame] | 167 | Bitmap bitmap = BitmapFactory.decodeByteArray(compressed, 0, compressed.length); |
Jay Shrauner | 884a4ca | 2014-11-24 14:44:24 -0800 | [diff] [blame] | 168 | if (bitmap == null) { |
| 169 | return null; |
| 170 | } |
Brian Attwell | 8d4304c | 2014-08-20 21:23:43 -0700 | [diff] [blame] | 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 | } |
Josh Gargus | 9758a92 | 2012-03-08 17:12:42 -0800 | [diff] [blame] | 176 | return new BitmapDrawable(rsrc, bitmap); |
| 177 | } |
Josh Gargus | 9758a92 | 2012-03-08 17:12:42 -0800 | [diff] [blame] | 178 | } |