1e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev/* 2e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev * Copyright (C) 2016 The Android Open Source Project 3e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev * 4e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev * Licensed under the Apache License, Version 2.0 (the "License"); 5e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev * you may not use this file except in compliance with the License. 6e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev * You may obtain a copy of the License at 7e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev * 8e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev * http://www.apache.org/licenses/LICENSE-2.0 9e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev * 10e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev * Unless required by applicable law or agreed to in writing, software 11e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev * distributed under the License is distributed on an "AS IS" BASIS, 12e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev * See the License for the specific language governing permissions and 14e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev * limitations under the License. 15e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev */ 16e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 17e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsevpackage android.car.cluster.demorenderer; 18e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 19e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsevimport static android.provider.ContactsContract.Contacts.openContactPhotoInputStream; 20e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 21e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsevimport android.annotation.Nullable; 22e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsevimport android.content.ContentResolver; 23e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsevimport android.content.ContentUris; 24e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsevimport android.content.Context; 25e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsevimport android.content.CursorLoader; 26e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsevimport android.content.Loader; 27e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsevimport android.content.Loader.OnLoadCompleteListener; 28e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsevimport android.content.res.Resources; 29e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsevimport android.database.Cursor; 30e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsevimport android.graphics.Bitmap; 31e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsevimport android.graphics.BitmapFactory; 32e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsevimport android.graphics.BitmapFactory.Options; 33e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsevimport android.graphics.Rect; 34e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsevimport android.net.Uri; 35e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsevimport android.os.AsyncTask; 36e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsevimport android.provider.ContactsContract; 37e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsevimport android.provider.ContactsContract.CommonDataKinds.Phone; 38e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsevimport android.provider.ContactsContract.PhoneLookup; 39e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsevimport android.telephony.PhoneNumberUtils; 40e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsevimport android.telephony.TelephonyManager; 41e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsevimport android.text.TextUtils; 42e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsevimport android.util.Log; 43e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsevimport android.util.LruCache; 44e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 45e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsevimport java.io.InputStream; 46e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsevimport java.lang.ref.WeakReference; 47e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsevimport java.util.HashMap; 48e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsevimport java.util.HashSet; 49e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsevimport java.util.Locale; 50e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsevimport java.util.Set; 51e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 52e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev/** 53e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev * Class that provides contact information. 54e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev */ 55e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsevclass PhoneBook { 56e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 57e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev private final static String TAG = PhoneBook.class.getSimpleName(); 58e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 59e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev private final ContentResolver mContentResolver; 60e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev private final Context mContext; 61e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev private final TelephonyManager mTelephonyManager; 62e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev private final Object mSyncContact = new Object(); 63e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev private final Object mSyncPhoto = new Object(); 64e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 65e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev private volatile String mVoiceMail; 66e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 67e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev private static final String[] CONTACT_ID_PROJECTION = new String[] { 68e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev PhoneLookup.DISPLAY_NAME, 69e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev PhoneLookup.TYPE, 70e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev PhoneLookup.LABEL, 71e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev PhoneLookup._ID 72e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev }; 73e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 74e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev private HashMap<String, Contact> mContactByNumber; 75e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev private LruCache<Integer, Bitmap> mContactPhotoById; 76e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev private Set<Integer> mContactsWithoutImage; 77e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 78e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev PhoneBook(Context context, TelephonyManager telephonyManager) { 79e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev mContentResolver = context.getContentResolver(); 80e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev mContext = context; 81e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev mTelephonyManager = telephonyManager; 82e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 83e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 84e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev /** 85e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev * Formats provided number according to current locale. 86e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev * */ 87e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev public static String getFormattedNumber(String number) { 88e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev if (TextUtils.isEmpty(number)) { 89e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev return ""; 90e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 91e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 92e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev String countryIso = Locale.getDefault().getCountry(); 93e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev if (countryIso == null || countryIso.length() != 2) { 94e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev countryIso = "US"; 95e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 96e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev String e164 = PhoneNumberUtils.formatNumberToE164(number, countryIso); 97e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev String formattedNumber = PhoneNumberUtils.formatNumber(number, e164, countryIso); 98e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev formattedNumber = TextUtils.isEmpty(formattedNumber) ? number : formattedNumber; 99e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev return formattedNumber; 100e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 101e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 102e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev /** 103e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev * Loads contact details for a given phone number asynchronously. It may call listener's 104e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev * callback function immediately if there were image in the cache. 105e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev */ 106e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev public void getContactDetailsAsync(String number, ContactLoadedListener listener) { 107e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev if (number == null || number.isEmpty()) { 108e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev listener.onContactLoaded(number, null); 109e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev return; 110e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 111e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 112e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev synchronized (mSyncContact) { 113e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev if (mContactByNumber == null) { 114e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev mContactByNumber = new HashMap<>(); 115e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } else if (mContactByNumber.containsKey(number)) { 116e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev listener.onContactLoaded(number, mContactByNumber.get(number)); 117e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev return; 118e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 119e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 120e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 121e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev fetchContactAsync(number, listener); 122e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 123e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 124e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev /** 125e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev * Loads photo for a given contactId asynchronously. It may call listener's callback function 126e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev * immediately if there were image in the cache. 127e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev */ 128e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev public void getContactPictureAsync(int contactId, ContactPhotoLoadedListener listener) { 129e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev synchronized (mSyncPhoto) { 130e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev if (mContactsWithoutImage != null && mContactsWithoutImage.contains(contactId)) { 131e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev listener.onPhotoLoaded(contactId, null); 132e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev return; 133e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 134e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 135e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev if (mContactPhotoById == null) { 136e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev mContactPhotoById = new LruCache<Integer, Bitmap>(4 << 20 /* 4mb */) { 137e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev @Override 138e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev protected int sizeOf(Integer key, Bitmap value) { 139e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev return value.getByteCount(); 140e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 141e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev }; 142e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } else { 143e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev Bitmap photo = mContactPhotoById.get(contactId); 144e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev if (photo != null) { 145e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev listener.onPhotoLoaded(contactId, photo); 146e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev return; 147e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 148e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 149e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 150e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 151e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev fetchPhotoAsync(contactId, listener); 152e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 153e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 154e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev /** Returns true if given phone number is a voice mail number. */ 155e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev public boolean isVoicemail(String number) { 156e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev return !TextUtils.isEmpty(number) && number.equals(getVoiceMailNumber()); 157e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 158e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 159e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev @Nullable 160e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev private String getVoiceMailNumber() { 161e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev if (mVoiceMail == null) { 162e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev mVoiceMail = mTelephonyManager.getVoiceMailNumber(); 163e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 164e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 165e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev return mVoiceMail; 166e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 167e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 168e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev interface ContactLoadedListener { 169e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev void onContactLoaded(String number, @Nullable Contact contact); 170e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 171e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 172e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev interface ContactPhotoLoadedListener { 173e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev void onPhotoLoaded(int contactId, @Nullable Bitmap picture); 174e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 175e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 176e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev private void fetchContactAsync(String number, ContactLoadedListener listener) { 177e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev CursorLoader cursorLoader = new CursorLoader(mContext); 178e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev cursorLoader.setUri(Uri.withAppendedPath( 179e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev PhoneLookup.CONTENT_FILTER_URI, 180e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev Uri.encode(number))); 181e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev cursorLoader.setProjection(CONTACT_ID_PROJECTION); 182e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev cursorLoader.registerListener(0, new LoadCompleteListener(this, number, listener)); 183e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev cursorLoader.startLoading(); 184e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 185e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 186e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev private void fetchPhotoAsync(int contactId, ContactPhotoLoadedListener listener) { 187e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev LoadPhotoAsyncTask.createAndExecute(this, contactId, listener); 188e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 189e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 190e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev private void cacheContactPhoto(int contactId, Bitmap bitmap) { 191e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev synchronized (mSyncPhoto) { 192e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev if (bitmap != null) { 193e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev mContactPhotoById.put(contactId, bitmap); 194e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } else { 195e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev if (mContactsWithoutImage == null) { 196e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev mContactsWithoutImage = new HashSet<>(); 197e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 198e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev mContactsWithoutImage.add(contactId); 199e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 200e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 201e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 202e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 203e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev static class Contact { 204e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev private final int mId; 205e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev private final String mName; 206e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev private final CharSequence mType; 207e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev private final String mNumber; 208e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 209e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev Contact(Resources resources, String number, int id, String name, String label, int type) { 210e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev mNumber = number; 211e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev mId = id; 212e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev mName = name; 213e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev mType = Phone.getTypeLabel(resources, type, label); 214e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 215e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 216e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev int getId() { 217e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev return mId; 218e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 219e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 220e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev public String getName() { 221e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev return mName; 222e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 223e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 224e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev public CharSequence getType() { 225e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev return mType; 226e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 227e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 228e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev public String getNumber() { return mNumber; } 229e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 230e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 231e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev private static class LoadPhotoAsyncTask extends AsyncTask<Void, Void, Bitmap> { 232e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 233e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev private final WeakReference<PhoneBook> mPhoneBookRef; 234e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev private final ContactPhotoLoadedListener mListener; 235e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev private final int mContactId; 236e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 237e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev static void createAndExecute(PhoneBook phoneBook, int contactId, 238e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev ContactPhotoLoadedListener listener) { 239e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev new LoadPhotoAsyncTask(phoneBook, contactId, listener) 240e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev .execute(); 241e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 242e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 243e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev private LoadPhotoAsyncTask(PhoneBook phoneBook, int contactId, 244e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev ContactPhotoLoadedListener listener) { 245e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev mPhoneBookRef = new WeakReference<>(phoneBook); 246e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev mContactId = contactId; 247e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev mListener = listener; 248e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 249e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 250e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev @Nullable 251e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev private Bitmap fetchBitmap(int contactId) { 252e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev Log.d(TAG, "fetchBitmap, contactId: " + contactId); 253e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev PhoneBook phoneBook = mPhoneBookRef.get(); 254e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev if (phoneBook == null) { 255e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev return null; 256e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 257e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 258e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev Uri uri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, contactId); 259e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev InputStream photoDataStream = openContactPhotoInputStream( 260e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev phoneBook.mContentResolver, uri, true); 261e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev Log.d(TAG, "fetchBitmap, uri: " + uri); 262e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 263e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev Options options = new Options(); 264e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev options.inPreferQualityOverSpeed = true; 265e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev options.inScaled = false; 266e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev Rect nullPadding = null; 267e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev Bitmap photo = BitmapFactory.decodeStream(photoDataStream, nullPadding, options); 268e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev if (photo != null) { 269e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev photo.setDensity(Bitmap.DENSITY_NONE); 270e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 271e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev Log.d(TAG, "bitmap fetched: " + photo); 272e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev return photo; 273e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 274e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 275e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev @Override 276e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev protected Bitmap doInBackground(Void... params) { 277e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev return fetchBitmap(mContactId); 278e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 279e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 280e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev @Override 281e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev protected void onPostExecute(Bitmap bitmap) { 282e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev PhoneBook phoneBook = mPhoneBookRef.get(); 283e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev if (phoneBook != null) { 284e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev phoneBook.cacheContactPhoto(mContactId, bitmap); 285e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 286e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev mListener.onPhotoLoaded(0, bitmap); 287e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 288e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 289e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 290e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev private static class LoadCompleteListener implements OnLoadCompleteListener<Cursor> { 291e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev private final String mNumber; 292e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev private final ContactLoadedListener mContactLoadedListener; 293e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev private final WeakReference<PhoneBook> mPhoneBookRef; 294e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 295e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev private LoadCompleteListener(PhoneBook phoneBook, String number, 296e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev ContactLoadedListener contactLoadedListener) { 297e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev mPhoneBookRef = new WeakReference<>(phoneBook); 298e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev mNumber = number; 299e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev mContactLoadedListener = contactLoadedListener; 300e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 301e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 302e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev @Override 303e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev public void onLoadComplete(Loader<Cursor> loader, Cursor cursor) { 304e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev Log.d(TAG, "onLoadComplete, cursor: " + cursor); 305e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev PhoneBook phoneBook = mPhoneBookRef.get(); 306e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev Contact contact = null; 307e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev if (cursor != null && phoneBook != null) { 308e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev try { 309e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev if (cursor.moveToFirst()) { 310e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev int id = cursor.getInt(cursor.getColumnIndex(PhoneLookup._ID)); 311e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev String name = cursor 312e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev .getString(cursor.getColumnIndex(PhoneLookup.DISPLAY_NAME)); 313e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev String label = cursor.getString(cursor.getColumnIndex(PhoneLookup.LABEL)); 314e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev int type = cursor.getInt(cursor.getColumnIndex(PhoneLookup.TYPE)); 315e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev Resources resources = phoneBook.mContext.getResources(); 316e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev contact = new Contact(resources, mNumber, id, name, label, type); 317e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 318e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } finally { 319e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev cursor.close(); 320e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 321e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 322e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev if (contact != null) { 323e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev synchronized (phoneBook.mSyncContact) { 324e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev phoneBook.mContactByNumber.put(mNumber, contact); 325e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 326e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 327e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 328e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev 329e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev mContactLoadedListener.onContactLoaded(mNumber, contact); 330e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 331e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev } 332e909dff3fdad5fec56377ebee6978adf45373cb6Pavel Maltsev} 333