blob: 6147c39750c9a936944529ff8f2e8715395467c5 [file] [log] [blame]
Josh Gargus9758a922012-03-08 17:12:42 -08001/*
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
17package com.android.contacts.util;
18
Josh Gargus9758a922012-03-08 17:12:42 -080019import android.content.res.Resources;
Josh Gargus9758a922012-03-08 17:12:42 -080020import android.graphics.Bitmap;
21import android.graphics.BitmapFactory;
Josh Gargus9758a922012-03-08 17:12:42 -080022import android.graphics.drawable.BitmapDrawable;
23import android.graphics.drawable.Drawable;
24import android.graphics.drawable.TransitionDrawable;
Brian Attwell8d4304c2014-08-20 21:23:43 -070025import android.media.ThumbnailUtils;
Yorke Leef6774502014-02-20 10:21:26 -080026import android.text.TextUtils;
Josh Gargus9758a922012-03-08 17:12:42 -080027import android.widget.ImageView;
Josh Gargus9758a922012-03-08 17:12:42 -080028
Chiao Cheng79a6b5d2012-10-16 13:17:28 -070029import com.android.contacts.common.ContactPhotoManager;
Yorke Leef6774502014-02-20 10:21:26 -080030import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
31import com.android.contacts.common.lettertiles.LetterTileDrawable;
Yorke Leecd321f62013-10-28 15:20:15 -070032import com.android.contacts.common.model.Contact;
Chiao Chenge0b2f1e2012-06-12 13:07:56 -070033
Josh Gargus9758a922012-03-08 17:12:42 -080034import 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 */
40public class ImageViewDrawableSetter {
41 private ImageView mTarget;
42 private byte[] mCompressed;
43 private Drawable mPreviousDrawable;
Maurice Chu6e607d52012-05-15 16:57:26 -070044 private int mDurationInMillis = 0;
Yorke Leef6774502014-02-20 10:21:26 -080045 private Contact mContact;
Josh Gargus9758a922012-03-08 17:12:42 -080046 private static final String TAG = "ImageViewDrawableSetter";
47
48 public ImageViewDrawableSetter() {
Josh Gargus9758a922012-03-08 17:12:42 -080049 }
50
51 public ImageViewDrawableSetter(ImageView target) {
52 mTarget = target;
53 }
54
Yorke Leef6774502014-02-20 10:21:26 -080055 public Bitmap setupContactPhoto(Contact contactData, ImageView photoView) {
56 mContact = contactData;
Josh Gargus9758a922012-03-08 17:12:42 -080057 setTarget(photoView);
Yorke Leef6774502014-02-20 10:21:26 -080058 return setCompressedImage(contactData.getPhotoBinaryData());
Josh Gargus9758a922012-03-08 17:12:42 -080059 }
60
Maurice Chu6e607d52012-05-15 16:57:26 -070061 public void setTransitionDuration(int durationInMillis) {
62 mDurationInMillis = durationInMillis;
63 }
64
Josh Gargus187c8162012-03-13 17:06:53 -070065 public ImageView getTarget() {
66 return mTarget;
Josh Gargus9758a922012-03-08 17:12:42 -080067 }
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 Gargus187c8162012-03-13 17:06:53 -070074 protected void setTarget(ImageView target) {
Josh Gargus9758a922012-03-08 17:12:42 -080075 if (mTarget != target) {
76 mTarget = target;
77 mCompressed = null;
78 mPreviousDrawable = null;
79 }
80 }
81
Josh Gargus187c8162012-03-13 17:06:53 -070082 protected byte[] getCompressedImage() {
83 return mCompressed;
84 }
85
86 protected Bitmap setCompressedImage(byte[] compressed) {
Josh Gargus9758a922012-03-08 17:12:42 -080087 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 Lee9cb26932014-03-26 11:03:13 -070090 } else if (mPreviousDrawable != null
91 && mPreviousDrawable instanceof BitmapDrawable
92 && Arrays.equals(mCompressed, compressed)) {
Josh Gargus9758a922012-03-08 17:12:42 -080093 // 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 Shrauner884a4ca2014-11-24 14:44:24 -0800100 Drawable newDrawable = decodedBitmapDrawable(compressed);
101 if (newDrawable == null) {
102 newDrawable = defaultDrawable();
103 }
Josh Gargus9758a922012-03-08 17:12:42 -0800104
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 Chu6e607d52012-05-15 16:57:26 -0700111 if (mPreviousDrawable == null || mDurationInMillis == 0) {
Josh Gargus9758a922012-03-08 17:12:42 -0800112 // 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 Chu6e607d52012-05-15 16:57:26 -0700121 transition.startTransition(mDurationInMillis);
Josh Gargus9758a922012-03-08 17:12:42 -0800122 }
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 Leef6774502014-02-20 10:21:26 -0800132 return (mPreviousDrawable == null) ? null
133 : mPreviousDrawable instanceof LetterTileDrawable ? null
Josh Gargus9758a922012-03-08 17:12:42 -0800134 : ((BitmapDrawable) mPreviousDrawable).getBitmap();
135 }
136
Josh Gargus9758a922012-03-08 17:12:42 -0800137 /**
Yorke Leef6774502014-02-20 10:21:26 -0800138 * 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 Gargus9758a922012-03-08 17:12:42 -0800142 */
143 private Drawable defaultDrawable() {
144 Resources resources = mTarget.getResources();
Yorke Leef6774502014-02-20 10:21:26 -0800145 DefaultImageRequest request;
146 int contactType = ContactPhotoManager.TYPE_DEFAULT;
147
148 if (mContact.isDisplayNameFromOrganization()) {
149 contactType = ContactPhotoManager.TYPE_BUSINESS;
Josh Gargus9758a922012-03-08 17:12:42 -0800150 }
Yorke Leef6774502014-02-20 10:21:26 -0800151
152 if (TextUtils.isEmpty(mContact.getLookupKey())) {
Yorke Lee41369d72014-04-28 18:17:51 -0700153 request = new DefaultImageRequest(null, mContact.getDisplayName(), contactType,
154 false /* isCircular */);
Yorke Leef6774502014-02-20 10:21:26 -0800155 } else {
156 request = new DefaultImageRequest(mContact.getDisplayName(), mContact.getLookupKey(),
Yorke Lee41369d72014-04-28 18:17:51 -0700157 contactType, false /* isCircular */);
Yorke Leef6774502014-02-20 10:21:26 -0800158 }
159 return ContactPhotoManager.getDefaultAvatarDrawableForContact(resources, true, request);
Josh Gargus9758a922012-03-08 17:12:42 -0800160 }
161
162 private BitmapDrawable decodedBitmapDrawable(byte[] compressed) {
Jay Shrauner884a4ca2014-11-24 14:44:24 -0800163 if (compressed == null) {
164 return null;
165 }
Brian Attwell8d4304c2014-08-20 21:23:43 -0700166 final Resources rsrc = mTarget.getResources();
Josh Gargus9758a922012-03-08 17:12:42 -0800167 Bitmap bitmap = BitmapFactory.decodeByteArray(compressed, 0, compressed.length);
Jay Shrauner884a4ca2014-11-24 14:44:24 -0800168 if (bitmap == null) {
169 return null;
170 }
Brian Attwell8d4304c2014-08-20 21:23:43 -0700171 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 Gargus9758a922012-03-08 17:12:42 -0800176 return new BitmapDrawable(rsrc, bitmap);
177 }
Josh Gargus9758a922012-03-08 17:12:42 -0800178}