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