1c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng/*
2c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng * Copyright (C) 2010 The Android Open Source Project
3c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng *
4c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng * Licensed under the Apache License, Version 2.0 (the "License");
5c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng * you may not use this file except in compliance with the License.
6c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng * You may obtain a copy of the License at
7c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng *
8c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng *      http://www.apache.org/licenses/LICENSE-2.0
9c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng *
10c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng * Unless required by applicable law or agreed to in writing, software
11c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng * distributed under the License is distributed on an "AS IS" BASIS,
12c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng * See the License for the specific language governing permissions and
14c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng * limitations under the License.
15c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng */
16c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
17c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengpackage com.android.contacts.common;
18c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
199a54849e59d9b95bd99112b1099431d3a1a9302eYorke Leeimport android.app.ActivityManager;
20c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport android.content.ComponentCallbacks2;
21c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport android.content.ContentResolver;
22c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport android.content.ContentUris;
23c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport android.content.Context;
24c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport android.content.res.Configuration;
25c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport android.content.res.Resources;
26c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport android.database.Cursor;
27c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport android.graphics.Bitmap;
28c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport android.graphics.Canvas;
29c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport android.graphics.Color;
30c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport android.graphics.Paint;
31c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport android.graphics.Paint.Style;
32c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport android.graphics.drawable.BitmapDrawable;
33c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport android.graphics.drawable.ColorDrawable;
34c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport android.graphics.drawable.Drawable;
35c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport android.graphics.drawable.TransitionDrawable;
36c3de481fe384b15d1186b1e468f49bed73657d58Yorke Leeimport android.media.ThumbnailUtils;
3748a393e750f69a7cde240e8596d645b5898ccc0aYorke Leeimport android.net.TrafficStats;
38c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport android.net.Uri;
396084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Leeimport android.net.Uri.Builder;
40c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport android.os.Handler;
41c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport android.os.Handler.Callback;
42c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport android.os.HandlerThread;
43c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport android.os.Message;
44c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport android.provider.ContactsContract;
45c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport android.provider.ContactsContract.Contacts;
46c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport android.provider.ContactsContract.Contacts.Photo;
47c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport android.provider.ContactsContract.Data;
48c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport android.provider.ContactsContract.Directory;
493f9c2f426058413055fa54c08c69ad9461717658Yorke Leeimport android.support.v4.graphics.drawable.RoundedBitmapDrawable;
509d4d8321aafae09125b8f62bce4c299191db0252Chris Craikimport android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
51c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport android.text.TextUtils;
52c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport android.util.Log;
53c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport android.util.LruCache;
54c3f21a33fa554f683ddbe6d9553d1b3bd9a4fd29Brian Attwellimport android.view.View;
55c3f21a33fa554f683ddbe6d9553d1b3bd9a4fd29Brian Attwellimport android.view.ViewGroup;
56c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport android.widget.ImageView;
57c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
586084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Leeimport com.android.contacts.common.lettertiles.LetterTileDrawable;
59c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport com.android.contacts.common.util.BitmapUtil;
604f76e61f8e0a0cf48a0f80fa9ec0ea60adbf536fYorke Leeimport com.android.contacts.common.util.PermissionsUtil;
6148a393e750f69a7cde240e8596d645b5898ccc0aYorke Leeimport com.android.contacts.common.util.TrafficStatsTags;
62c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport com.android.contacts.common.util.UriUtils;
6377cb7da3bcdc92d7ea8369c717b05f72ab6b33d5Tyler Gunnimport com.android.contacts.commonbind.util.UserAgentGenerator;
646084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee
65e5c7689df6465d7952695764476b5304801f6f48Andrew Leeimport com.google.common.annotations.VisibleForTesting;
66c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport com.google.common.collect.Lists;
67c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport com.google.common.collect.Sets;
68c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
69c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport java.io.ByteArrayOutputStream;
7077cb7da3bcdc92d7ea8369c717b05f72ab6b33d5Tyler Gunnimport java.io.IOException;
71c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport java.io.InputStream;
72c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport java.lang.ref.Reference;
73c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport java.lang.ref.SoftReference;
7477cb7da3bcdc92d7ea8369c717b05f72ab6b33d5Tyler Gunnimport java.net.HttpURLConnection;
759a0f2498251247781c7ff0f20dc09c509eccecc9Jay Shraunerimport java.net.URL;
76c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport java.util.Iterator;
77c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport java.util.List;
78c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport java.util.Set;
79c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport java.util.concurrent.ConcurrentHashMap;
80c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengimport java.util.concurrent.atomic.AtomicInteger;
81c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
82c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng/**
83c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng * Asynchronously loads contact photos and maintains a cache of photos.
84c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng */
85c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengpublic abstract class ContactPhotoManager implements ComponentCallbacks2 {
86c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    static final String TAG = "ContactPhotoManager";
87c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    static final boolean DEBUG = false; // Don't submit with true
88c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    static final boolean DEBUG_SIZES = false; // Don't submit with true
89c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
906084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee    /** Contact type constants used for default letter images */
916084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee    public static final int TYPE_PERSON = LetterTileDrawable.TYPE_PERSON;
926084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee    public static final int TYPE_BUSINESS = LetterTileDrawable.TYPE_BUSINESS;
936084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee    public static final int TYPE_VOICEMAIL = LetterTileDrawable.TYPE_VOICEMAIL;
946084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee    public static final int TYPE_DEFAULT = LetterTileDrawable.TYPE_DEFAULT;
956084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee
966084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee    /** Scale and offset default constants used for default letter images */
976084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee    public static final float SCALE_DEFAULT = 1.0f;
986084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee    public static final float OFFSET_DEFAULT = 0.0f;
996084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee
1003f9c2f426058413055fa54c08c69ad9461717658Yorke Lee    public static final boolean IS_CIRCULAR_DEFAULT = false;
1013f9c2f426058413055fa54c08c69ad9461717658Yorke Lee
1026084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee    /** Uri-related constants used for default letter images */
1036084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee    private static final String DISPLAY_NAME_PARAM_KEY = "display_name";
1046084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee    private static final String IDENTIFIER_PARAM_KEY = "identifier";
1056084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee    private static final String CONTACT_TYPE_PARAM_KEY = "contact_type";
1066084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee    private static final String SCALE_PARAM_KEY = "scale";
1076084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee    private static final String OFFSET_PARAM_KEY = "offset";
1083f9c2f426058413055fa54c08c69ad9461717658Yorke Lee    private static final String IS_CIRCULAR_PARAM_KEY = "is_circular";
1096084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee    private static final String DEFAULT_IMAGE_URI_SCHEME = "defaultimage";
1106084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee    private static final Uri DEFAULT_IMAGE_URI = Uri.parse(DEFAULT_IMAGE_URI_SCHEME + "://");
111c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1126084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee    // Static field used to cache the default letter avatar drawable that is created
1136084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee    // using a null {@link DefaultImageRequest}
1146084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee    private static Drawable sDefaultLetterAvatar = null;
1156084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee
116e5c7689df6465d7952695764476b5304801f6f48Andrew Lee    private static ContactPhotoManager sInstance;
117e5c7689df6465d7952695764476b5304801f6f48Andrew Lee
118c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    /**
1196084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee     * Given a {@link DefaultImageRequest}, returns a {@link Drawable}, that when drawn, will
1206084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee     * draw a letter tile avatar based on the request parameters defined in the
1216084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee     * {@link DefaultImageRequest}.
1226084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee     */
1236084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee    public static Drawable getDefaultAvatarDrawableForContact(Resources resources, boolean hires,
1246084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee            DefaultImageRequest defaultImageRequest) {
1256084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee        if (defaultImageRequest == null) {
1266084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee            if (sDefaultLetterAvatar == null) {
1276084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee                // Cache and return the letter tile drawable that is created by a null request,
1286084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee                // so that it doesn't have to be recreated every time it is requested again.
1296084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee                sDefaultLetterAvatar = LetterTileDefaultImageProvider.getDefaultImageForContact(
1306084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee                        resources, null);
1316084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee            }
1326084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee            return sDefaultLetterAvatar;
1336084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee        }
1346084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee        return LetterTileDefaultImageProvider.getDefaultImageForContact(resources,
1356084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee                defaultImageRequest);
1366084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee    }
1376084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee
1386084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee    /**
1396084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee     * Given a {@link DefaultImageRequest}, returns an Uri that can be used to request a
1406084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee     * letter tile avatar when passed to the {@link ContactPhotoManager}. The internal
1416084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee     * implementation of this uri is not guaranteed to remain the same across application
1426084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee     * versions, so the actual uri should never be persisted in long-term storage and reused.
1436084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee     *
1446084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee     * @param request A {@link DefaultImageRequest} object with the fields configured
1456084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee     * to return a
1466084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee     * @return A Uri that when later passed to the {@link ContactPhotoManager} via
1476084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee     * {@link #loadPhoto(ImageView, Uri, int, boolean, DefaultImageRequest)}, can be
1486084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee     * used to request a default contact image, drawn as a letter tile using the
1496084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee     * parameters as configured in the provided {@link DefaultImageRequest}
150c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     */
1516084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee    public static Uri getDefaultAvatarUriForContact(DefaultImageRequest request) {
1526084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee        final Builder builder = DEFAULT_IMAGE_URI.buildUpon();
1536084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee        if (request != null) {
1546084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee            if (!TextUtils.isEmpty(request.displayName)) {
1556084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee                builder.appendQueryParameter(DISPLAY_NAME_PARAM_KEY, request.displayName);
1566084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee            }
1576084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee            if (!TextUtils.isEmpty(request.identifier)) {
1586084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee                builder.appendQueryParameter(IDENTIFIER_PARAM_KEY, request.identifier);
1596084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee            }
1606084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee            if (request.contactType != TYPE_DEFAULT) {
1616084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee                builder.appendQueryParameter(CONTACT_TYPE_PARAM_KEY,
1626084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee                        String.valueOf(request.contactType));
1636084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee            }
1646084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee            if (request.scale != SCALE_DEFAULT) {
1656084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee                builder.appendQueryParameter(SCALE_PARAM_KEY, String.valueOf(request.scale));
1666084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee            }
1676084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee            if (request.offset != OFFSET_DEFAULT) {
1686084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee                builder.appendQueryParameter(OFFSET_PARAM_KEY, String.valueOf(request.offset));
1696084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee            }
1703f9c2f426058413055fa54c08c69ad9461717658Yorke Lee            if (request.isCircular != IS_CIRCULAR_DEFAULT) {
1713f9c2f426058413055fa54c08c69ad9461717658Yorke Lee                builder.appendQueryParameter(IS_CIRCULAR_PARAM_KEY,
1723f9c2f426058413055fa54c08c69ad9461717658Yorke Lee                        String.valueOf(request.isCircular));
1733f9c2f426058413055fa54c08c69ad9461717658Yorke Lee            }
1746084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee
1756084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee        }
1766084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee        return builder.build();
1776084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee    }
1786084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee
17907bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn    /**
18007bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn     * Adds a business contact type encoded fragment to the URL.  Used to ensure photo URLS
18107bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn     * from Nearby Places can be identified as business photo URLs rather than URLs for personal
18207bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn     * contact photos.
18307bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn     *
18407bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn     * @param photoUrl The photo URL to modify.
18507bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn     * @return URL with the contact type parameter added and set to TYPE_BUSINESS.
18607bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn     */
18707bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn    public static String appendBusinessContactType(String photoUrl) {
18807bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn        Uri uri = Uri.parse(photoUrl);
18907bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn        Builder builder = uri.buildUpon();
19007bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn        builder.encodedFragment(String.valueOf(TYPE_BUSINESS));
19107bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn        return builder.build().toString();
19207bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn    }
19307bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn
19407bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn    /**
19507bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn     * Removes the contact type information stored in the photo URI encoded fragment.
19607bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn     *
19707bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn     * @param photoUri The photo URI to remove the contact type from.
19807bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn     * @return The photo URI with contact type removed.
19907bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn     */
20007bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn    public static Uri removeContactType(Uri photoUri) {
20107bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn        String encodedFragment = photoUri.getEncodedFragment();
20207bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn        if (!TextUtils.isEmpty(encodedFragment)) {
20307bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn            Builder builder = photoUri.buildUpon();
20407bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn            builder.encodedFragment(null);
20507bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn            return builder.build();
20607bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn        }
20707bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn        return photoUri;
20807bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn    }
20907bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn
21007bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn    /**
21107bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn     * Inspects a photo URI to determine if the photo URI represents a business.
21207bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn     *
21307bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn     * @param photoUri The URI to inspect.
21407bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn     * @return Whether the URI represents a business photo or not.
21507bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn     */
21607bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn    public static boolean isBusinessContactUri(Uri photoUri) {
21707bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn        if (photoUri == null) {
21807bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn            return false;
21907bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn        }
22007bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn
22107bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn        String encodedFragment = photoUri.getEncodedFragment();
22207bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn        return !TextUtils.isEmpty(encodedFragment)
22307bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn                && encodedFragment.equals(String.valueOf(TYPE_BUSINESS));
22407bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn    }
22507bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn
2266084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee    protected static DefaultImageRequest getDefaultImageRequestFromUri(Uri uri) {
2276084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee        final DefaultImageRequest request = new DefaultImageRequest(
2286084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee                uri.getQueryParameter(DISPLAY_NAME_PARAM_KEY),
2293f9c2f426058413055fa54c08c69ad9461717658Yorke Lee                uri.getQueryParameter(IDENTIFIER_PARAM_KEY), false);
2306084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee        try {
2316084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee            String contactType = uri.getQueryParameter(CONTACT_TYPE_PARAM_KEY);
2326084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee            if (!TextUtils.isEmpty(contactType)) {
2336084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee                request.contactType = Integer.valueOf(contactType);
2346084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee            }
2356084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee
2366084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee            String scale = uri.getQueryParameter(SCALE_PARAM_KEY);
2376084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee            if (!TextUtils.isEmpty(scale)) {
2386084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee                request.scale = Float.valueOf(scale);
2396084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee            }
2406084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee
2416084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee            String offset = uri.getQueryParameter(OFFSET_PARAM_KEY);
2426084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee            if (!TextUtils.isEmpty(offset)) {
2436084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee                request.offset = Float.valueOf(offset);
2446084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee            }
2453f9c2f426058413055fa54c08c69ad9461717658Yorke Lee
2463f9c2f426058413055fa54c08c69ad9461717658Yorke Lee            String isCircular = uri.getQueryParameter(IS_CIRCULAR_PARAM_KEY);
2473f9c2f426058413055fa54c08c69ad9461717658Yorke Lee            if (!TextUtils.isEmpty(isCircular)) {
2483f9c2f426058413055fa54c08c69ad9461717658Yorke Lee                request.isCircular = Boolean.valueOf(isCircular);
2493f9c2f426058413055fa54c08c69ad9461717658Yorke Lee            }
2506084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee        } catch (NumberFormatException e) {
2516084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee            Log.w(TAG, "Invalid DefaultImageRequest image parameters provided, ignoring and using "
2526084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee                    + "defaults.");
253c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
254c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
2556084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee        return request;
256c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    }
257c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
2586084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee    protected boolean isDefaultImageUri(Uri uri) {
2596084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee        return DEFAULT_IMAGE_URI_SCHEME.equals(uri.getScheme());
2606084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee    }
2616084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee
2626084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee    /**
2636084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee     * Contains fields used to contain contact details and other user-defined settings that might
2646084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee     * be used by the ContactPhotoManager to generate a default contact image. This contact image
2656084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee     * takes the form of a letter or bitmap drawn on top of a colored tile.
2666084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee     */
2676084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee    public static class DefaultImageRequest {
2686084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee        /**
2696084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee         * The contact's display name. The display name is used to
2706084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee         */
2716084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee        public String displayName;
2723f9c2f426058413055fa54c08c69ad9461717658Yorke Lee
2736084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee        /**
2746084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee         * A unique and deterministic string that can be used to identify this contact. This is
2756084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee         * usually the contact's lookup key, but other contact details can be used as well,
2766084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee         * especially for non-local or temporary contacts that might not have a lookup key. This
2776084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee         * is used to determine the color of the tile.
2786084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee         */
2796084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee        public String identifier;
2803f9c2f426058413055fa54c08c69ad9461717658Yorke Lee
2816084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee        /**
2826084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee         * The type of this contact. This contact type may be used to decide the kind of
2836084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee         * image to use in the case where a unique letter cannot be generated from the contact's
2846084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee         * display name and identifier. See:
2856084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee         * {@link #TYPE_PERSON}
2866084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee         * {@link #TYPE_BUSINESS}
2876084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee         * {@link #TYPE_PERSON}
2886084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee         * {@link #TYPE_DEFAULT}
2896084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee         */
2906084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee        public int contactType = TYPE_DEFAULT;
2913f9c2f426058413055fa54c08c69ad9461717658Yorke Lee
2926084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee        /**
2936084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee         * The amount to scale the letter or bitmap to, as a ratio of its default size (from a
2946084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee         * range of 0.0f to 2.0f). The default value is 1.0f.
2956084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee         */
2966084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee        public float scale = SCALE_DEFAULT;
2973f9c2f426058413055fa54c08c69ad9461717658Yorke Lee
2986084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee        /**
2996084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee         * The amount to vertically offset the letter or image to within the tile.
3006084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee         * The provided offset must be within the range of -0.5f to 0.5f.
3016084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee         * If set to -0.5f, the letter will be shifted upwards by 0.5 times the height of the canvas
3026084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee         * it is being drawn on, which means it will be drawn with the center of the letter starting
3036084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee         * at the top edge of the canvas.
3046084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee         * If set to 0.5f, the letter will be shifted downwards by 0.5 times the height of the
3056084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee         * canvas it is being drawn on, which means it will be drawn with the center of the letter
3066084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee         * starting at the bottom edge of the canvas.
3076084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee         * The default is 0.0f, which means the letter is drawn in the exact vertical center of
3086084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee         * the tile.
3096084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee         */
3106084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee        public float offset = OFFSET_DEFAULT;
3116084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee
3123f9c2f426058413055fa54c08c69ad9461717658Yorke Lee        /**
3133f9c2f426058413055fa54c08c69ad9461717658Yorke Lee         * Whether or not to draw the default image as a circle, instead of as a square/rectangle.
3143f9c2f426058413055fa54c08c69ad9461717658Yorke Lee         */
3153f9c2f426058413055fa54c08c69ad9461717658Yorke Lee        public boolean isCircular = false;
3163f9c2f426058413055fa54c08c69ad9461717658Yorke Lee
3173f9c2f426058413055fa54c08c69ad9461717658Yorke Lee        /**
3183f9c2f426058413055fa54c08c69ad9461717658Yorke Lee         * Used to indicate that a drawable that represents a contact without any contact details
3193f9c2f426058413055fa54c08c69ad9461717658Yorke Lee         * should be returned.
3203f9c2f426058413055fa54c08c69ad9461717658Yorke Lee         */
3213f9c2f426058413055fa54c08c69ad9461717658Yorke Lee        public static DefaultImageRequest EMPTY_DEFAULT_IMAGE_REQUEST = new DefaultImageRequest();
3223f9c2f426058413055fa54c08c69ad9461717658Yorke Lee
3233f9c2f426058413055fa54c08c69ad9461717658Yorke Lee        /**
32407bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn         * Used to indicate that a drawable that represents a business without a business photo
32507bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn         * should be returned.
32607bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn         */
32707bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn        public static DefaultImageRequest EMPTY_DEFAULT_BUSINESS_IMAGE_REQUEST =
32807bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn                new DefaultImageRequest(null, null, TYPE_BUSINESS, false);
32907bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn
33007bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn        /**
3313f9c2f426058413055fa54c08c69ad9461717658Yorke Lee         * Used to indicate that a circular drawable that represents a contact without any contact
3323f9c2f426058413055fa54c08c69ad9461717658Yorke Lee         * details should be returned.
3333f9c2f426058413055fa54c08c69ad9461717658Yorke Lee         */
3343f9c2f426058413055fa54c08c69ad9461717658Yorke Lee        public static DefaultImageRequest EMPTY_CIRCULAR_DEFAULT_IMAGE_REQUEST =
3353f9c2f426058413055fa54c08c69ad9461717658Yorke Lee                new DefaultImageRequest(null, null, true);
3363f9c2f426058413055fa54c08c69ad9461717658Yorke Lee
33707bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn        /**
33807bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn         * Used to indicate that a circular drawable that represents a business without a business
33907bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn         * photo should be returned.
34007bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn         */
34107bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn        public static DefaultImageRequest EMPTY_CIRCULAR_BUSINESS_IMAGE_REQUEST =
34207bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn                new DefaultImageRequest(null, null, TYPE_BUSINESS, true);
34307bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn
3446084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee        public DefaultImageRequest() {
3456084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee        }
3466084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee
3473f9c2f426058413055fa54c08c69ad9461717658Yorke Lee        public DefaultImageRequest(String displayName, String identifier, boolean isCircular) {
3483f9c2f426058413055fa54c08c69ad9461717658Yorke Lee            this(displayName, identifier, TYPE_DEFAULT, SCALE_DEFAULT, OFFSET_DEFAULT, isCircular);
3496084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee        }
3506084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee
3513f9c2f426058413055fa54c08c69ad9461717658Yorke Lee        public DefaultImageRequest(String displayName, String identifier, int contactType,
3523f9c2f426058413055fa54c08c69ad9461717658Yorke Lee                boolean isCircular) {
3533f9c2f426058413055fa54c08c69ad9461717658Yorke Lee            this(displayName, identifier, contactType, SCALE_DEFAULT, OFFSET_DEFAULT, isCircular);
3546084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee        }
3556084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee
3566084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee        public DefaultImageRequest(String displayName, String identifier, int contactType,
3573f9c2f426058413055fa54c08c69ad9461717658Yorke Lee                float scale, float offset, boolean isCircular) {
3586084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee            this.displayName = displayName;
3596084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee            this.identifier = identifier;
3606084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee            this.contactType = contactType;
3616084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee            this.scale = scale;
3626084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee            this.offset = offset;
3633f9c2f426058413055fa54c08c69ad9461717658Yorke Lee            this.isCircular = isCircular;
3646084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee        }
365c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    }
366c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
367c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    public static abstract class DefaultImageProvider {
368c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        /**
369c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng         * Applies the default avatar to the ImageView. Extent is an indicator for the size (width
370c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng         * or height). If darkTheme is set, the avatar is one that looks better on dark background
3716084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee         *
3726084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee         * @param defaultImageRequest {@link DefaultImageRequest} object that specifies how a
3736084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee         * default letter tile avatar should be drawn.
374c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng         */
3756084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee        public abstract void applyDefaultImage(ImageView view, int extent, boolean darkTheme,
3766084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee                DefaultImageRequest defaultImageRequest);
377c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    }
378c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
3796084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee    /**
3806084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee     * A default image provider that applies a letter tile consisting of a colored background
3816084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee     * and a letter in the foreground as the default image for a contact. The color of the
3826084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee     * background and the type of letter is decided based on the contact's details.
3836084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee     */
3846084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee    private static class LetterTileDefaultImageProvider extends DefaultImageProvider {
385c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        @Override
3866084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee        public void applyDefaultImage(ImageView view, int extent, boolean darkTheme,
3876084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee                DefaultImageRequest defaultImageRequest) {
3886084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee            final Drawable drawable = getDefaultImageForContact(view.getResources(),
3896084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee                    defaultImageRequest);
3906084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee            view.setImageDrawable(drawable);
3916084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee        }
3926084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee
3936084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee        public static Drawable getDefaultImageForContact(Resources resources,
3946084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee                DefaultImageRequest defaultImageRequest) {
3956084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee            final LetterTileDrawable drawable = new LetterTileDrawable(resources);
3966084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee            if (defaultImageRequest != null) {
3976084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee                // If the contact identifier is null or empty, fallback to the
3986084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee                // displayName. In that case, use {@code null} for the contact's
3996084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee                // display name so that a default bitmap will be used instead of a
4006084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee                // letter
4016084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee                if (TextUtils.isEmpty(defaultImageRequest.identifier)) {
4026084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee                    drawable.setContactDetails(null, defaultImageRequest.displayName);
4036084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee                } else {
4046084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee                    drawable.setContactDetails(defaultImageRequest.displayName,
4056084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee                            defaultImageRequest.identifier);
4066084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee                }
4076084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee                drawable.setContactType(defaultImageRequest.contactType);
4086084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee                drawable.setScale(defaultImageRequest.scale);
4096084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee                drawable.setOffset(defaultImageRequest.offset);
4103f9c2f426058413055fa54c08c69ad9461717658Yorke Lee                drawable.setIsCircular(defaultImageRequest.isCircular);
4116084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee            }
4126084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee            return drawable;
413c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
414c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    }
415c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
416c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    private static class BlankDefaultImageProvider extends DefaultImageProvider {
417c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        private static Drawable sDrawable;
418c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
419c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        @Override
4206084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee        public void applyDefaultImage(ImageView view, int extent, boolean darkTheme,
4216084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee                DefaultImageRequest defaultImageRequest) {
422c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            if (sDrawable == null) {
423c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                Context context = view.getContext();
424c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                sDrawable = new ColorDrawable(context.getResources().getColor(
425c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                        R.color.image_placeholder));
426c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            }
427c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            view.setImageDrawable(sDrawable);
428c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
429c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    }
430c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
4316084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee    public static DefaultImageProvider DEFAULT_AVATAR = new LetterTileDefaultImageProvider();
432c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
433c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    public static final DefaultImageProvider DEFAULT_BLANK = new BlankDefaultImageProvider();
434c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
435c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    public static ContactPhotoManager getInstance(Context context) {
436e5c7689df6465d7952695764476b5304801f6f48Andrew Lee        if (sInstance == null) {
437e5c7689df6465d7952695764476b5304801f6f48Andrew Lee            Context applicationContext = context.getApplicationContext();
438e5c7689df6465d7952695764476b5304801f6f48Andrew Lee            sInstance = createContactPhotoManager(applicationContext);
439e5c7689df6465d7952695764476b5304801f6f48Andrew Lee            applicationContext.registerComponentCallbacks(sInstance);
4404f76e61f8e0a0cf48a0f80fa9ec0ea60adbf536fYorke Lee            if (PermissionsUtil.hasContactsPermissions(context)) {
4414f76e61f8e0a0cf48a0f80fa9ec0ea60adbf536fYorke Lee                sInstance.preloadPhotosInBackground();
4424f76e61f8e0a0cf48a0f80fa9ec0ea60adbf536fYorke Lee            }
443c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
444e5c7689df6465d7952695764476b5304801f6f48Andrew Lee        return sInstance;
445c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    }
446c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
447c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    public static synchronized ContactPhotoManager createContactPhotoManager(Context context) {
448c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        return new ContactPhotoManagerImpl(context);
449c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    }
450c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
451e5c7689df6465d7952695764476b5304801f6f48Andrew Lee    @VisibleForTesting
452e5c7689df6465d7952695764476b5304801f6f48Andrew Lee    public static void injectContactPhotoManagerForTesting(ContactPhotoManager photoManager) {
453e5c7689df6465d7952695764476b5304801f6f48Andrew Lee        sInstance = photoManager;
454e5c7689df6465d7952695764476b5304801f6f48Andrew Lee    }
455e5c7689df6465d7952695764476b5304801f6f48Andrew Lee
456c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    /**
457c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * Load thumbnail image into the supplied image view. If the photo is already cached,
458c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * it is displayed immediately.  Otherwise a request is sent to load the photo
459c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * from the database.
460c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     */
461c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    public abstract void loadThumbnail(ImageView view, long photoId, boolean darkTheme,
4623f9c2f426058413055fa54c08c69ad9461717658Yorke Lee            boolean isCircular, DefaultImageRequest defaultImageRequest,
4633f9c2f426058413055fa54c08c69ad9461717658Yorke Lee            DefaultImageProvider defaultProvider);
464c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
465c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    /**
4666084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee     * Calls {@link #loadThumbnail(ImageView, long, boolean, DefaultImageRequest,
4676084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee     * DefaultImageProvider)} using the {@link DefaultImageProvider} {@link #DEFAULT_AVATAR}.
4686084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee    */
4696084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee    public final void loadThumbnail(ImageView view, long photoId, boolean darkTheme,
4703f9c2f426058413055fa54c08c69ad9461717658Yorke Lee            boolean isCircular, DefaultImageRequest defaultImageRequest) {
4713f9c2f426058413055fa54c08c69ad9461717658Yorke Lee        loadThumbnail(view, photoId, darkTheme, isCircular, defaultImageRequest, DEFAULT_AVATAR);
472c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    }
473c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
4746084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee
475c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    /**
476c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * Load photo into the supplied image view. If the photo is already cached,
477c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * it is displayed immediately. Otherwise a request is sent to load the photo
478c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * from the location specified by the URI.
4796084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee     *
480c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * @param view The target view
481c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * @param photoUri The uri of the photo to load
482c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * @param requestedExtent Specifies an approximate Max(width, height) of the targetView.
483c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * This is useful if the source image can be a lot bigger that the target, so that the decoding
484c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * is done using efficient sampling. If requestedExtent is specified, no sampling of the image
485c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * is performed
486c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * @param darkTheme Whether the background is dark. This is used for default avatars
4876084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee     * @param defaultImageRequest {@link DefaultImageRequest} object that specifies how a default
4886084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee     * letter tile avatar should be drawn.
489c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * @param defaultProvider The provider of default avatars (this is used if photoUri doesn't
490c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * refer to an existing image)
491c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     */
492c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    public abstract void loadPhoto(ImageView view, Uri photoUri, int requestedExtent,
4933f9c2f426058413055fa54c08c69ad9461717658Yorke Lee            boolean darkTheme, boolean isCircular, DefaultImageRequest defaultImageRequest,
4946084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee            DefaultImageProvider defaultProvider);
495c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
496c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    /**
4976084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee     * Calls {@link #loadPhoto(ImageView, Uri, int, boolean, DefaultImageRequest,
4986084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee     * DefaultImageProvider)} with {@link #DEFAULT_AVATAR} and {@code null} display names and
4996084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee     * lookup keys.
5006084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee     *
5016084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee     * @param defaultImageRequest {@link DefaultImageRequest} object that specifies how a default
5026084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee     * letter tile avatar should be drawn.
503c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     */
504c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    public final void loadPhoto(ImageView view, Uri photoUri, int requestedExtent,
5053f9c2f426058413055fa54c08c69ad9461717658Yorke Lee            boolean darkTheme, boolean isCircular, DefaultImageRequest defaultImageRequest) {
5063f9c2f426058413055fa54c08c69ad9461717658Yorke Lee        loadPhoto(view, photoUri, requestedExtent, darkTheme, isCircular,
5073f9c2f426058413055fa54c08c69ad9461717658Yorke Lee                defaultImageRequest, DEFAULT_AVATAR);
508c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    }
509c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
510c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    /**
5116084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee     * Calls {@link #loadPhoto(ImageView, Uri, boolean, boolean, DefaultImageRequest,
5126084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee     * DefaultImageProvider)} with {@link #DEFAULT_AVATAR} and with the assumption, that
5136084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee     * the image is a thumbnail.
5146084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee     *
5156084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee     * @param defaultImageRequest {@link DefaultImageRequest} object that specifies how a default
5166084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee     * letter tile avatar should be drawn.
517c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     */
5186084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee    public final void loadDirectoryPhoto(ImageView view, Uri photoUri, boolean darkTheme,
5193f9c2f426058413055fa54c08c69ad9461717658Yorke Lee            boolean isCircular, DefaultImageRequest defaultImageRequest) {
5203f9c2f426058413055fa54c08c69ad9461717658Yorke Lee        loadPhoto(view, photoUri, -1, darkTheme, isCircular, defaultImageRequest, DEFAULT_AVATAR);
521c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    }
522c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
523c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    /**
524c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * Remove photo from the supplied image view. This also cancels current pending load request
525c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * inside this photo manager.
526c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     */
527c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    public abstract void removePhoto(ImageView view);
528c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
529c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    /**
530353068614111bd79ac92e0ae98af433868ba3fb3Tyler Gunn     * Cancels all pending requests to load photos asynchronously.
531353068614111bd79ac92e0ae98af433868ba3fb3Tyler Gunn     */
532c3f21a33fa554f683ddbe6d9553d1b3bd9a4fd29Brian Attwell    public abstract void cancelPendingRequests(View fragmentRootView);
533353068614111bd79ac92e0ae98af433868ba3fb3Tyler Gunn
534353068614111bd79ac92e0ae98af433868ba3fb3Tyler Gunn    /**
535c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * Temporarily stops loading photos from the database.
536c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     */
537c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    public abstract void pause();
538c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
539c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    /**
540c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * Resumes loading photos from the database.
541c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     */
542c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    public abstract void resume();
543c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
544c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    /**
545c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * Marks all cached photos for reloading.  We can continue using cache but should
546c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * also make sure the photos haven't changed in the background and notify the views
547c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * if so.
548c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     */
549c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    public abstract void refreshCache();
550c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
551c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    /**
552c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * Stores the given bitmap directly in the LRU bitmap cache.
553c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * @param photoUri The URI of the photo (for future requests).
554c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * @param bitmap The bitmap.
555c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * @param photoBytes The bytes that were parsed to create the bitmap.
556c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     */
557c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    public abstract void cacheBitmap(Uri photoUri, Bitmap bitmap, byte[] photoBytes);
558c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
559c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    /**
560c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * Initiates a background process that over time will fill up cache with
561c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * preload photos.
562c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     */
563c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    public abstract void preloadPhotosInBackground();
564c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
565c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    // ComponentCallbacks2
566c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    @Override
567c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    public void onConfigurationChanged(Configuration newConfig) {
568c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    }
569c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
570c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    // ComponentCallbacks2
571c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    @Override
572c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    public void onLowMemory() {
573c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    }
574c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
575c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    // ComponentCallbacks2
576c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    @Override
577c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    public void onTrimMemory(int level) {
578c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    }
579c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng}
580c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
581c36c3196c594b992cec8b5de1a3dba7556648804Chiao Chengclass ContactPhotoManagerImpl extends ContactPhotoManager implements Callback {
582c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    private static final String LOADER_THREAD_NAME = "ContactPhotoLoader";
583c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
584c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    private static final int FADE_TRANSITION_DURATION = 200;
585c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
586c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    /**
587c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * Type of message sent by the UI thread to itself to indicate that some photos
588c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * need to be loaded.
589c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     */
590c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    private static final int MESSAGE_REQUEST_LOADING = 1;
591c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
592c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    /**
593c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * Type of message sent by the loader thread to indicate that some photos have
594c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * been loaded.
595c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     */
596c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    private static final int MESSAGE_PHOTOS_LOADED = 2;
597c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
598c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    private static final String[] EMPTY_STRING_ARRAY = new String[0];
599c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
600c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    private static final String[] COLUMNS = new String[] { Photo._ID, Photo.PHOTO };
601c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
602c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    /**
603078d5a53bc0333b3a6fdfe81023ad15f32bed027Yorke Lee     * Dummy object used to indicate that a bitmap for a given key could not be stored in the
604078d5a53bc0333b3a6fdfe81023ad15f32bed027Yorke Lee     * cache.
605078d5a53bc0333b3a6fdfe81023ad15f32bed027Yorke Lee     */
606078d5a53bc0333b3a6fdfe81023ad15f32bed027Yorke Lee    private static final BitmapHolder BITMAP_UNAVAILABLE;
607078d5a53bc0333b3a6fdfe81023ad15f32bed027Yorke Lee
608078d5a53bc0333b3a6fdfe81023ad15f32bed027Yorke Lee    static {
609078d5a53bc0333b3a6fdfe81023ad15f32bed027Yorke Lee        BITMAP_UNAVAILABLE = new BitmapHolder(new byte[0], 0);
610078d5a53bc0333b3a6fdfe81023ad15f32bed027Yorke Lee        BITMAP_UNAVAILABLE.bitmapRef = new SoftReference<Bitmap>(null);
611078d5a53bc0333b3a6fdfe81023ad15f32bed027Yorke Lee    }
612078d5a53bc0333b3a6fdfe81023ad15f32bed027Yorke Lee
613078d5a53bc0333b3a6fdfe81023ad15f32bed027Yorke Lee    /**
614c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * Maintains the state of a particular photo.
615c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     */
616c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    private static class BitmapHolder {
617c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        final byte[] bytes;
618c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        final int originalSmallerExtent;
619c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
620c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        volatile boolean fresh;
621c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        Bitmap bitmap;
622c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        Reference<Bitmap> bitmapRef;
623c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        int decodedSampleSize;
624c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
625c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        public BitmapHolder(byte[] bytes, int originalSmallerExtent) {
626c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            this.bytes = bytes;
627c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            this.fresh = true;
628c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            this.originalSmallerExtent = originalSmallerExtent;
629c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
630c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    }
631c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
632c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    private final Context mContext;
633c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
634c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    /**
635c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * An LRU cache for bitmap holders. The cache contains bytes for photos just
636c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * as they come from the database. Each holder has a soft reference to the
637c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * actual bitmap.
638c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     */
639c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    private final LruCache<Object, BitmapHolder> mBitmapHolderCache;
640c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
641c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    /**
642c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * {@code true} if ALL entries in {@link #mBitmapHolderCache} are NOT fresh.
643c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     */
644c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    private volatile boolean mBitmapHolderCacheAllUnfresh = true;
645c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
646c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    /**
647c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * Cache size threshold at which bitmaps will not be preloaded.
648c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     */
649c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    private final int mBitmapHolderCacheRedZoneBytes;
650c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
651c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    /**
652c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * Level 2 LRU cache for bitmaps. This is a smaller cache that holds
653c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * the most recently used bitmaps to save time on decoding
654c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * them from bytes (the bytes are stored in {@link #mBitmapHolderCache}.
655c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     */
656c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    private final LruCache<Object, Bitmap> mBitmapCache;
657c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
658c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    /**
659c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * A map from ImageView to the corresponding photo ID or uri, encapsulated in a request.
660c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * The request may swapped out before the photo loading request is started.
661c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     */
662c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    private final ConcurrentHashMap<ImageView, Request> mPendingRequests =
663c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            new ConcurrentHashMap<ImageView, Request>();
664c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
665c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    /**
666c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * Handler for messages sent to the UI thread.
667c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     */
668c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    private final Handler mMainThreadHandler = new Handler(this);
669c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
670c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    /**
671c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * Thread responsible for loading photos from the database. Created upon
672c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * the first request.
673c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     */
674c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    private LoaderThread mLoaderThread;
675c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
676c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    /**
677c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * A gate to make sure we only send one instance of MESSAGE_PHOTOS_NEEDED at a time.
678c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     */
679c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    private boolean mLoadingRequested;
680c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
681c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    /**
682c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * Flag indicating if the image loading is paused.
683c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     */
684c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    private boolean mPaused;
685c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
686c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    /** Cache size for {@link #mBitmapHolderCache} for devices with "large" RAM. */
687c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    private static final int HOLDER_CACHE_SIZE = 2000000;
688c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
689c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    /** Cache size for {@link #mBitmapCache} for devices with "large" RAM. */
690c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    private static final int BITMAP_CACHE_SIZE = 36864 * 48; // 1728K
691c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
692c3de481fe384b15d1186b1e468f49bed73657d58Yorke Lee    /** Height/width of a thumbnail image */
693c3de481fe384b15d1186b1e468f49bed73657d58Yorke Lee    private static int mThumbnailSize;
694c3de481fe384b15d1186b1e468f49bed73657d58Yorke Lee
695c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    /** For debug: How many times we had to reload cached photo for a stale entry */
696c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    private final AtomicInteger mStaleCacheOverwrite = new AtomicInteger();
697c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
698c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    /** For debug: How many times we had to reload cached photo for a fresh entry.  Should be 0. */
699c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    private final AtomicInteger mFreshCacheOverwrite = new AtomicInteger();
700c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
70177cb7da3bcdc92d7ea8369c717b05f72ab6b33d5Tyler Gunn    /**
70277cb7da3bcdc92d7ea8369c717b05f72ab6b33d5Tyler Gunn     * The user agent string to use when loading URI based photos.
70377cb7da3bcdc92d7ea8369c717b05f72ab6b33d5Tyler Gunn     */
70477cb7da3bcdc92d7ea8369c717b05f72ab6b33d5Tyler Gunn    private String mUserAgent;
70577cb7da3bcdc92d7ea8369c717b05f72ab6b33d5Tyler Gunn
706c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    public ContactPhotoManagerImpl(Context context) {
707c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        mContext = context;
708c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
7099a54849e59d9b95bd99112b1099431d3a1a9302eYorke Lee        final ActivityManager am = ((ActivityManager) context.getSystemService(
7109a54849e59d9b95bd99112b1099431d3a1a9302eYorke Lee                Context.ACTIVITY_SERVICE));
7119a54849e59d9b95bd99112b1099431d3a1a9302eYorke Lee
7129a54849e59d9b95bd99112b1099431d3a1a9302eYorke Lee        final float cacheSizeAdjustment = (am.isLowRamDevice()) ? 0.5f : 1.0f;
7139a54849e59d9b95bd99112b1099431d3a1a9302eYorke Lee
714c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        final int bitmapCacheSize = (int) (cacheSizeAdjustment * BITMAP_CACHE_SIZE);
715c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        mBitmapCache = new LruCache<Object, Bitmap>(bitmapCacheSize) {
716c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            @Override protected int sizeOf(Object key, Bitmap value) {
717c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                return value.getByteCount();
718c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            }
719c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
720c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            @Override protected void entryRemoved(
721c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    boolean evicted, Object key, Bitmap oldValue, Bitmap newValue) {
722c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                if (DEBUG) dumpStats();
723c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            }
724c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        };
725c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        final int holderCacheSize = (int) (cacheSizeAdjustment * HOLDER_CACHE_SIZE);
726c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        mBitmapHolderCache = new LruCache<Object, BitmapHolder>(holderCacheSize) {
727c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            @Override protected int sizeOf(Object key, BitmapHolder value) {
728c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                return value.bytes != null ? value.bytes.length : 0;
729c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            }
730c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
731c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            @Override protected void entryRemoved(
732c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    boolean evicted, Object key, BitmapHolder oldValue, BitmapHolder newValue) {
733c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                if (DEBUG) dumpStats();
734c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            }
735c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        };
736c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        mBitmapHolderCacheRedZoneBytes = (int) (holderCacheSize * 0.75);
737c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        Log.i(TAG, "Cache adj: " + cacheSizeAdjustment);
738c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        if (DEBUG) {
739c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            Log.d(TAG, "Cache size: " + btk(mBitmapHolderCache.maxSize())
740c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    + " + " + btk(mBitmapCache.maxSize()));
741c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
742c3de481fe384b15d1186b1e468f49bed73657d58Yorke Lee
743c3de481fe384b15d1186b1e468f49bed73657d58Yorke Lee        mThumbnailSize = context.getResources().getDimensionPixelSize(
744c3de481fe384b15d1186b1e468f49bed73657d58Yorke Lee                R.dimen.contact_browser_list_item_photo_size);
74577cb7da3bcdc92d7ea8369c717b05f72ab6b33d5Tyler Gunn
74677cb7da3bcdc92d7ea8369c717b05f72ab6b33d5Tyler Gunn        // Get a user agent string to use for URI photo requests.
74777cb7da3bcdc92d7ea8369c717b05f72ab6b33d5Tyler Gunn        mUserAgent = UserAgentGenerator.getUserAgent(context);
74877cb7da3bcdc92d7ea8369c717b05f72ab6b33d5Tyler Gunn        if (mUserAgent == null) {
74977cb7da3bcdc92d7ea8369c717b05f72ab6b33d5Tyler Gunn            mUserAgent = "";
75077cb7da3bcdc92d7ea8369c717b05f72ab6b33d5Tyler Gunn        }
751c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    }
752c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
753c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    /** Converts bytes to K bytes, rounding up.  Used only for debug log. */
754c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    private static String btk(int bytes) {
755c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        return ((bytes + 1023) / 1024) + "K";
756c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    }
757c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
758c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    private static final int safeDiv(int dividend, int divisor) {
759c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        return (divisor  == 0) ? 0 : (dividend / divisor);
760c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    }
761c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
762c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    /**
763c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * Dump cache stats on logcat.
764c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     */
765c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    private void dumpStats() {
766c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        if (!DEBUG) return;
767c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        {
768c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            int numHolders = 0;
769c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            int rawBytes = 0;
770c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            int bitmapBytes = 0;
771c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            int numBitmaps = 0;
772c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            for (BitmapHolder h : mBitmapHolderCache.snapshot().values()) {
773c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                numHolders++;
774c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                if (h.bytes != null) {
775c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    rawBytes += h.bytes.length;
776c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                }
777c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                Bitmap b = h.bitmapRef != null ? h.bitmapRef.get() : null;
778c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                if (b != null) {
779c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    numBitmaps++;
780c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    bitmapBytes += b.getByteCount();
781c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                }
782c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            }
783c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            Log.d(TAG, "L1: " + btk(rawBytes) + " + " + btk(bitmapBytes) + " = "
784c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    + btk(rawBytes + bitmapBytes) + ", " + numHolders + " holders, "
785c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    + numBitmaps + " bitmaps, avg: "
786c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    + btk(safeDiv(rawBytes, numHolders))
787c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    + "," + btk(safeDiv(bitmapBytes,numBitmaps)));
788c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            Log.d(TAG, "L1 Stats: " + mBitmapHolderCache.toString()
789c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    + ", overwrite: fresh=" + mFreshCacheOverwrite.get()
790c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    + " stale=" + mStaleCacheOverwrite.get());
791c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
792c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
793c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        {
794c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            int numBitmaps = 0;
795c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            int bitmapBytes = 0;
796c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            for (Bitmap b : mBitmapCache.snapshot().values()) {
797c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                numBitmaps++;
798c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                bitmapBytes += b.getByteCount();
799c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            }
800c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            Log.d(TAG, "L2: " + btk(bitmapBytes) + ", " + numBitmaps + " bitmaps"
801c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    + ", avg: " + btk(safeDiv(bitmapBytes, numBitmaps)));
802c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            // We don't get from L2 cache, so L2 stats is meaningless.
803c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
804c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    }
805c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
806c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    @Override
807c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    public void onTrimMemory(int level) {
808c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        if (DEBUG) Log.d(TAG, "onTrimMemory: " + level);
809c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        if (level >= ComponentCallbacks2.TRIM_MEMORY_MODERATE) {
810c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            // Clear the caches.  Note all pending requests will be removed too.
811c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            clear();
812c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
813c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    }
814c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
815c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    @Override
816c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    public void preloadPhotosInBackground() {
817c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        ensureLoaderThread();
818c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        mLoaderThread.requestPreloading();
819c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    }
820c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
821c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    @Override
8223f9c2f426058413055fa54c08c69ad9461717658Yorke Lee    public void loadThumbnail(ImageView view, long photoId, boolean darkTheme, boolean isCircular,
8236084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee            DefaultImageRequest defaultImageRequest, DefaultImageProvider defaultProvider) {
824c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        if (photoId == 0) {
825c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            // No photo is needed
8266084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee            defaultProvider.applyDefaultImage(view, -1, darkTheme, defaultImageRequest);
827c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            mPendingRequests.remove(view);
828c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        } else {
829c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            if (DEBUG) Log.d(TAG, "loadPhoto request: " + photoId);
8303f9c2f426058413055fa54c08c69ad9461717658Yorke Lee            loadPhotoByIdOrUri(view, Request.createFromThumbnailId(photoId, darkTheme, isCircular,
831c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    defaultProvider));
832c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
833c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    }
834c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
835c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    @Override
836c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    public void loadPhoto(ImageView view, Uri photoUri, int requestedExtent, boolean darkTheme,
8373f9c2f426058413055fa54c08c69ad9461717658Yorke Lee            boolean isCircular, DefaultImageRequest defaultImageRequest,
8383f9c2f426058413055fa54c08c69ad9461717658Yorke Lee            DefaultImageProvider defaultProvider) {
839c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        if (photoUri == null) {
840c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            // No photo is needed
8416084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee            defaultProvider.applyDefaultImage(view, requestedExtent, darkTheme,
8426084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee                    defaultImageRequest);
843c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            mPendingRequests.remove(view);
844c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        } else {
845c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            if (DEBUG) Log.d(TAG, "loadPhoto request: " + photoUri);
8466084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee            if (isDefaultImageUri(photoUri)) {
8476084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee                createAndApplyDefaultImageForUri(view, photoUri, requestedExtent, darkTheme,
848ce692f3f26380496d0ac443cccef770b387de586Yorke Lee                        isCircular, defaultProvider);
8496084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee            } else {
8506084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee                loadPhotoByIdOrUri(view, Request.createFromUri(photoUri, requestedExtent,
8513f9c2f426058413055fa54c08c69ad9461717658Yorke Lee                        darkTheme, isCircular, defaultProvider));
8526084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee            }
853c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
854c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    }
855c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
8566084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee    private void createAndApplyDefaultImageForUri(ImageView view, Uri uri, int requestedExtent,
857ce692f3f26380496d0ac443cccef770b387de586Yorke Lee            boolean darkTheme, boolean isCircular, DefaultImageProvider defaultProvider) {
8586084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee        DefaultImageRequest request = getDefaultImageRequestFromUri(uri);
859ce692f3f26380496d0ac443cccef770b387de586Yorke Lee        request.isCircular = isCircular;
8606084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee        defaultProvider.applyDefaultImage(view, requestedExtent, darkTheme, request);
8616084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee    }
8626084726fbdda78bdb16e2d4cc1c3b81c84fd5da1Yorke Lee
863c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    private void loadPhotoByIdOrUri(ImageView view, Request request) {
864c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        boolean loaded = loadCachedPhoto(view, request, false);
865c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        if (loaded) {
866c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            mPendingRequests.remove(view);
867c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        } else {
868c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            mPendingRequests.put(view, request);
869c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            if (!mPaused) {
870c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                // Send a request to start loading photos
871c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                requestLoading();
872c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            }
873c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
874c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    }
875c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
876c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    @Override
877c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    public void removePhoto(ImageView view) {
878c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        view.setImageDrawable(null);
879c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        mPendingRequests.remove(view);
880c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    }
881c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
882353068614111bd79ac92e0ae98af433868ba3fb3Tyler Gunn
883353068614111bd79ac92e0ae98af433868ba3fb3Tyler Gunn    /**
884c3f21a33fa554f683ddbe6d9553d1b3bd9a4fd29Brian Attwell     * Cancels pending requests to load photos asynchronously for views inside
885c3f21a33fa554f683ddbe6d9553d1b3bd9a4fd29Brian Attwell     * {@param fragmentRootView}. If {@param fragmentRootView} is null, cancels all requests.
886353068614111bd79ac92e0ae98af433868ba3fb3Tyler Gunn     */
887353068614111bd79ac92e0ae98af433868ba3fb3Tyler Gunn    @Override
888c3f21a33fa554f683ddbe6d9553d1b3bd9a4fd29Brian Attwell    public void cancelPendingRequests(View fragmentRootView) {
889c3f21a33fa554f683ddbe6d9553d1b3bd9a4fd29Brian Attwell        if (fragmentRootView == null) {
890c3f21a33fa554f683ddbe6d9553d1b3bd9a4fd29Brian Attwell            mPendingRequests.clear();
891c3f21a33fa554f683ddbe6d9553d1b3bd9a4fd29Brian Attwell            return;
892c3f21a33fa554f683ddbe6d9553d1b3bd9a4fd29Brian Attwell        }
893c3f21a33fa554f683ddbe6d9553d1b3bd9a4fd29Brian Attwell        ImageView[] requestSetCopy = mPendingRequests.keySet().toArray(new ImageView[
894c3f21a33fa554f683ddbe6d9553d1b3bd9a4fd29Brian Attwell                mPendingRequests.size()]);
895c3f21a33fa554f683ddbe6d9553d1b3bd9a4fd29Brian Attwell        for (ImageView imageView : requestSetCopy) {
896c3f21a33fa554f683ddbe6d9553d1b3bd9a4fd29Brian Attwell            // If an ImageView is orphaned (currently scrap) or a child of fragmentRootView, then
897c3f21a33fa554f683ddbe6d9553d1b3bd9a4fd29Brian Attwell            // we can safely remove its request.
898c3f21a33fa554f683ddbe6d9553d1b3bd9a4fd29Brian Attwell            if (imageView.getParent() == null || isChildView(fragmentRootView, imageView)) {
899c3f21a33fa554f683ddbe6d9553d1b3bd9a4fd29Brian Attwell                mPendingRequests.remove(imageView);
900c3f21a33fa554f683ddbe6d9553d1b3bd9a4fd29Brian Attwell            }
901c3f21a33fa554f683ddbe6d9553d1b3bd9a4fd29Brian Attwell        }
902c3f21a33fa554f683ddbe6d9553d1b3bd9a4fd29Brian Attwell    }
903c3f21a33fa554f683ddbe6d9553d1b3bd9a4fd29Brian Attwell
904c3f21a33fa554f683ddbe6d9553d1b3bd9a4fd29Brian Attwell    private static boolean isChildView(View parent, View potentialChild) {
905c3f21a33fa554f683ddbe6d9553d1b3bd9a4fd29Brian Attwell        return potentialChild.getParent() != null && (potentialChild.getParent() == parent || (
906c3f21a33fa554f683ddbe6d9553d1b3bd9a4fd29Brian Attwell                potentialChild.getParent() instanceof ViewGroup && isChildView(parent,
907c3f21a33fa554f683ddbe6d9553d1b3bd9a4fd29Brian Attwell                        (ViewGroup) potentialChild.getParent())));
908353068614111bd79ac92e0ae98af433868ba3fb3Tyler Gunn    }
909353068614111bd79ac92e0ae98af433868ba3fb3Tyler Gunn
910c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    @Override
911c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    public void refreshCache() {
912c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        if (mBitmapHolderCacheAllUnfresh) {
913c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            if (DEBUG) Log.d(TAG, "refreshCache -- no fresh entries.");
914c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            return;
915c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
916c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        if (DEBUG) Log.d(TAG, "refreshCache");
917c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        mBitmapHolderCacheAllUnfresh = true;
918c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        for (BitmapHolder holder : mBitmapHolderCache.snapshot().values()) {
919078d5a53bc0333b3a6fdfe81023ad15f32bed027Yorke Lee            if (holder != BITMAP_UNAVAILABLE) {
920078d5a53bc0333b3a6fdfe81023ad15f32bed027Yorke Lee                holder.fresh = false;
921078d5a53bc0333b3a6fdfe81023ad15f32bed027Yorke Lee            }
922c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
923c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    }
924c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
925c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    /**
926c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * Checks if the photo is present in cache.  If so, sets the photo on the view.
927c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     *
928c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * @return false if the photo needs to be (re)loaded from the provider.
929c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     */
930c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    private boolean loadCachedPhoto(ImageView view, Request request, boolean fadeIn) {
931c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        BitmapHolder holder = mBitmapHolderCache.get(request.getKey());
932c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        if (holder == null) {
933c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            // The bitmap has not been loaded ==> show default avatar
9343f9c2f426058413055fa54c08c69ad9461717658Yorke Lee            request.applyDefaultImage(view, request.mIsCircular);
935c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            return false;
936c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
937c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
938c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        if (holder.bytes == null) {
9393f9c2f426058413055fa54c08c69ad9461717658Yorke Lee            request.applyDefaultImage(view, request.mIsCircular);
940c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            return holder.fresh;
941c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
942c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
943c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        Bitmap cachedBitmap = holder.bitmapRef == null ? null : holder.bitmapRef.get();
944c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        if (cachedBitmap == null) {
945c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            if (holder.bytes.length < 8 * 1024) {
946c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                // Small thumbnails are usually quick to inflate. Let's do that on the UI thread
947c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                inflateBitmap(holder, request.getRequestedExtent());
948c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                cachedBitmap = holder.bitmap;
949c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                if (cachedBitmap == null) return false;
950c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            } else {
951c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                // This is bigger data. Let's send that back to the Loader so that we can
952c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                // inflate this in the background
9533f9c2f426058413055fa54c08c69ad9461717658Yorke Lee                request.applyDefaultImage(view, request.mIsCircular);
954c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                return false;
955c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            }
956c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
957c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
958c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        final Drawable previousDrawable = view.getDrawable();
959c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        if (fadeIn && previousDrawable != null) {
960c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            final Drawable[] layers = new Drawable[2];
961c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            // Prevent cascade of TransitionDrawables.
962c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            if (previousDrawable instanceof TransitionDrawable) {
963c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                final TransitionDrawable previousTransitionDrawable =
964c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                        (TransitionDrawable) previousDrawable;
965c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                layers[0] = previousTransitionDrawable.getDrawable(
966c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                        previousTransitionDrawable.getNumberOfLayers() - 1);
967c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            } else {
968c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                layers[0] = previousDrawable;
969c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            }
9703f9c2f426058413055fa54c08c69ad9461717658Yorke Lee            layers[1] = getDrawableForBitmap(mContext.getResources(), cachedBitmap, request);
971c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            TransitionDrawable drawable = new TransitionDrawable(layers);
972c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            view.setImageDrawable(drawable);
973c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            drawable.startTransition(FADE_TRANSITION_DURATION);
974c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        } else {
9753f9c2f426058413055fa54c08c69ad9461717658Yorke Lee            view.setImageDrawable(
9763f9c2f426058413055fa54c08c69ad9461717658Yorke Lee                    getDrawableForBitmap(mContext.getResources(), cachedBitmap, request));
977c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
978c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
979c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        // Put the bitmap in the LRU cache. But only do this for images that are small enough
980c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        // (we require that at least six of those can be cached at the same time)
981c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        if (cachedBitmap.getByteCount() < mBitmapCache.maxSize() / 6) {
982c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            mBitmapCache.put(request.getKey(), cachedBitmap);
983c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
984c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
985c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        // Soften the reference
986c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        holder.bitmap = null;
987c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
988c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        return holder.fresh;
989c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    }
990c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
991c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    /**
9923f9c2f426058413055fa54c08c69ad9461717658Yorke Lee     * Given a bitmap, returns a drawable that is configured to display the bitmap based on the
9933f9c2f426058413055fa54c08c69ad9461717658Yorke Lee     * specified request.
9943f9c2f426058413055fa54c08c69ad9461717658Yorke Lee     */
9953f9c2f426058413055fa54c08c69ad9461717658Yorke Lee    private Drawable getDrawableForBitmap(Resources resources, Bitmap bitmap, Request request) {
9963f9c2f426058413055fa54c08c69ad9461717658Yorke Lee        if (request.mIsCircular) {
9973f9c2f426058413055fa54c08c69ad9461717658Yorke Lee            final RoundedBitmapDrawable drawable =
9989d4d8321aafae09125b8f62bce4c299191db0252Chris Craik                    RoundedBitmapDrawableFactory.create(resources, bitmap);
9993f9c2f426058413055fa54c08c69ad9461717658Yorke Lee            drawable.setAntiAlias(true);
10003f9c2f426058413055fa54c08c69ad9461717658Yorke Lee            drawable.setCornerRadius(bitmap.getHeight() / 2);
10013f9c2f426058413055fa54c08c69ad9461717658Yorke Lee            return drawable;
10023f9c2f426058413055fa54c08c69ad9461717658Yorke Lee        } else {
10033f9c2f426058413055fa54c08c69ad9461717658Yorke Lee            return new BitmapDrawable(resources, bitmap);
10043f9c2f426058413055fa54c08c69ad9461717658Yorke Lee        }
10053f9c2f426058413055fa54c08c69ad9461717658Yorke Lee    }
10063f9c2f426058413055fa54c08c69ad9461717658Yorke Lee
10073f9c2f426058413055fa54c08c69ad9461717658Yorke Lee    /**
1008c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * If necessary, decodes bytes stored in the holder to Bitmap.  As long as the
1009c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * bitmap is held either by {@link #mBitmapCache} or by a soft reference in
1010c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * the holder, it will not be necessary to decode the bitmap.
1011c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     */
1012c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    private static void inflateBitmap(BitmapHolder holder, int requestedExtent) {
1013c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        final int sampleSize =
1014c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                BitmapUtil.findOptimalSampleSize(holder.originalSmallerExtent, requestedExtent);
1015c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        byte[] bytes = holder.bytes;
1016c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        if (bytes == null || bytes.length == 0) {
1017c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            return;
1018c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
1019c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1020c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        if (sampleSize == holder.decodedSampleSize) {
1021c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            // Check the soft reference.  If will be retained if the bitmap is also
1022c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            // in the LRU cache, so we don't need to check the LRU cache explicitly.
1023c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            if (holder.bitmapRef != null) {
1024c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                holder.bitmap = holder.bitmapRef.get();
1025c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                if (holder.bitmap != null) {
1026c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    return;
1027c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                }
1028c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            }
1029c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
1030c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1031c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        try {
1032c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            Bitmap bitmap = BitmapUtil.decodeBitmapFromBytes(bytes, sampleSize);
1033c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1034c3de481fe384b15d1186b1e468f49bed73657d58Yorke Lee            // TODO: As a temporary workaround while framework support is being added to
1035c3de481fe384b15d1186b1e468f49bed73657d58Yorke Lee            // clip non-square bitmaps into a perfect circle, manually crop the bitmap into
1036c3de481fe384b15d1186b1e468f49bed73657d58Yorke Lee            // into a square if it will be displayed as a thumbnail so that it can be cropped
1037c3de481fe384b15d1186b1e468f49bed73657d58Yorke Lee            // into a circle.
1038c3de481fe384b15d1186b1e468f49bed73657d58Yorke Lee            final int height = bitmap.getHeight();
1039c3de481fe384b15d1186b1e468f49bed73657d58Yorke Lee            final int width = bitmap.getWidth();
1040f31e733ba3fcc54e52ebab8ba36f6f07b2a17182Yorke Lee
1041f31e733ba3fcc54e52ebab8ba36f6f07b2a17182Yorke Lee            // The smaller dimension of a scaled bitmap can range from anywhere from 0 to just
1042f31e733ba3fcc54e52ebab8ba36f6f07b2a17182Yorke Lee            // below twice the length of a thumbnail image due to the way we calculate the optimal
1043f31e733ba3fcc54e52ebab8ba36f6f07b2a17182Yorke Lee            // sample size.
1044f31e733ba3fcc54e52ebab8ba36f6f07b2a17182Yorke Lee            if (height != width && Math.min(height, width) <= mThumbnailSize * 2) {
1045c3de481fe384b15d1186b1e468f49bed73657d58Yorke Lee                final int dimension = Math.min(height, width);
1046c3de481fe384b15d1186b1e468f49bed73657d58Yorke Lee                bitmap = ThumbnailUtils.extractThumbnail(bitmap, dimension, dimension);
1047c3de481fe384b15d1186b1e468f49bed73657d58Yorke Lee            }
1048c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            // make bitmap mutable and draw size onto it
1049c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            if (DEBUG_SIZES) {
1050c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                Bitmap original = bitmap;
1051c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                bitmap = bitmap.copy(bitmap.getConfig(), true);
1052c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                original.recycle();
1053c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                Canvas canvas = new Canvas(bitmap);
1054c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                Paint paint = new Paint();
1055c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                paint.setTextSize(16);
1056c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                paint.setColor(Color.BLUE);
1057c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                paint.setStyle(Style.FILL);
1058c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                canvas.drawRect(0.0f, 0.0f, 50.0f, 20.0f, paint);
1059c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                paint.setColor(Color.WHITE);
1060c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                paint.setAntiAlias(true);
1061c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                canvas.drawText(bitmap.getWidth() + "/" + sampleSize, 0, 15, paint);
1062c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            }
1063c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1064c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            holder.decodedSampleSize = sampleSize;
1065c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            holder.bitmap = bitmap;
1066c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            holder.bitmapRef = new SoftReference<Bitmap>(bitmap);
1067c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            if (DEBUG) {
1068c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                Log.d(TAG, "inflateBitmap " + btk(bytes.length) + " -> "
1069c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                        + bitmap.getWidth() + "x" + bitmap.getHeight()
1070c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                        + ", " + btk(bitmap.getByteCount()));
1071c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            }
1072c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        } catch (OutOfMemoryError e) {
1073c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            // Do nothing - the photo will appear to be missing
1074c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
1075c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    }
1076c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1077c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    public void clear() {
1078c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        if (DEBUG) Log.d(TAG, "clear");
1079c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        mPendingRequests.clear();
1080c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        mBitmapHolderCache.evictAll();
1081c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        mBitmapCache.evictAll();
1082c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    }
1083c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1084c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    @Override
1085c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    public void pause() {
1086c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        mPaused = true;
1087c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    }
1088c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1089c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    @Override
1090c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    public void resume() {
1091c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        mPaused = false;
1092c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        if (DEBUG) dumpStats();
1093c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        if (!mPendingRequests.isEmpty()) {
1094c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            requestLoading();
1095c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
1096c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    }
1097c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1098c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    /**
1099c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * Sends a message to this thread itself to start loading images.  If the current
1100c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * view contains multiple image views, all of those image views will get a chance
1101c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * to request their respective photos before any of those requests are executed.
1102c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * This allows us to load images in bulk.
1103c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     */
1104c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    private void requestLoading() {
1105c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        if (!mLoadingRequested) {
1106c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            mLoadingRequested = true;
1107c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            mMainThreadHandler.sendEmptyMessage(MESSAGE_REQUEST_LOADING);
1108c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
1109c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    }
1110c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1111c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    /**
1112c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * Processes requests on the main thread.
1113c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     */
1114c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    @Override
1115c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    public boolean handleMessage(Message msg) {
1116c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        switch (msg.what) {
1117c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            case MESSAGE_REQUEST_LOADING: {
1118c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                mLoadingRequested = false;
1119c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                if (!mPaused) {
1120c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    ensureLoaderThread();
1121c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    mLoaderThread.requestLoading();
1122c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                }
1123c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                return true;
1124c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            }
1125c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1126c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            case MESSAGE_PHOTOS_LOADED: {
1127c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                if (!mPaused) {
1128c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    processLoadedImages();
1129c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                }
1130c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                if (DEBUG) dumpStats();
1131c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                return true;
1132c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            }
1133c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
1134c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        return false;
1135c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    }
1136c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1137c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    public void ensureLoaderThread() {
1138c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        if (mLoaderThread == null) {
1139c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            mLoaderThread = new LoaderThread(mContext.getContentResolver());
1140c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            mLoaderThread.start();
1141c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
1142c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    }
1143c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1144c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    /**
1145c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * Goes over pending loading requests and displays loaded photos.  If some of the
1146c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * photos still haven't been loaded, sends another request for image loading.
1147c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     */
1148c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    private void processLoadedImages() {
1149c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        Iterator<ImageView> iterator = mPendingRequests.keySet().iterator();
1150c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        while (iterator.hasNext()) {
1151c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            ImageView view = iterator.next();
1152c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            Request key = mPendingRequests.get(view);
11533f9c2f426058413055fa54c08c69ad9461717658Yorke Lee            // TODO: Temporarily disable contact photo fading in, until issues with
11543f9c2f426058413055fa54c08c69ad9461717658Yorke Lee            // RoundedBitmapDrawables overlapping the default image drawables are resolved.
11553f9c2f426058413055fa54c08c69ad9461717658Yorke Lee            boolean loaded = loadCachedPhoto(view, key, false);
1156c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            if (loaded) {
1157c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                iterator.remove();
1158c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            }
1159c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
1160c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1161c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        softenCache();
1162c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1163c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        if (!mPendingRequests.isEmpty()) {
1164c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            requestLoading();
1165c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
1166c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    }
1167c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1168c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    /**
1169c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * Removes strong references to loaded bitmaps to allow them to be garbage collected
1170c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * if needed.  Some of the bitmaps will still be retained by {@link #mBitmapCache}.
1171c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     */
1172c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    private void softenCache() {
1173c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        for (BitmapHolder holder : mBitmapHolderCache.snapshot().values()) {
1174c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            holder.bitmap = null;
1175c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
1176c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    }
1177c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1178c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    /**
1179c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * Stores the supplied bitmap in cache.
1180c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     */
1181c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    private void cacheBitmap(Object key, byte[] bytes, boolean preloading, int requestedExtent) {
1182c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        if (DEBUG) {
1183c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            BitmapHolder prev = mBitmapHolderCache.get(key);
1184c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            if (prev != null && prev.bytes != null) {
1185c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                Log.d(TAG, "Overwriting cache: key=" + key + (prev.fresh ? " FRESH" : " stale"));
1186c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                if (prev.fresh) {
1187c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    mFreshCacheOverwrite.incrementAndGet();
1188c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                } else {
1189c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    mStaleCacheOverwrite.incrementAndGet();
1190c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                }
1191c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            }
1192c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            Log.d(TAG, "Caching data: key=" + key + ", " +
1193c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    (bytes == null ? "<null>" : btk(bytes.length)));
1194c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
1195c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        BitmapHolder holder = new BitmapHolder(bytes,
1196c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                bytes == null ? -1 : BitmapUtil.getSmallerExtentFromBytes(bytes));
1197c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1198c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        // Unless this image is being preloaded, decode it right away while
1199c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        // we are still on the background thread.
1200c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        if (!preloading) {
1201c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            inflateBitmap(holder, requestedExtent);
1202c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
1203c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1204078d5a53bc0333b3a6fdfe81023ad15f32bed027Yorke Lee        if (bytes != null) {
1205078d5a53bc0333b3a6fdfe81023ad15f32bed027Yorke Lee            mBitmapHolderCache.put(key, holder);
1206078d5a53bc0333b3a6fdfe81023ad15f32bed027Yorke Lee            if (mBitmapHolderCache.get(key) != holder) {
1207078d5a53bc0333b3a6fdfe81023ad15f32bed027Yorke Lee                Log.w(TAG, "Bitmap too big to fit in cache.");
1208078d5a53bc0333b3a6fdfe81023ad15f32bed027Yorke Lee                mBitmapHolderCache.put(key, BITMAP_UNAVAILABLE);
1209078d5a53bc0333b3a6fdfe81023ad15f32bed027Yorke Lee            }
1210078d5a53bc0333b3a6fdfe81023ad15f32bed027Yorke Lee        } else {
1211078d5a53bc0333b3a6fdfe81023ad15f32bed027Yorke Lee            mBitmapHolderCache.put(key, BITMAP_UNAVAILABLE);
1212078d5a53bc0333b3a6fdfe81023ad15f32bed027Yorke Lee        }
1213078d5a53bc0333b3a6fdfe81023ad15f32bed027Yorke Lee
1214c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        mBitmapHolderCacheAllUnfresh = false;
1215c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    }
1216c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1217c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    @Override
1218c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    public void cacheBitmap(Uri photoUri, Bitmap bitmap, byte[] photoBytes) {
1219c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        final int smallerExtent = Math.min(bitmap.getWidth(), bitmap.getHeight());
1220c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        // We can pretend here that the extent of the photo was the size that we originally
1221c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        // requested
12223f9c2f426058413055fa54c08c69ad9461717658Yorke Lee        Request request = Request.createFromUri(photoUri, smallerExtent, false /* darkTheme */,
12233f9c2f426058413055fa54c08c69ad9461717658Yorke Lee                false /* isCircular */ , DEFAULT_AVATAR);
1224c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        BitmapHolder holder = new BitmapHolder(photoBytes, smallerExtent);
1225c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        holder.bitmapRef = new SoftReference<Bitmap>(bitmap);
1226c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        mBitmapHolderCache.put(request.getKey(), holder);
1227c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        mBitmapHolderCacheAllUnfresh = false;
1228c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        mBitmapCache.put(request.getKey(), bitmap);
1229c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    }
1230c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1231c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    /**
1232c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * Populates an array of photo IDs that need to be loaded. Also decodes bitmaps that we have
1233c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * already loaded
1234c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     */
1235c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    private void obtainPhotoIdsAndUrisToLoad(Set<Long> photoIds,
1236c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            Set<String> photoIdsAsStrings, Set<Request> uris) {
1237c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        photoIds.clear();
1238c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        photoIdsAsStrings.clear();
1239c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        uris.clear();
1240c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1241c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        boolean jpegsDecoded = false;
1242c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1243c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        /*
1244c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng         * Since the call is made from the loader thread, the map could be
1245c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng         * changing during the iteration. That's not really a problem:
1246c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng         * ConcurrentHashMap will allow those changes to happen without throwing
1247c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng         * exceptions. Since we may miss some requests in the situation of
1248c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng         * concurrent change, we will need to check the map again once loading
1249c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng         * is complete.
1250c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng         */
1251c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        Iterator<Request> iterator = mPendingRequests.values().iterator();
1252c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        while (iterator.hasNext()) {
1253c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            Request request = iterator.next();
1254c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            final BitmapHolder holder = mBitmapHolderCache.get(request.getKey());
1255b12672802db55182e0f07ae78814365b9ce9c694Yorke Lee            if (holder == BITMAP_UNAVAILABLE) {
1256b12672802db55182e0f07ae78814365b9ce9c694Yorke Lee                continue;
1257b12672802db55182e0f07ae78814365b9ce9c694Yorke Lee            }
1258c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            if (holder != null && holder.bytes != null && holder.fresh &&
1259c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    (holder.bitmapRef == null || holder.bitmapRef.get() == null)) {
1260c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                // This was previously loaded but we don't currently have the inflated Bitmap
1261c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                inflateBitmap(holder, request.getRequestedExtent());
1262c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                jpegsDecoded = true;
1263c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            } else {
1264c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                if (holder == null || !holder.fresh) {
1265c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    if (request.isUriRequest()) {
1266c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                        uris.add(request);
1267c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    } else {
1268c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                        photoIds.add(request.getId());
1269c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                        photoIdsAsStrings.add(String.valueOf(request.mId));
1270c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    }
1271c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                }
1272c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            }
1273c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
1274c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1275c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        if (jpegsDecoded) mMainThreadHandler.sendEmptyMessage(MESSAGE_PHOTOS_LOADED);
1276c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    }
1277c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1278c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    /**
1279c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * The thread that performs loading of photos from the database.
1280c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     */
1281c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    private class LoaderThread extends HandlerThread implements Callback {
1282c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        private static final int BUFFER_SIZE = 1024*16;
1283c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        private static final int MESSAGE_PRELOAD_PHOTOS = 0;
1284c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        private static final int MESSAGE_LOAD_PHOTOS = 1;
1285c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1286c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        /**
1287c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng         * A pause between preload batches that yields to the UI thread.
1288c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng         */
1289c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        private static final int PHOTO_PRELOAD_DELAY = 1000;
1290c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1291c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        /**
1292c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng         * Number of photos to preload per batch.
1293c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng         */
1294c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        private static final int PRELOAD_BATCH = 25;
1295c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1296c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        /**
1297c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng         * Maximum number of photos to preload.  If the cache size is 2Mb and
1298c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng         * the expected average size of a photo is 4kb, then this number should be 2Mb/4kb = 500.
1299c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng         */
1300c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        private static final int MAX_PHOTOS_TO_PRELOAD = 100;
1301c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1302c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        private final ContentResolver mResolver;
1303c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        private final StringBuilder mStringBuilder = new StringBuilder();
1304c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        private final Set<Long> mPhotoIds = Sets.newHashSet();
1305c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        private final Set<String> mPhotoIdsAsStrings = Sets.newHashSet();
1306c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        private final Set<Request> mPhotoUris = Sets.newHashSet();
1307c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        private final List<Long> mPreloadPhotoIds = Lists.newArrayList();
1308c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1309c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        private Handler mLoaderThreadHandler;
1310c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        private byte mBuffer[];
1311c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1312c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        private static final int PRELOAD_STATUS_NOT_STARTED = 0;
1313c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        private static final int PRELOAD_STATUS_IN_PROGRESS = 1;
1314c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        private static final int PRELOAD_STATUS_DONE = 2;
1315c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1316c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        private int mPreloadStatus = PRELOAD_STATUS_NOT_STARTED;
1317c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1318c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        public LoaderThread(ContentResolver resolver) {
1319c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            super(LOADER_THREAD_NAME);
1320c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            mResolver = resolver;
1321c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
1322c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1323c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        public void ensureHandler() {
1324c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            if (mLoaderThreadHandler == null) {
1325c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                mLoaderThreadHandler = new Handler(getLooper(), this);
1326c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            }
1327c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
1328c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1329c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        /**
1330c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng         * Kicks off preloading of the next batch of photos on the background thread.
1331c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng         * Preloading will happen after a delay: we want to yield to the UI thread
1332c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng         * as much as possible.
1333c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng         * <p>
1334c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng         * If preloading is already complete, does nothing.
1335c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng         */
1336c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        public void requestPreloading() {
1337c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            if (mPreloadStatus == PRELOAD_STATUS_DONE) {
1338c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                return;
1339c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            }
1340c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1341c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            ensureHandler();
1342c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            if (mLoaderThreadHandler.hasMessages(MESSAGE_LOAD_PHOTOS)) {
1343c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                return;
1344c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            }
1345c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1346c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            mLoaderThreadHandler.sendEmptyMessageDelayed(
1347c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    MESSAGE_PRELOAD_PHOTOS, PHOTO_PRELOAD_DELAY);
1348c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
1349c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1350c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        /**
1351c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng         * Sends a message to this thread to load requested photos.  Cancels a preloading
1352c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng         * request, if any: we don't want preloading to impede loading of the photos
1353c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng         * we need to display now.
1354c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng         */
1355c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        public void requestLoading() {
1356c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            ensureHandler();
1357c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            mLoaderThreadHandler.removeMessages(MESSAGE_PRELOAD_PHOTOS);
1358c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            mLoaderThreadHandler.sendEmptyMessage(MESSAGE_LOAD_PHOTOS);
1359c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
1360c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1361c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        /**
1362c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng         * Receives the above message, loads photos and then sends a message
1363c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng         * to the main thread to process them.
1364c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng         */
1365c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        @Override
1366c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        public boolean handleMessage(Message msg) {
1367c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            switch (msg.what) {
1368c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                case MESSAGE_PRELOAD_PHOTOS:
1369c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    preloadPhotosInBackground();
1370c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    break;
1371c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                case MESSAGE_LOAD_PHOTOS:
1372c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    loadPhotosInBackground();
1373c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    break;
1374c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            }
1375c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            return true;
1376c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
1377c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1378c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        /**
1379c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng         * The first time it is called, figures out which photos need to be preloaded.
1380c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng         * Each subsequent call preloads the next batch of photos and requests
1381c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng         * another cycle of preloading after a delay.  The whole process ends when
1382c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng         * we either run out of photos to preload or fill up cache.
1383c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng         */
1384c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        private void preloadPhotosInBackground() {
1385c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            if (mPreloadStatus == PRELOAD_STATUS_DONE) {
1386c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                return;
1387c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            }
1388c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1389c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            if (mPreloadStatus == PRELOAD_STATUS_NOT_STARTED) {
1390c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                queryPhotosForPreload();
1391c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                if (mPreloadPhotoIds.isEmpty()) {
1392c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    mPreloadStatus = PRELOAD_STATUS_DONE;
1393c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                } else {
1394c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    mPreloadStatus = PRELOAD_STATUS_IN_PROGRESS;
1395c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                }
1396c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                requestPreloading();
1397c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                return;
1398c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            }
1399c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1400c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            if (mBitmapHolderCache.size() > mBitmapHolderCacheRedZoneBytes) {
1401c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                mPreloadStatus = PRELOAD_STATUS_DONE;
1402c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                return;
1403c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            }
1404c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1405c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            mPhotoIds.clear();
1406c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            mPhotoIdsAsStrings.clear();
1407c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1408c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            int count = 0;
1409c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            int preloadSize = mPreloadPhotoIds.size();
1410c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            while(preloadSize > 0 && mPhotoIds.size() < PRELOAD_BATCH) {
1411c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                preloadSize--;
1412c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                count++;
1413c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                Long photoId = mPreloadPhotoIds.get(preloadSize);
1414c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                mPhotoIds.add(photoId);
1415c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                mPhotoIdsAsStrings.add(photoId.toString());
1416c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                mPreloadPhotoIds.remove(preloadSize);
1417c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            }
1418c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1419c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            loadThumbnails(true);
1420c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1421c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            if (preloadSize == 0) {
1422c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                mPreloadStatus = PRELOAD_STATUS_DONE;
1423c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            }
1424c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1425c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            Log.v(TAG, "Preloaded " + count + " photos.  Cached bytes: "
1426c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    + mBitmapHolderCache.size());
1427c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1428c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            requestPreloading();
1429c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
1430c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1431c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        private void queryPhotosForPreload() {
1432c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            Cursor cursor = null;
1433c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            try {
1434c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                Uri uri = Contacts.CONTENT_URI.buildUpon().appendQueryParameter(
1435c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                        ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(Directory.DEFAULT))
1436c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                        .appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY,
1437c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                                String.valueOf(MAX_PHOTOS_TO_PRELOAD))
1438c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                        .build();
1439c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                cursor = mResolver.query(uri, new String[] { Contacts.PHOTO_ID },
1440c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                        Contacts.PHOTO_ID + " NOT NULL AND " + Contacts.PHOTO_ID + "!=0",
1441c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                        null,
1442c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                        Contacts.STARRED + " DESC, " + Contacts.LAST_TIME_CONTACTED + " DESC");
1443c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1444c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                if (cursor != null) {
1445c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    while (cursor.moveToNext()) {
1446c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                        // Insert them in reverse order, because we will be taking
1447c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                        // them from the end of the list for loading.
1448c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                        mPreloadPhotoIds.add(0, cursor.getLong(0));
1449c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    }
1450c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                }
1451c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            } finally {
1452c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                if (cursor != null) {
1453c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    cursor.close();
1454c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                }
1455c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            }
1456c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
1457c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1458c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        private void loadPhotosInBackground() {
14598adebb791c7e2858d274c5dd927a9da53bd911c5Yorke Lee            if (!PermissionsUtil.hasPermission(mContext,
14608adebb791c7e2858d274c5dd927a9da53bd911c5Yorke Lee                    android.Manifest.permission.READ_CONTACTS)) {
14618adebb791c7e2858d274c5dd927a9da53bd911c5Yorke Lee                return;
14628adebb791c7e2858d274c5dd927a9da53bd911c5Yorke Lee            }
1463c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            obtainPhotoIdsAndUrisToLoad(mPhotoIds, mPhotoIdsAsStrings, mPhotoUris);
1464c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            loadThumbnails(false);
1465c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            loadUriBasedPhotos();
1466c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            requestPreloading();
1467c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
1468c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1469c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        /** Loads thumbnail photos with ids */
1470c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        private void loadThumbnails(boolean preloading) {
1471c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            if (mPhotoIds.isEmpty()) {
1472c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                return;
1473c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            }
1474c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1475c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            // Remove loaded photos from the preload queue: we don't want
1476c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            // the preloading process to load them again.
1477c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            if (!preloading && mPreloadStatus == PRELOAD_STATUS_IN_PROGRESS) {
1478c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                for (Long id : mPhotoIds) {
1479c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    mPreloadPhotoIds.remove(id);
1480c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                }
1481c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                if (mPreloadPhotoIds.isEmpty()) {
1482c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    mPreloadStatus = PRELOAD_STATUS_DONE;
1483c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                }
1484c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            }
1485c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1486c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            mStringBuilder.setLength(0);
1487c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            mStringBuilder.append(Photo._ID + " IN(");
1488c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            for (int i = 0; i < mPhotoIds.size(); i++) {
1489c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                if (i != 0) {
1490c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    mStringBuilder.append(',');
1491c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                }
1492c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                mStringBuilder.append('?');
1493c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            }
1494c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            mStringBuilder.append(')');
1495c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1496c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            Cursor cursor = null;
1497c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            try {
1498c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                if (DEBUG) Log.d(TAG, "Loading " + TextUtils.join(",", mPhotoIdsAsStrings));
1499c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                cursor = mResolver.query(Data.CONTENT_URI,
1500c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                        COLUMNS,
1501c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                        mStringBuilder.toString(),
1502c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                        mPhotoIdsAsStrings.toArray(EMPTY_STRING_ARRAY),
1503c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                        null);
1504c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1505c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                if (cursor != null) {
1506c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    while (cursor.moveToNext()) {
1507c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                        Long id = cursor.getLong(0);
1508c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                        byte[] bytes = cursor.getBlob(1);
1509c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                        cacheBitmap(id, bytes, preloading, -1);
1510c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                        mPhotoIds.remove(id);
1511c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    }
1512c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                }
1513c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            } finally {
1514c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                if (cursor != null) {
1515c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    cursor.close();
1516c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                }
1517c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            }
1518c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1519c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            // Remaining photos were not found in the contacts database (but might be in profile).
1520c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            for (Long id : mPhotoIds) {
1521c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                if (ContactsContract.isProfileId(id)) {
1522c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    Cursor profileCursor = null;
1523c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    try {
1524c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                        profileCursor = mResolver.query(
1525c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                                ContentUris.withAppendedId(Data.CONTENT_URI, id),
1526c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                                COLUMNS, null, null, null);
1527c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                        if (profileCursor != null && profileCursor.moveToFirst()) {
1528c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                            cacheBitmap(profileCursor.getLong(0), profileCursor.getBlob(1),
1529c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                                    preloading, -1);
1530c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                        } else {
1531c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                            // Couldn't load a photo this way either.
1532c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                            cacheBitmap(id, null, preloading, -1);
1533c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                        }
1534c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    } finally {
1535c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                        if (profileCursor != null) {
1536c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                            profileCursor.close();
1537c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                        }
1538c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    }
1539c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                } else {
1540c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    // Not a profile photo and not found - mark the cache accordingly
1541c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    cacheBitmap(id, null, preloading, -1);
1542c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                }
1543c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            }
1544c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1545c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            mMainThreadHandler.sendEmptyMessage(MESSAGE_PHOTOS_LOADED);
1546c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
1547c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1548c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        /**
1549c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng         * Loads photos referenced with Uris. Those can be remote thumbnails
1550c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng         * (from directory searches), display photos etc
1551c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng         */
1552c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        private void loadUriBasedPhotos() {
1553c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            for (Request uriRequest : mPhotoUris) {
155407bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn                // Keep the original URI and use this to key into the cache.  Failure to do so will
155507bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn                // result in an image being continually reloaded into cache if the original URI
155607bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn                // has a contact type encodedFragment (eg nearby places business photo URLs).
155707bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn                Uri originalUri = uriRequest.getUri();
155807bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn
155907bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn                // Strip off the "contact type" we added to the URI to ensure it was identifiable as
156007bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn                // a business photo -- there is no need to pass this on to the server.
156107bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn                Uri uri = ContactPhotoManager.removeContactType(originalUri);
156207bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn
1563c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                if (mBuffer == null) {
1564c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    mBuffer = new byte[BUFFER_SIZE];
1565c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                }
1566c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                try {
1567c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    if (DEBUG) Log.d(TAG, "Loading " + uri);
15689a0f2498251247781c7ff0f20dc09c509eccecc9Jay Shrauner                    final String scheme = uri.getScheme();
15699a0f2498251247781c7ff0f20dc09c509eccecc9Jay Shrauner                    InputStream is = null;
15709a0f2498251247781c7ff0f20dc09c509eccecc9Jay Shrauner                    if (scheme.equals("http") || scheme.equals("https")) {
157148a393e750f69a7cde240e8596d645b5898ccc0aYorke Lee                        TrafficStats.setThreadStatsTag(TrafficStatsTags.CONTACT_PHOTO_DOWNLOAD_TAG);
157277cb7da3bcdc92d7ea8369c717b05f72ab6b33d5Tyler Gunn                        final HttpURLConnection connection =
157377cb7da3bcdc92d7ea8369c717b05f72ab6b33d5Tyler Gunn                                (HttpURLConnection) new URL(uri.toString()).openConnection();
157477cb7da3bcdc92d7ea8369c717b05f72ab6b33d5Tyler Gunn
157577cb7da3bcdc92d7ea8369c717b05f72ab6b33d5Tyler Gunn                        // Include the user agent if it is specified.
157677cb7da3bcdc92d7ea8369c717b05f72ab6b33d5Tyler Gunn                        if (!TextUtils.isEmpty(mUserAgent)) {
157777cb7da3bcdc92d7ea8369c717b05f72ab6b33d5Tyler Gunn                            connection.setRequestProperty("User-Agent", mUserAgent);
157877cb7da3bcdc92d7ea8369c717b05f72ab6b33d5Tyler Gunn                        }
157977cb7da3bcdc92d7ea8369c717b05f72ab6b33d5Tyler Gunn                        try {
158077cb7da3bcdc92d7ea8369c717b05f72ab6b33d5Tyler Gunn                            is = connection.getInputStream();
158177cb7da3bcdc92d7ea8369c717b05f72ab6b33d5Tyler Gunn                        } catch (IOException e) {
158277cb7da3bcdc92d7ea8369c717b05f72ab6b33d5Tyler Gunn                            connection.disconnect();
158377cb7da3bcdc92d7ea8369c717b05f72ab6b33d5Tyler Gunn                            is = null;
158477cb7da3bcdc92d7ea8369c717b05f72ab6b33d5Tyler Gunn                        }
158548a393e750f69a7cde240e8596d645b5898ccc0aYorke Lee                        TrafficStats.clearThreadStatsTag();
15869a0f2498251247781c7ff0f20dc09c509eccecc9Jay Shrauner                    } else {
15879a0f2498251247781c7ff0f20dc09c509eccecc9Jay Shrauner                        is = mResolver.openInputStream(uri);
15889a0f2498251247781c7ff0f20dc09c509eccecc9Jay Shrauner                    }
1589c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    if (is != null) {
1590c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                        ByteArrayOutputStream baos = new ByteArrayOutputStream();
1591c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                        try {
1592c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                            int size;
1593c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                            while ((size = is.read(mBuffer)) != -1) {
1594c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                                baos.write(mBuffer, 0, size);
1595c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                            }
1596c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                        } finally {
1597c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                            is.close();
1598c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                        }
159907bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn                        cacheBitmap(originalUri, baos.toByteArray(), false,
1600c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                                uriRequest.getRequestedExtent());
1601c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                        mMainThreadHandler.sendEmptyMessage(MESSAGE_PHOTOS_LOADED);
1602c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    } else {
1603c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                        Log.v(TAG, "Cannot load photo " + uri);
160407bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn                        cacheBitmap(originalUri, null, false, uriRequest.getRequestedExtent());
1605c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    }
160698575ef624c1f8e41c126448763663ecd28ffc0aJay Shrauner                } catch (final Exception | OutOfMemoryError ex) {
1607c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                    Log.v(TAG, "Cannot load photo " + uri, ex);
160807bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn                    cacheBitmap(originalUri, null, false, uriRequest.getRequestedExtent());
1609c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                }
1610c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            }
1611c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
1612c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    }
1613c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1614c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    /**
1615c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * A holder for either a Uri or an id and a flag whether this was requested for the dark or
1616c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     * light theme
1617c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng     */
1618c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    private static final class Request {
1619c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        private final long mId;
1620c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        private final Uri mUri;
1621c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        private final boolean mDarkTheme;
1622c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        private final int mRequestedExtent;
1623c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        private final DefaultImageProvider mDefaultProvider;
16243f9c2f426058413055fa54c08c69ad9461717658Yorke Lee        /**
16253f9c2f426058413055fa54c08c69ad9461717658Yorke Lee         * Whether or not the contact photo is to be displayed as a circle
16263f9c2f426058413055fa54c08c69ad9461717658Yorke Lee         */
16273f9c2f426058413055fa54c08c69ad9461717658Yorke Lee        private final boolean mIsCircular;
1628c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1629c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        private Request(long id, Uri uri, int requestedExtent, boolean darkTheme,
16303f9c2f426058413055fa54c08c69ad9461717658Yorke Lee                boolean isCircular, DefaultImageProvider defaultProvider) {
1631c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            mId = id;
1632c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            mUri = uri;
1633c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            mDarkTheme = darkTheme;
16343f9c2f426058413055fa54c08c69ad9461717658Yorke Lee            mIsCircular = isCircular;
1635c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            mRequestedExtent = requestedExtent;
1636c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            mDefaultProvider = defaultProvider;
1637c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
1638c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
16393f9c2f426058413055fa54c08c69ad9461717658Yorke Lee        public static Request createFromThumbnailId(long id, boolean darkTheme, boolean isCircular,
1640c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng                DefaultImageProvider defaultProvider) {
16413f9c2f426058413055fa54c08c69ad9461717658Yorke Lee            return new Request(id, null /* no URI */, -1, darkTheme, isCircular, defaultProvider);
1642c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
1643c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1644c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        public static Request createFromUri(Uri uri, int requestedExtent, boolean darkTheme,
16453f9c2f426058413055fa54c08c69ad9461717658Yorke Lee                boolean isCircular, DefaultImageProvider defaultProvider) {
16463f9c2f426058413055fa54c08c69ad9461717658Yorke Lee            return new Request(0 /* no ID */, uri, requestedExtent, darkTheme, isCircular,
16473f9c2f426058413055fa54c08c69ad9461717658Yorke Lee                    defaultProvider);
1648c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
1649c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1650c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        public boolean isUriRequest() {
1651c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            return mUri != null;
1652c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
1653c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1654c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        public Uri getUri() {
1655c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            return mUri;
1656c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
1657c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1658c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        public long getId() {
1659c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            return mId;
1660c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
1661c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1662c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        public int getRequestedExtent() {
1663c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            return mRequestedExtent;
1664c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
1665c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1666c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        @Override
1667c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        public int hashCode() {
1668c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            final int prime = 31;
1669c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            int result = 1;
1670c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            result = prime * result + (int) (mId ^ (mId >>> 32));
1671c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            result = prime * result + mRequestedExtent;
1672c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            result = prime * result + ((mUri == null) ? 0 : mUri.hashCode());
1673c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            return result;
1674c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
1675c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1676c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        @Override
1677c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        public boolean equals(Object obj) {
1678c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            if (this == obj) return true;
1679c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            if (obj == null) return false;
1680c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            if (getClass() != obj.getClass()) return false;
1681c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            final Request that = (Request) obj;
1682c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            if (mId != that.mId) return false;
1683c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            if (mRequestedExtent != that.mRequestedExtent) return false;
1684c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            if (!UriUtils.areEqual(mUri, that.mUri)) return false;
1685c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            // Don't compare equality of mDarkTheme because it is only used in the default contact
1686c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            // photo case. When the contact does have a photo, the contact photo is the same
1687c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            // regardless of mDarkTheme, so we shouldn't need to put the photo request on the queue
1688c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            // twice.
1689c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            return true;
1690c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
1691c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
1692c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        public Object getKey() {
1693c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng            return mUri == null ? mId : mUri;
1694c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
1695c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng
169607bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn        /**
169707bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn         * Applies the default image to the current view. If the request is URI-based, looks for
169807bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn         * the contact type encoded fragment to determine if this is a request for a business photo,
169907bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn         * in which case we will load the default business photo.
170007bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn         *
170107bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn         * @param view The current image view to apply the image to.
170207bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn         * @param isCircular Whether the image is circular or not.
170307bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn         */
17043f9c2f426058413055fa54c08c69ad9461717658Yorke Lee        public void applyDefaultImage(ImageView view, boolean isCircular) {
170507bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn            final DefaultImageRequest request;
170607bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn
170707bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn            if (isCircular) {
170807bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn                request = ContactPhotoManager.isBusinessContactUri(mUri)
170907bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn                        ? DefaultImageRequest.EMPTY_CIRCULAR_BUSINESS_IMAGE_REQUEST
171007bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn                        : DefaultImageRequest.EMPTY_CIRCULAR_DEFAULT_IMAGE_REQUEST;
171107bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn            } else {
171207bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn                request = ContactPhotoManager.isBusinessContactUri(mUri)
171307bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn                        ? DefaultImageRequest.EMPTY_DEFAULT_BUSINESS_IMAGE_REQUEST
171407bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn                        : DefaultImageRequest.EMPTY_DEFAULT_IMAGE_REQUEST;
171507bbbaa24a73cb4386a6ff3bd8cfe105f1fe5c45Tyler Gunn            }
17163f9c2f426058413055fa54c08c69ad9461717658Yorke Lee            mDefaultProvider.applyDefaultImage(view, mRequestedExtent, mDarkTheme, request);
1717c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng        }
1718c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng    }
1719c36c3196c594b992cec8b5de1a3dba7556648804Chiao Cheng}
1720