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