1a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei/*
2a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei * Copyright (C) 2013 Google Inc.
3a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei * Licensed to The Android Open Source Project.
4a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei *
5a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei * Licensed under the Apache License, Version 2.0 (the "License");
6a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei * you may not use this file except in compliance with the License.
7a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei * You may obtain a copy of the License at
8a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei *
9a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei *      http://www.apache.org/licenses/LICENSE-2.0
10a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei *
11a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei * Unless required by applicable law or agreed to in writing, software
12a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei * distributed under the License is distributed on an "AS IS" BASIS,
13a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei * See the License for the specific language governing permissions and
15a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei * limitations under the License.
16a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei */
17a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
18a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Weipackage com.android.mail.bitmap;
19a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
20a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Weiimport android.content.ContentResolver;
21a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Weiimport android.os.AsyncTask;
22a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Weiimport android.os.AsyncTask.Status;
23a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Weiimport android.os.Handler;
24a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
252b806edc62eb8e83c77edc471fda4652281a15c4James Lemieuximport com.android.bitmap.BitmapCache;
262b806edc62eb8e83c77edc471fda4652281a15c4James Lemieuximport com.android.bitmap.DecodeTask;
272b806edc62eb8e83c77edc471fda4652281a15c4James Lemieuximport com.android.bitmap.RequestKey;
282b806edc62eb8e83c77edc471fda4652281a15c4James Lemieuximport com.android.bitmap.ReusableBitmap;
29a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Weiimport com.android.ex.photo.util.Trace;
30a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Weiimport com.android.mail.ContactInfo;
31a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Weiimport com.android.mail.SenderInfoLoader;
32a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Weiimport com.android.mail.bitmap.ContactRequest.ContactRequestHolder;
33a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Weiimport com.android.mail.utils.LogTag;
34a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Weiimport com.android.mail.utils.LogUtils;
35a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Weiimport com.google.common.collect.ImmutableMap;
36a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
37a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Weiimport java.util.HashSet;
38a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Weiimport java.util.LinkedHashSet;
39a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Weiimport java.util.Set;
40a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Weiimport java.util.concurrent.Executor;
41a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Weiimport java.util.concurrent.LinkedBlockingQueue;
42a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Weiimport java.util.concurrent.ThreadPoolExecutor;
43a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Weiimport java.util.concurrent.TimeUnit;
44a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
45a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei/**
46a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei * Batches up ContactRequests so we can efficiently query the contacts provider. Kicks off a
47a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei * ContactResolverTask to query for contact images in the background.
48a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei */
49a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Weipublic class ContactResolver implements Runnable {
50a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
51a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    private static final String TAG = LogTag.getLogTag();
52a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
53c12da4a53eee911765975f0d8afe62f749806b60Jin Cao    // The maximum size returned from ContactsContract.Contacts.Photo.PHOTO is 96px by 96px.
54c12da4a53eee911765975f0d8afe62f749806b60Jin Cao    private static final int MAXIMUM_PHOTO_SIZE = 96;
55c12da4a53eee911765975f0d8afe62f749806b60Jin Cao    private static final int HALF_MAXIMUM_PHOTO_SIZE = 48;
56c12da4a53eee911765975f0d8afe62f749806b60Jin Cao
578913ca6aebe6017efe8060b61b8ad38eda95aa8bAndrew Sapperstein    protected final ContentResolver mResolver;
58a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    private final BitmapCache mCache;
59a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    /** Insertion ordered set allows us to work from the top down. */
60a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    private final LinkedHashSet<ContactRequestHolder> mBatch;
61a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
62a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    private final Handler mHandler = new Handler();
63a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    private ContactResolverTask mTask;
64a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
65a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
66a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    /** Size 1 pool mostly to make systrace output traces on one line. */
67a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    private static final Executor SMALL_POOL_EXECUTOR = new ThreadPoolExecutor(1, 1,
68a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            1, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
69a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    private static final Executor EXECUTOR = SMALL_POOL_EXECUTOR;
70a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
71c7849b23a73d699b5e7f199f0a3afce5b9dee7a6Martin Hibdon    public interface ContactDrawableInterface {
722b806edc62eb8e83c77edc471fda4652281a15c4James Lemieux        public void onDecodeComplete(final RequestKey key, final ReusableBitmap result);
732b806edc62eb8e83c77edc471fda4652281a15c4James Lemieux        public int getDecodeWidth();
742b806edc62eb8e83c77edc471fda4652281a15c4James Lemieux        public int getDecodeHeight();
75c7849b23a73d699b5e7f199f0a3afce5b9dee7a6Martin Hibdon    }
76c7849b23a73d699b5e7f199f0a3afce5b9dee7a6Martin Hibdon
77a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    public ContactResolver(final ContentResolver resolver, final BitmapCache cache) {
78a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        mResolver = resolver;
79a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        mCache = cache;
80a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        mBatch = new LinkedHashSet<ContactRequestHolder>();
81a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    }
82a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
83a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    @Override
84a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    public void run() {
85a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        // Start to process a new batch.
86a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        if (mBatch.isEmpty()) {
87a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            return;
88a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        }
89a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
90a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        if (mTask != null && mTask.getStatus() == Status.RUNNING) {
91a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            LogUtils.d(TAG, "ContactResolver << batch skip");
92a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            return;
93a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        }
94a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
95a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        Trace.beginSection("ContactResolver run");
96a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        LogUtils.d(TAG, "ContactResolver >> batch start");
97a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
98a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        // Make a copy of the batch.
99a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        LinkedHashSet<ContactRequestHolder> batch = new LinkedHashSet<ContactRequestHolder>(mBatch);
100a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
101a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        if (mTask != null) {
102a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            mTask.cancel(true);
103a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        }
104a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
1058913ca6aebe6017efe8060b61b8ad38eda95aa8bAndrew Sapperstein        mTask = getContactResolverTask(batch);
106a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        mTask.executeOnExecutor(EXECUTOR);
107a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        Trace.endSection();
108a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    }
109a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
1108913ca6aebe6017efe8060b61b8ad38eda95aa8bAndrew Sapperstein    protected ContactResolverTask getContactResolverTask(
1118913ca6aebe6017efe8060b61b8ad38eda95aa8bAndrew Sapperstein            LinkedHashSet<ContactRequestHolder> batch) {
1128913ca6aebe6017efe8060b61b8ad38eda95aa8bAndrew Sapperstein        return new ContactResolverTask(batch, mResolver, mCache, this);
1138913ca6aebe6017efe8060b61b8ad38eda95aa8bAndrew Sapperstein    }
1148913ca6aebe6017efe8060b61b8ad38eda95aa8bAndrew Sapperstein
1158913ca6aebe6017efe8060b61b8ad38eda95aa8bAndrew Sapperstein    public BitmapCache getCache() {
1168913ca6aebe6017efe8060b61b8ad38eda95aa8bAndrew Sapperstein        return mCache;
1178913ca6aebe6017efe8060b61b8ad38eda95aa8bAndrew Sapperstein    }
1188913ca6aebe6017efe8060b61b8ad38eda95aa8bAndrew Sapperstein
119c7849b23a73d699b5e7f199f0a3afce5b9dee7a6Martin Hibdon    public void add(final ContactRequest request, final ContactDrawableInterface drawable) {
120a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        mBatch.add(new ContactRequestHolder(request, drawable));
121a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        notifyBatchReady();
122a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    }
123a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
124c7849b23a73d699b5e7f199f0a3afce5b9dee7a6Martin Hibdon    public void remove(final ContactRequest request, final ContactDrawableInterface drawable) {
125a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        mBatch.remove(new ContactRequestHolder(request, drawable));
126a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    }
127a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
128a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    /**
129a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * A layout pass traverses the whole tree during a single iteration of the event loop. That
130a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * means that every ContactDrawable on the screen will add its ContactRequest to the batch in
131a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * a single iteration of the event loop.
132a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     *
133a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * <p/>
134a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * We take advantage of this by posting a Runnable (happens to be this object) at the end of
135a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * the event queue. Every time something is added to the batch as part of the same layout pass,
136a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * the Runnable is moved to the back of the queue. When the next layout pass occurs,
137a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * it is placed in the event loop behind this Runnable. That allows us to process the batch
138a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * that was added previously.
139a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     */
140a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    private void notifyBatchReady() {
141a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        LogUtils.d(TAG, "ContactResolver  > batch   %d", mBatch.size());
142a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        mHandler.removeCallbacks(this);
143a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        mHandler.post(this);
144a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    }
145a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
146a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    /**
147a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * This is not a very traditional AsyncTask, in the sense that we do not care about what gets
148a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * returned in doInBackground(). Instead, we signal traditional "return values" through
149a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * publishProgress().
150a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     *
151a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * <p/>
152a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * The reason we do this is because this task is responsible for decoding an entire batch of
153a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * ContactRequests. But, we do not want to have to wait to decode all of them before updating
154a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * any views. So we must do all the work in doInBackground(),
155a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * but upon finishing each individual task, we need to jump out to the UI thread and update
156a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * that view.
157a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     */
1588913ca6aebe6017efe8060b61b8ad38eda95aa8bAndrew Sapperstein    public static class ContactResolverTask extends AsyncTask<Void, Result, Void> {
159a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
160a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        private final Set<ContactRequestHolder> mContactRequests;
161a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        private final ContentResolver mResolver;
162a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        private final BitmapCache mCache;
163a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        private final ContactResolver mCallback;
164a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
165a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        public ContactResolverTask(final Set<ContactRequestHolder> contactRequests,
166a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                final ContentResolver resolver, final BitmapCache cache,
167a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                final ContactResolver callback) {
168a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            mContactRequests = contactRequests;
169a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            mResolver = resolver;
170a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            mCache = cache;
171a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            mCallback = callback;
172a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        }
173a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
174a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        @Override
175a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        protected Void doInBackground(final Void... params) {
176a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            Trace.beginSection("set up");
177a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            final Set<String> emails = new HashSet<String>(mContactRequests.size());
178a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            for (ContactRequestHolder request : mContactRequests) {
179a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                final String email = request.getEmail();
180a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                emails.add(email);
181a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            }
182a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            Trace.endSection();
183a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
184a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            Trace.beginSection("load contact photo bytes");
185a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            // Query the contacts provider for the current batch of emails.
1868913ca6aebe6017efe8060b61b8ad38eda95aa8bAndrew Sapperstein            final ImmutableMap<String, ContactInfo> contactInfos = loadContactPhotos(emails);
187a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            Trace.endSection();
188a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
189a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            for (ContactRequestHolder request : mContactRequests) {
190a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                Trace.beginSection("decode");
191a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                final String email = request.getEmail();
192a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                if (contactInfos == null) {
193a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    // Query failed.
194a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    LogUtils.d(TAG, "ContactResolver -- failed  %s", email);
195a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    publishProgress(new Result(request, null));
196a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    Trace.endSection();
197a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    continue;
198a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                }
199a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
200a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                final ContactInfo contactInfo = contactInfos.get(email);
201a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                if (contactInfo == null) {
202a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    // Request skipped. Try again next batch.
203a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    LogUtils.d(TAG, "ContactResolver  = skipped %s", email);
204a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    Trace.endSection();
205a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    continue;
206a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                }
207a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
208a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                // Query attempted.
209a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                final byte[] photo = contactInfo.photoBytes;
210a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                if (photo == null) {
211a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    // No photo bytes found.
212a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    LogUtils.d(TAG, "ContactResolver -- failed  %s", email);
213a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    publishProgress(new Result(request, null));
214a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    Trace.endSection();
215a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    continue;
216a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                }
217a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
218a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                // Query succeeded. Photo bytes found.
219a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                request.contactRequest.bytes = photo;
220a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
221a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                // Start decode.
222a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                LogUtils.d(TAG, "ContactResolver ++ found   %s", email);
223a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                // Synchronously decode the photo bytes. We are already in a background
224a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                // thread, and we want decodes to finish in order. The decodes are blazing
225a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                // fast so we don't need to kick off multiple threads.
226c12da4a53eee911765975f0d8afe62f749806b60Jin Cao                final int width = HALF_MAXIMUM_PHOTO_SIZE >= request.destination.getDecodeWidth()
227c12da4a53eee911765975f0d8afe62f749806b60Jin Cao                        ? HALF_MAXIMUM_PHOTO_SIZE : MAXIMUM_PHOTO_SIZE;
228c12da4a53eee911765975f0d8afe62f749806b60Jin Cao                final int height = HALF_MAXIMUM_PHOTO_SIZE >= request.destination.getDecodeHeight()
229c12da4a53eee911765975f0d8afe62f749806b60Jin Cao                        ? HALF_MAXIMUM_PHOTO_SIZE : MAXIMUM_PHOTO_SIZE;
2302b806edc62eb8e83c77edc471fda4652281a15c4James Lemieux                final DecodeTask.DecodeOptions opts = new DecodeTask.DecodeOptions(
231c12da4a53eee911765975f0d8afe62f749806b60Jin Cao                        width, height, 1 / 2f, DecodeTask.DecodeOptions.STRATEGY_ROUND_NEAREST);
2322b806edc62eb8e83c77edc471fda4652281a15c4James Lemieux                final ReusableBitmap result = new DecodeTask(request.contactRequest, opts, null,
2332b806edc62eb8e83c77edc471fda4652281a15c4James Lemieux                        null, mCache).decode();
234a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                request.contactRequest.bytes = null;
235a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
236a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                // Decode success.
237a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                publishProgress(new Result(request, result));
238a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                Trace.endSection();
239a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            }
240a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
241a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            return null;
242a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        }
243a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
2448913ca6aebe6017efe8060b61b8ad38eda95aa8bAndrew Sapperstein        protected ImmutableMap<String, ContactInfo> loadContactPhotos(Set<String> emails) {
24524ab60e7864f45b10bb74105bb8a1cf1a908dbeaRégis Décamps            if (mResolver == null) {
24624ab60e7864f45b10bb74105bb8a1cf1a908dbeaRégis Décamps                return null;
24724ab60e7864f45b10bb74105bb8a1cf1a908dbeaRégis Décamps            }
2488913ca6aebe6017efe8060b61b8ad38eda95aa8bAndrew Sapperstein            return SenderInfoLoader.loadContactPhotos(mResolver, emails, false /* decodeBitmaps */);
2498913ca6aebe6017efe8060b61b8ad38eda95aa8bAndrew Sapperstein        }
2508913ca6aebe6017efe8060b61b8ad38eda95aa8bAndrew Sapperstein
251a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        /**
252a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei         * We use progress updates to jump to the UI thread so we can decode the batch
253a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei         * incrementally.
254a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei         */
255a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        @Override
256a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        protected void onProgressUpdate(final Result... values) {
257a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            final ContactRequestHolder request = values[0].request;
258a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            final ReusableBitmap bitmap = values[0].bitmap;
259a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
260a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            // DecodeTask does not add null results to the cache.
26124ab60e7864f45b10bb74105bb8a1cf1a908dbeaRégis Décamps            if (bitmap == null && mCache != null) {
262a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                // Cache null result.
263a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                mCache.put(request.contactRequest, null);
264a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            }
265a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
266a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            request.destination.onDecodeComplete(request.contactRequest, bitmap);
267a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        }
268a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
269a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        @Override
270a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        protected void onPostExecute(final Void aVoid) {
271a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            // Batch completed. Start next batch.
272a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            mCallback.notifyBatchReady();
273a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        }
274a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    }
275a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
276a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    /**
277a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * Wrapper for the ContactRequest and its decoded bitmap. This class is used to pass results
278a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * to onProgressUpdate().
279a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     */
280a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    private static class Result {
281a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        public final ContactRequestHolder request;
282a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        public final ReusableBitmap bitmap;
283a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
284a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        private Result(final ContactRequestHolder request, final ReusableBitmap bitmap) {
285a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            this.request = request;
286a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            this.bitmap = bitmap;
287a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        }
288a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    }
289a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei}
290