ContactResolver.java revision a8301e2eb5a083a73f58331279c86e8ec7b11a48
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
25a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Weiimport com.android.bitmap.BitmapCache;
26a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Weiimport com.android.bitmap.DecodeTask;
27a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Weiimport com.android.bitmap.ReusableBitmap;
28a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Weiimport com.android.ex.photo.util.Trace;
29a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Weiimport com.android.mail.ContactInfo;
30a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Weiimport com.android.mail.SenderInfoLoader;
31a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Weiimport com.android.mail.bitmap.ContactRequest.ContactRequestHolder;
32a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Weiimport com.android.mail.utils.LogTag;
33a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Weiimport com.android.mail.utils.LogUtils;
34a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Weiimport com.google.common.collect.ImmutableMap;
35a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
36a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Weiimport java.util.HashSet;
37a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Weiimport java.util.LinkedHashSet;
38a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Weiimport java.util.Set;
39a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Weiimport java.util.concurrent.Executor;
40a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Weiimport java.util.concurrent.LinkedBlockingQueue;
41a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Weiimport java.util.concurrent.ThreadPoolExecutor;
42a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Weiimport java.util.concurrent.TimeUnit;
43a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
44a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei/**
45a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei * Batches up ContactRequests so we can efficiently query the contacts provider. Kicks off a
46a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei * ContactResolverTask to query for contact images in the background.
47a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei */
48a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Weipublic class ContactResolver implements Runnable {
49a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
50a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    private static final String TAG = LogTag.getLogTag();
51a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
52a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    private final ContentResolver mResolver;
53a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    private final BitmapCache mCache;
54a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    /** Insertion ordered set allows us to work from the top down. */
55a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    private final LinkedHashSet<ContactRequestHolder> mBatch;
56a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
57a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    private final Handler mHandler = new Handler();
58a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    private ContactResolverTask mTask;
59a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
60a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
61a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    /** Size 1 pool mostly to make systrace output traces on one line. */
62a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    private static final Executor SMALL_POOL_EXECUTOR = new ThreadPoolExecutor(1, 1,
63a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            1, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
64a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    private static final Executor EXECUTOR = SMALL_POOL_EXECUTOR;
65a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
66a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    public ContactResolver(final ContentResolver resolver, final BitmapCache cache) {
67a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        mResolver = resolver;
68a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        mCache = cache;
69a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        mBatch = new LinkedHashSet<ContactRequestHolder>();
70a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    }
71a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
72a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    @Override
73a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    public void run() {
74a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        // Start to process a new batch.
75a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        if (mBatch.isEmpty()) {
76a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            return;
77a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        }
78a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
79a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        if (mTask != null && mTask.getStatus() == Status.RUNNING) {
80a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            LogUtils.d(TAG, "ContactResolver << batch skip");
81a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            return;
82a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        }
83a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
84a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        Trace.beginSection("ContactResolver run");
85a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        LogUtils.d(TAG, "ContactResolver >> batch start");
86a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
87a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        // Make a copy of the batch.
88a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        LinkedHashSet<ContactRequestHolder> batch = new LinkedHashSet<ContactRequestHolder>(mBatch);
89a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
90a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        if (mTask != null) {
91a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            mTask.cancel(true);
92a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        }
93a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
94a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        mTask = new ContactResolverTask(batch, mResolver, mCache, this);
95a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        mTask.executeOnExecutor(EXECUTOR);
96a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        Trace.endSection();
97a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    }
98a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
99a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    public void add(final ContactRequest request, final ContactDrawable drawable) {
100a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        mBatch.add(new ContactRequestHolder(request, drawable));
101a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        notifyBatchReady();
102a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    }
103a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
104a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    public void remove(final ContactRequest request, final ContactDrawable drawable) {
105a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        mBatch.remove(new ContactRequestHolder(request, drawable));
106a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    }
107a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
108a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    /**
109a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * A layout pass traverses the whole tree during a single iteration of the event loop. That
110a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * means that every ContactDrawable on the screen will add its ContactRequest to the batch in
111a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * a single iteration of the event loop.
112a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     *
113a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * <p/>
114a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * We take advantage of this by posting a Runnable (happens to be this object) at the end of
115a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * the event queue. Every time something is added to the batch as part of the same layout pass,
116a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * the Runnable is moved to the back of the queue. When the next layout pass occurs,
117a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * it is placed in the event loop behind this Runnable. That allows us to process the batch
118a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * that was added previously.
119a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     */
120a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    private void notifyBatchReady() {
121a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        LogUtils.d(TAG, "ContactResolver  > batch   %d", mBatch.size());
122a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        mHandler.removeCallbacks(this);
123a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        mHandler.post(this);
124a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    }
125a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
126a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    /**
127a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * This is not a very traditional AsyncTask, in the sense that we do not care about what gets
128a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * returned in doInBackground(). Instead, we signal traditional "return values" through
129a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * publishProgress().
130a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     *
131a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * <p/>
132a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * The reason we do this is because this task is responsible for decoding an entire batch of
133a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * ContactRequests. But, we do not want to have to wait to decode all of them before updating
134a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * any views. So we must do all the work in doInBackground(),
135a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * but upon finishing each individual task, we need to jump out to the UI thread and update
136a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * that view.
137a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     */
138a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    private static class ContactResolverTask extends AsyncTask<Void, Result, Void> {
139a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
140a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        private final Set<ContactRequestHolder> mContactRequests;
141a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        private final ContentResolver mResolver;
142a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        private final BitmapCache mCache;
143a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        private final ContactResolver mCallback;
144a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
145a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        public ContactResolverTask(final Set<ContactRequestHolder> contactRequests,
146a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                final ContentResolver resolver, final BitmapCache cache,
147a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                final ContactResolver callback) {
148a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            mContactRequests = contactRequests;
149a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            mResolver = resolver;
150a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            mCache = cache;
151a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            mCallback = callback;
152a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        }
153a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
154a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        @Override
155a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        protected Void doInBackground(final Void... params) {
156a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            Trace.beginSection("set up");
157a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            final Set<String> emails = new HashSet<String>(mContactRequests.size());
158a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            for (ContactRequestHolder request : mContactRequests) {
159a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                final String email = request.getEmail();
160a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                emails.add(email);
161a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            }
162a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            Trace.endSection();
163a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
164a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            Trace.beginSection("load contact photo bytes");
165a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            // Query the contacts provider for the current batch of emails.
166a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            ImmutableMap<String, ContactInfo> contactInfos = SenderInfoLoader
167a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    .loadContactPhotos(mResolver, emails, false /* decodeBitmaps */);
168a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            Trace.endSection();
169a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
170a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            for (ContactRequestHolder request : mContactRequests) {
171a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                Trace.beginSection("decode");
172a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                final String email = request.getEmail();
173a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                if (contactInfos == null) {
174a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    // Query failed.
175a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    LogUtils.d(TAG, "ContactResolver -- failed  %s", email);
176a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    publishProgress(new Result(request, null));
177a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    Trace.endSection();
178a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    continue;
179a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                }
180a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
181a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                final ContactInfo contactInfo = contactInfos.get(email);
182a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                if (contactInfo == null) {
183a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    // Request skipped. Try again next batch.
184a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    LogUtils.d(TAG, "ContactResolver  = skipped %s", email);
185a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    Trace.endSection();
186a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    continue;
187a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                }
188a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
189a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                // Query attempted.
190a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                final byte[] photo = contactInfo.photoBytes;
191a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                if (photo == null) {
192a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    // No photo bytes found.
193a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    LogUtils.d(TAG, "ContactResolver -- failed  %s", email);
194a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    publishProgress(new Result(request, null));
195a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    Trace.endSection();
196a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                    continue;
197a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                }
198a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
199a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                // Query succeeded. Photo bytes found.
200a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                request.contactRequest.bytes = photo;
201a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
202a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                // Start decode.
203a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                LogUtils.d(TAG, "ContactResolver ++ found   %s", email);
204a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                final ReusableBitmap result;
205a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                final int width = mCache.getDecodeWidth();
206a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                final int height = mCache.getDecodeHeight();
207a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                // Synchronously decode the photo bytes. We are already in a background
208a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                // thread, and we want decodes to finish in order. The decodes are blazing
209a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                // fast so we don't need to kick off multiple threads.
210a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                result = new DecodeTask(request.contactRequest, width, height, width, height, null,
211a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                        mCache).decode();
212a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                request.contactRequest.bytes = null;
213a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
214a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                // Decode success.
215a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                publishProgress(new Result(request, result));
216a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                Trace.endSection();
217a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            }
218a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
219a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            return null;
220a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        }
221a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
222a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        /**
223a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei         * We use progress updates to jump to the UI thread so we can decode the batch
224a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei         * incrementally.
225a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei         */
226a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        @Override
227a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        protected void onProgressUpdate(final Result... values) {
228a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            final ContactRequestHolder request = values[0].request;
229a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            final ReusableBitmap bitmap = values[0].bitmap;
230a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
231a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            // DecodeTask does not add null results to the cache.
232a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            if (bitmap == null) {
233a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                // Cache null result.
234a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei                mCache.put(request.contactRequest, null);
235a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            }
236a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
237a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            request.destination.onDecodeComplete(request.contactRequest, bitmap);
238a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        }
239a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
240a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        @Override
241a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        protected void onPostExecute(final Void aVoid) {
242a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            // Batch completed. Start next batch.
243a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            mCallback.notifyBatchReady();
244a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        }
245a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    }
246a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
247a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    /**
248a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * Wrapper for the ContactRequest and its decoded bitmap. This class is used to pass results
249a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     * to onProgressUpdate().
250a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei     */
251a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    private static class Result {
252a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        public final ContactRequestHolder request;
253a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        public final ReusableBitmap bitmap;
254a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei
255a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        private Result(final ContactRequestHolder request, final ReusableBitmap bitmap) {
256a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            this.request = request;
257a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei            this.bitmap = bitmap;
258a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei        }
259a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei    }
260a8301e2eb5a083a73f58331279c86e8ec7b11a48Mark Wei}
261