19758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus/*
29758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus * Copyright (C) 2012 The Android Open Source Project
39758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus *
49758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus * Licensed under the Apache License, Version 2.0 (the "License");
59758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus * you may not use this file except in compliance with the License.
69758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus * You may obtain a copy of the License at
79758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus *
89758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus *      http://www.apache.org/licenses/LICENSE-2.0
99758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus *
109758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus * Unless required by applicable law or agreed to in writing, software
119758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus * distributed under the License is distributed on an "AS IS" BASIS,
129758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus * See the License for the specific language governing permissions and
149758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus * limitations under the License.
159758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus */
169758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus
179758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Garguspackage com.android.contacts.util;
189758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus
199758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargusimport android.content.res.Resources;
209758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargusimport android.content.res.Resources.NotFoundException;
219758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargusimport android.graphics.Bitmap;
229758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargusimport android.graphics.BitmapFactory;
239758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargusimport android.graphics.drawable.BitmapDrawable;
249758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargusimport android.graphics.drawable.Drawable;
259758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargusimport android.graphics.drawable.TransitionDrawable;
26b00ca40641bfa0f8056edae7420b7f4cf713b5a1Yorke Leeimport android.provider.ContactsContract.DisplayNameSources;
27b00ca40641bfa0f8056edae7420b7f4cf713b5a1Yorke Leeimport android.text.TextUtils;
289758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargusimport android.util.Log;
299758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargusimport android.widget.ImageView;
309758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus
3179a6b5d3a885bc508b4b98c92a946fe12643c444Chiao Chengimport com.android.contacts.common.ContactPhotoManager;
32b00ca40641bfa0f8056edae7420b7f4cf713b5a1Yorke Leeimport com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
33b00ca40641bfa0f8056edae7420b7f4cf713b5a1Yorke Leeimport com.android.contacts.common.lettertiles.LetterTileDrawable;
34cd321f65f1e50409812976380ad1f0fdb3fa35cbYorke Leeimport com.android.contacts.common.model.Contact;
35e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Cheng
369758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargusimport java.util.Arrays;
379758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus
389758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus/**
399758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus * Initialized with a target ImageView. When provided with a compressed image
409758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus * (i.e. a byte[]), it appropriately updates the ImageView's Drawable.
419758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus */
429758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Garguspublic class ImageViewDrawableSetter {
439758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus    private ImageView mTarget;
449758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus    private byte[] mCompressed;
459758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus    private Drawable mPreviousDrawable;
466e607d54ce08ebcc78d94963646a42605697f132Maurice Chu    private int mDurationInMillis = 0;
47b00ca40641bfa0f8056edae7420b7f4cf713b5a1Yorke Lee    private Contact mContact;
489758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus    private static final String TAG = "ImageViewDrawableSetter";
499758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus
509758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus    public ImageViewDrawableSetter() {
519758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus    }
529758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus
539758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus    public ImageViewDrawableSetter(ImageView target) {
549758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus        mTarget = target;
559758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus    }
569758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus
57b00ca40641bfa0f8056edae7420b7f4cf713b5a1Yorke Lee    public Bitmap setupContactPhoto(Contact contactData, ImageView photoView) {
58b00ca40641bfa0f8056edae7420b7f4cf713b5a1Yorke Lee        mContact = contactData;
599758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus        setTarget(photoView);
60b00ca40641bfa0f8056edae7420b7f4cf713b5a1Yorke Lee        return setCompressedImage(contactData.getPhotoBinaryData());
619758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus    }
629758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus
636e607d54ce08ebcc78d94963646a42605697f132Maurice Chu    public void setTransitionDuration(int durationInMillis) {
646e607d54ce08ebcc78d94963646a42605697f132Maurice Chu        mDurationInMillis = durationInMillis;
656e607d54ce08ebcc78d94963646a42605697f132Maurice Chu    }
666e607d54ce08ebcc78d94963646a42605697f132Maurice Chu
67187c8167d77687fbc04237c9ac1e87564b2d5f33Josh Gargus    public ImageView getTarget() {
68187c8167d77687fbc04237c9ac1e87564b2d5f33Josh Gargus        return mTarget;
699758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus    }
709758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus
719758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus    /**
729758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus     * Re-initialize to use new target. As a result, the next time a new image
739758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus     * is set, it will immediately be applied to the target (there will be no
749758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus     * fade transition).
759758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus     */
76187c8167d77687fbc04237c9ac1e87564b2d5f33Josh Gargus    protected void setTarget(ImageView target) {
779758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus        if (mTarget != target) {
789758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus            mTarget = target;
799758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus            mCompressed = null;
809758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus            mPreviousDrawable = null;
819758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus        }
829758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus    }
839758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus
84187c8167d77687fbc04237c9ac1e87564b2d5f33Josh Gargus    protected byte[] getCompressedImage() {
85187c8167d77687fbc04237c9ac1e87564b2d5f33Josh Gargus        return mCompressed;
86187c8167d77687fbc04237c9ac1e87564b2d5f33Josh Gargus    }
87187c8167d77687fbc04237c9ac1e87564b2d5f33Josh Gargus
88187c8167d77687fbc04237c9ac1e87564b2d5f33Josh Gargus    protected Bitmap setCompressedImage(byte[] compressed) {
899758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus        if (mPreviousDrawable == null) {
909758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus            // If we don't already have a drawable, skip the exit-early test
919758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus            // below; otherwise we might not end up setting the default image.
929758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus        } else if (mPreviousDrawable != null && Arrays.equals(mCompressed, compressed)) {
939758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus            // TODO: the worst case is when the arrays are equal but not
949758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus            // identical. This takes about 1ms (more with high-res photos). A
959758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus            // possible optimization is to sparsely sample chunks of the arrays
969758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus            // to compare.
979758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus            return previousBitmap();
989758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus        }
999758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus
1009758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus        final Drawable newDrawable = (compressed == null)
1019758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus                ? defaultDrawable()
1029758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus                : decodedBitmapDrawable(compressed);
1039758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus
1049758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus        // Remember this for next time, so that we can check if it changed.
1059758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus        mCompressed = compressed;
1069758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus
1079758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus        // If we don't have a new Drawable, something went wrong... bail out.
1089758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus        if (newDrawable == null) return previousBitmap();
1099758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus
1106e607d54ce08ebcc78d94963646a42605697f132Maurice Chu        if (mPreviousDrawable == null || mDurationInMillis == 0) {
1119758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus            // Set the new one immediately.
1129758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus            mTarget.setImageDrawable(newDrawable);
1139758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus        } else {
1149758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus            // Set up a transition from the previous Drawable to the new one.
1159758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus            final Drawable[] beforeAndAfter = new Drawable[2];
1169758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus            beforeAndAfter[0] = mPreviousDrawable;
1179758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus            beforeAndAfter[1] = newDrawable;
1189758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus            final TransitionDrawable transition = new TransitionDrawable(beforeAndAfter);
1199758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus            mTarget.setImageDrawable(transition);
1206e607d54ce08ebcc78d94963646a42605697f132Maurice Chu            transition.startTransition(mDurationInMillis);
1219758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus        }
1229758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus
1239758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus        // Remember this for next time, so that we can transition from it to the
1249758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus        // new one.
1259758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus        mPreviousDrawable = newDrawable;
1269758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus
1279758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus        return previousBitmap();
1289758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus    }
1299758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus
1309758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus    private Bitmap previousBitmap() {
131b00ca40641bfa0f8056edae7420b7f4cf713b5a1Yorke Lee        return (mPreviousDrawable == null) ? null
132b00ca40641bfa0f8056edae7420b7f4cf713b5a1Yorke Lee                : mPreviousDrawable instanceof LetterTileDrawable ? null
1339758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus                : ((BitmapDrawable) mPreviousDrawable).getBitmap();
1349758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus    }
1359758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus
1369758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus    /**
137b00ca40641bfa0f8056edae7420b7f4cf713b5a1Yorke Lee     * Obtain the default drawable for a contact when no photo is available. If this is a local
138b00ca40641bfa0f8056edae7420b7f4cf713b5a1Yorke Lee     * contact, then use the contact's display name and lookup key (as a unique identifier) to
139b00ca40641bfa0f8056edae7420b7f4cf713b5a1Yorke Lee     * retrieve a default drawable for this contact. If not, then use the name as the contact
140b00ca40641bfa0f8056edae7420b7f4cf713b5a1Yorke Lee     * identifier instead.
1419758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus     */
1429758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus    private Drawable defaultDrawable() {
1439758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus        Resources resources = mTarget.getResources();
144b00ca40641bfa0f8056edae7420b7f4cf713b5a1Yorke Lee        DefaultImageRequest request;
145b00ca40641bfa0f8056edae7420b7f4cf713b5a1Yorke Lee        int contactType = ContactPhotoManager.TYPE_DEFAULT;
146b00ca40641bfa0f8056edae7420b7f4cf713b5a1Yorke Lee
147b00ca40641bfa0f8056edae7420b7f4cf713b5a1Yorke Lee        if (mContact.isDisplayNameFromOrganization()) {
148b00ca40641bfa0f8056edae7420b7f4cf713b5a1Yorke Lee            contactType = ContactPhotoManager.TYPE_BUSINESS;
1499758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus        }
150b00ca40641bfa0f8056edae7420b7f4cf713b5a1Yorke Lee
151b00ca40641bfa0f8056edae7420b7f4cf713b5a1Yorke Lee        if (TextUtils.isEmpty(mContact.getLookupKey())) {
152b00ca40641bfa0f8056edae7420b7f4cf713b5a1Yorke Lee            request = new DefaultImageRequest(null, mContact.getDisplayName(), contactType);
153b00ca40641bfa0f8056edae7420b7f4cf713b5a1Yorke Lee        } else {
154b00ca40641bfa0f8056edae7420b7f4cf713b5a1Yorke Lee            request = new DefaultImageRequest(mContact.getDisplayName(), mContact.getLookupKey(),
155b00ca40641bfa0f8056edae7420b7f4cf713b5a1Yorke Lee                    contactType);
156b00ca40641bfa0f8056edae7420b7f4cf713b5a1Yorke Lee        }
157b00ca40641bfa0f8056edae7420b7f4cf713b5a1Yorke Lee        return ContactPhotoManager.getDefaultAvatarDrawableForContact(resources, true, request);
1589758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus    }
1599758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus
1609758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus    private BitmapDrawable decodedBitmapDrawable(byte[] compressed) {
1619758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus        Resources rsrc = mTarget.getResources();
1629758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus        Bitmap bitmap = BitmapFactory.decodeByteArray(compressed, 0, compressed.length);
1639758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus        return new BitmapDrawable(rsrc, bitmap);
1649758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus    }
1659758a92fac3e9f64892d893c992f6020d7fe3bfdJosh Gargus}
166