ContactResolver.java revision 24ab60e7864f45b10bb74105bb8a1cf1a908dbea
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
538913ca6aebe6017efe8060b61b8ad38eda95aa8bAndrew Sapperstein    protected final ContentResolver mResolver;
54a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    private final BitmapCache mCache;
55a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    /** Insertion ordered set allows us to work from the top down. */
56a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    private final LinkedHashSet<ContactRequestHolder> mBatch;
57a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
58a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    private final Handler mHandler = new Handler();
59a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    private ContactResolverTask mTask;
60a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
61a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
62a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    /** Size 1 pool mostly to make systrace output traces on one line. */
63a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    private static final Executor SMALL_POOL_EXECUTOR = new ThreadPoolExecutor(1, 1,
64a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            1, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
65a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    private static final Executor EXECUTOR = SMALL_POOL_EXECUTOR;
66a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
67c7849b23a73d699b5e7f199f0a3afce5b9dee7a6Martin Hibdon    public interface ContactDrawableInterface {
682b806edc62eb8e83c77edc471fda4652281a15c4James Lemieux        public void onDecodeComplete(final RequestKey key, final ReusableBitmap result);
692b806edc62eb8e83c77edc471fda4652281a15c4James Lemieux        public int getDecodeWidth();
702b806edc62eb8e83c77edc471fda4652281a15c4James Lemieux        public int getDecodeHeight();
71c7849b23a73d699b5e7f199f0a3afce5b9dee7a6Martin Hibdon    }
72c7849b23a73d699b5e7f199f0a3afce5b9dee7a6Martin Hibdon
73a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    public ContactResolver(final ContentResolver resolver, final BitmapCache cache) {
74a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        mResolver = resolver;
75a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        mCache = cache;
76a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        mBatch = new LinkedHashSet<ContactRequestHolder>();
77a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    }
78a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
79a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    @Override
80a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    public void run() {
81a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        // Start to process a new batch.
82a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        if (mBatch.isEmpty()) {
83a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            return;
84a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        }
85a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
86a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        if (mTask != null && mTask.getStatus() == Status.RUNNING) {
87a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            LogUtils.d(TAG, "ContactResolver << batch skip");
88a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            return;
89a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        }
90a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
91a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        Trace.beginSection("ContactResolver run");
92a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        LogUtils.d(TAG, "ContactResolver >> batch start");
93a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
94a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        // Make a copy of the batch.
95a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        LinkedHashSet<ContactRequestHolder> batch = new LinkedHashSet<ContactRequestHolder>(mBatch);
96a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
97a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        if (mTask != null) {
98a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            mTask.cancel(true);
99a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        }
100a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
1018913ca6aebe6017efe8060b61b8ad38eda95aa8bAndrew Sapperstein        mTask = getContactResolverTask(batch);
102a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        mTask.executeOnExecutor(EXECUTOR);
103a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        Trace.endSection();
104a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    }
105a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
1068913ca6aebe6017efe8060b61b8ad38eda95aa8bAndrew Sapperstein    protected ContactResolverTask getContactResolverTask(
1078913ca6aebe6017efe8060b61b8ad38eda95aa8bAndrew Sapperstein            LinkedHashSet<ContactRequestHolder> batch) {
1088913ca6aebe6017efe8060b61b8ad38eda95aa8bAndrew Sapperstein        return new ContactResolverTask(batch, mResolver, mCache, this);
1098913ca6aebe6017efe8060b61b8ad38eda95aa8bAndrew Sapperstein    }
1108913ca6aebe6017efe8060b61b8ad38eda95aa8bAndrew Sapperstein
1118913ca6aebe6017efe8060b61b8ad38eda95aa8bAndrew Sapperstein    public BitmapCache getCache() {
1128913ca6aebe6017efe8060b61b8ad38eda95aa8bAndrew Sapperstein        return mCache;
1138913ca6aebe6017efe8060b61b8ad38eda95aa8bAndrew Sapperstein    }
1148913ca6aebe6017efe8060b61b8ad38eda95aa8bAndrew Sapperstein
115c7849b23a73d699b5e7f199f0a3afce5b9dee7a6Martin Hibdon    public void add(final ContactRequest request, final ContactDrawableInterface drawable) {
116a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        mBatch.add(new ContactRequestHolder(request, drawable));
117a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        notifyBatchReady();
118a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    }
119a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
120c7849b23a73d699b5e7f199f0a3afce5b9dee7a6Martin Hibdon    public void remove(final ContactRequest request, final ContactDrawableInterface drawable) {
121a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        mBatch.remove(new ContactRequestHolder(request, drawable));
122a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    }
123a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
124a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    /**
125a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * A layout pass traverses the whole tree during a single iteration of the event loop. That
126a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * means that every ContactDrawable on the screen will add its ContactRequest to the batch in
127a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * a single iteration of the event loop.
128a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     *
129a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * <p/>
130a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * We take advantage of this by posting a Runnable (happens to be this object) at the end of
131a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * the event queue. Every time something is added to the batch as part of the same layout pass,
132a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * the Runnable is moved to the back of the queue. When the next layout pass occurs,
133a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * it is placed in the event loop behind this Runnable. That allows us to process the batch
134a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * that was added previously.
135a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     */
136a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    private void notifyBatchReady() {
137a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        LogUtils.d(TAG, "ContactResolver  > batch   %d", mBatch.size());
138a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        mHandler.removeCallbacks(this);
139a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        mHandler.post(this);
140a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    }
141a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
142a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    /**
143a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * This is not a very traditional AsyncTask, in the sense that we do not care about what gets
144a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * returned in doInBackground(). Instead, we signal traditional "return values" through
145a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * publishProgress().
146a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     *
147a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * <p/>
148a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * The reason we do this is because this task is responsible for decoding an entire batch of
149a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * ContactRequests. But, we do not want to have to wait to decode all of them before updating
150a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * any views. So we must do all the work in doInBackground(),
151a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * but upon finishing each individual task, we need to jump out to the UI thread and update
152a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * that view.
153a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     */
1548913ca6aebe6017efe8060b61b8ad38eda95aa8bAndrew Sapperstein    public static class ContactResolverTask extends AsyncTask<Void, Result, Void> {
155a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
156a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        private final Set<ContactRequestHolder> mContactRequests;
157a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        private final ContentResolver mResolver;
158a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        private final BitmapCache mCache;
159a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        private final ContactResolver mCallback;
160a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
161a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        public ContactResolverTask(final Set<ContactRequestHolder> contactRequests,
162a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                final ContentResolver resolver, final BitmapCache cache,
163a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                final ContactResolver callback) {
164a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            mContactRequests = contactRequests;
165a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            mResolver = resolver;
166a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            mCache = cache;
167a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            mCallback = callback;
168a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        }
169a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
170a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        @Override
171a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        protected Void doInBackground(final Void... params) {
172a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            Trace.beginSection("set up");
173a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            final Set<String> emails = new HashSet<String>(mContactRequests.size());
174a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            for (ContactRequestHolder request : mContactRequests) {
175a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                final String email = request.getEmail();
176a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                emails.add(email);
177a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            }
178a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            Trace.endSection();
179a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
180a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            Trace.beginSection("load contact photo bytes");
181a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            // Query the contacts provider for the current batch of emails.
1828913ca6aebe6017efe8060b61b8ad38eda95aa8bAndrew Sapperstein            final ImmutableMap<String, ContactInfo> contactInfos = loadContactPhotos(emails);
183a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            Trace.endSection();
184a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
185a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            for (ContactRequestHolder request : mContactRequests) {
186a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                Trace.beginSection("decode");
187a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                final String email = request.getEmail();
188a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                if (contactInfos == null) {
189a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    // Query failed.
190a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    LogUtils.d(TAG, "ContactResolver -- failed  %s", email);
191a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    publishProgress(new Result(request, null));
192a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    Trace.endSection();
193a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    continue;
194a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                }
195a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
196a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                final ContactInfo contactInfo = contactInfos.get(email);
197a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                if (contactInfo == null) {
198a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    // Request skipped. Try again next batch.
199a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    LogUtils.d(TAG, "ContactResolver  = skipped %s", email);
200a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    Trace.endSection();
201a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    continue;
202a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                }
203a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
204a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                // Query attempted.
205a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                final byte[] photo = contactInfo.photoBytes;
206a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                if (photo == null) {
207a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    // No photo bytes found.
208a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    LogUtils.d(TAG, "ContactResolver -- failed  %s", email);
209a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    publishProgress(new Result(request, null));
210a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    Trace.endSection();
211a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    continue;
212a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                }
213a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
214a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                // Query succeeded. Photo bytes found.
215a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                request.contactRequest.bytes = photo;
216a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
217a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                // Start decode.
218a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                LogUtils.d(TAG, "ContactResolver ++ found   %s", email);
219a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                // Synchronously decode the photo bytes. We are already in a background
220a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                // thread, and we want decodes to finish in order. The decodes are blazing
221a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                // fast so we don't need to kick off multiple threads.
2222b806edc62eb8e83c77edc471fda4652281a15c4James Lemieux                final DecodeTask.DecodeOptions opts = new DecodeTask.DecodeOptions(
2232b806edc62eb8e83c77edc471fda4652281a15c4James Lemieux                        request.destination.getDecodeWidth(),
2242b806edc62eb8e83c77edc471fda4652281a15c4James Lemieux                        request.destination.getDecodeHeight(), 1 / 2f,
2252b806edc62eb8e83c77edc471fda4652281a15c4James Lemieux                        DecodeTask.DecodeOptions.STRATEGY_ROUND_NEAREST);
2262b806edc62eb8e83c77edc471fda4652281a15c4James Lemieux                final ReusableBitmap result = new DecodeTask(request.contactRequest, opts, null,
2272b806edc62eb8e83c77edc471fda4652281a15c4James Lemieux                        null, mCache).decode();
228a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                request.contactRequest.bytes = null;
229a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
230a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                // Decode success.
231a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                publishProgress(new Result(request, result));
232a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                Trace.endSection();
233a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            }
234a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
235a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            return null;
236a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        }
237a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
2388913ca6aebe6017efe8060b61b8ad38eda95aa8bAndrew Sapperstein        protected ImmutableMap<String, ContactInfo> loadContactPhotos(Set<String> emails) {
23924ab60e7864f45b10bb74105bb8a1cf1a908dbeaRégis Décamps            if (mResolver == null) {
24024ab60e7864f45b10bb74105bb8a1cf1a908dbeaRégis Décamps                return null;
24124ab60e7864f45b10bb74105bb8a1cf1a908dbeaRégis Décamps            }
2428913ca6aebe6017efe8060b61b8ad38eda95aa8bAndrew Sapperstein            return SenderInfoLoader.loadContactPhotos(mResolver, emails, false /* decodeBitmaps */);
2438913ca6aebe6017efe8060b61b8ad38eda95aa8bAndrew Sapperstein        }
2448913ca6aebe6017efe8060b61b8ad38eda95aa8bAndrew Sapperstein
245a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        /**
246a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei         * We use progress updates to jump to the UI thread so we can decode the batch
247a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei         * incrementally.
248a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei         */
249a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        @Override
250a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        protected void onProgressUpdate(final Result... values) {
251a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            final ContactRequestHolder request = values[0].request;
252a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            final ReusableBitmap bitmap = values[0].bitmap;
253a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
254a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            // DecodeTask does not add null results to the cache.
25524ab60e7864f45b10bb74105bb8a1cf1a908dbeaRégis Décamps            if (bitmap == null && mCache != null) {
256a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                // Cache null result.
257a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                mCache.put(request.contactRequest, null);
258a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            }
259a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
260a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            request.destination.onDecodeComplete(request.contactRequest, bitmap);
261a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        }
262a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
263a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        @Override
264a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        protected void onPostExecute(final Void aVoid) {
265a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            // Batch completed. Start next batch.
266a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            mCallback.notifyBatchReady();
267a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        }
268a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    }
269a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
270a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    /**
271a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * Wrapper for the ContactRequest and its decoded bitmap. This class is used to pass results
272a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * to onProgressUpdate().
273a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     */
274a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    private static class Result {
275a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        public final ContactRequestHolder request;
276a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        public final ReusableBitmap bitmap;
277a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
278a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        private Result(final ContactRequestHolder request, final ReusableBitmap bitmap) {
279a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            this.request = request;
280a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            this.bitmap = bitmap;
281a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        }
282a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    }
283a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei}
284