blob: 1cba29b6c76e43ddb1699ff636308669ba059557 [file] [log] [blame]
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001/*
Daniel Lehmannef3f8f02010-07-26 18:55:25 -07002 * Copyright (C) 2010 The Android Open Source Project
Daniel Lehmann4cd94412010-04-08 16:44:36 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
Dmitri Plotnikov18ffaa22010-12-03 14:28:00 -080017package com.android.contacts;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070018
Makoto Onuki69b4a882011-07-22 10:05:10 -070019import com.android.contacts.model.AccountType;
20import com.android.contacts.model.AccountTypeManager;
Makoto Onuki6ad227f2011-08-15 13:46:59 -070021import com.android.contacts.model.AccountTypeWithDataSet;
Katherine Kuan6cd5b0a2011-09-16 11:46:01 -070022import com.android.contacts.util.ContactLoaderUtils;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070023import com.android.contacts.util.DataStatus;
Dave Santoro39156002011-07-19 01:18:14 -070024import com.android.contacts.util.StreamItemEntry;
25import com.android.contacts.util.StreamItemPhotoEntry;
Makoto Onuki69b4a882011-07-22 10:05:10 -070026import com.google.android.collect.Lists;
Flavio Lerda37a26842011-06-27 11:36:52 +010027import com.google.common.annotations.VisibleForTesting;
Makoto Onuki6ad227f2011-08-15 13:46:59 -070028import com.google.common.collect.Maps;
Makoto Onukiaba2b832011-08-12 15:44:53 -070029import com.google.common.collect.Sets;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070030
31import android.content.ContentResolver;
32import android.content.ContentUris;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -070033import android.content.ContentValues;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070034import android.content.Context;
35import android.content.Entity;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -070036import android.content.Entity.NamedContentValues;
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -070037import android.content.Intent;
Jeff Hamilton3c462912010-05-15 02:20:01 -050038import android.content.Loader;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -070039import android.content.pm.PackageManager;
40import android.content.pm.PackageManager.NameNotFoundException;
Dave Santoro0a2a5db2011-06-29 00:37:06 -070041import android.content.res.AssetFileDescriptor;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -070042import android.content.res.Resources;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070043import android.database.Cursor;
44import android.net.Uri;
45import android.os.AsyncTask;
Daniel Lehmann1316b132010-04-13 15:08:53 -070046import android.provider.ContactsContract;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -070047import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -080048import android.provider.ContactsContract.CommonDataKinds.Photo;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070049import android.provider.ContactsContract.Contacts;
50import android.provider.ContactsContract.Data;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -070051import android.provider.ContactsContract.Directory;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070052import android.provider.ContactsContract.DisplayNameSources;
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -070053import android.provider.ContactsContract.Groups;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070054import android.provider.ContactsContract.RawContacts;
Dave Santoro39156002011-07-19 01:18:14 -070055import android.provider.ContactsContract.StreamItemPhotos;
Makoto Onuki69b4a882011-07-22 10:05:10 -070056import android.provider.ContactsContract.StreamItems;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -070057import android.text.TextUtils;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070058import android.util.Log;
59
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -080060import java.io.ByteArrayOutputStream;
Dave Santoro0a2a5db2011-06-29 00:37:06 -070061import java.io.FileInputStream;
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -080062import java.io.IOException;
63import java.io.InputStream;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070064import java.util.ArrayList;
Dave Santoro39156002011-07-19 01:18:14 -070065import java.util.Collections;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070066import java.util.HashMap;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -070067import java.util.List;
Dave Santoro39156002011-07-19 01:18:14 -070068import java.util.Map;
Makoto Onukiaba2b832011-08-12 15:44:53 -070069import java.util.Set;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070070
71/**
72 * Loads a single Contact and all it constituent RawContacts.
73 */
Daniel Lehmanncdef2b62010-06-06 18:25:49 -070074public class ContactLoader extends Loader<ContactLoader.Result> {
Daniel Lehmann18f104f2010-05-07 15:41:11 -070075 private static final String TAG = "ContactLoader";
76
Daniel Lehmann685157e2011-08-29 21:07:01 -070077 private final Uri mRequestedUri;
Makoto Onuki2621c5b2011-10-03 12:56:16 -070078 private Uri mLookupUri;
Dmitri Plotnikove843f912010-09-16 15:21:48 -070079 private boolean mLoadGroupMetaData;
Dave Santoro39156002011-07-19 01:18:14 -070080 private boolean mLoadStreamItems;
Makoto Onuki69b4a882011-07-22 10:05:10 -070081 private final boolean mLoadInvitableAccountTypes;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070082 private Result mContact;
83 private ForceLoadContentObserver mObserver;
84 private boolean mDestroyed;
Makoto Onukiaba2b832011-08-12 15:44:53 -070085 private final Set<Long> mNotifiedRawContactIds = Sets.newHashSet();
Dmitri Plotnikove843f912010-09-16 15:21:48 -070086
Daniel Lehmanncdef2b62010-06-06 18:25:49 -070087 public interface Listener {
Daniel Lehmann4cd94412010-04-08 16:44:36 -070088 public void onContactLoaded(Result contact);
89 }
90
91 /**
92 * The result of a load operation. Contains all data necessary to display the contact.
93 */
94 public static final class Result {
Makoto Onuki2621c5b2011-10-03 12:56:16 -070095 private enum Status {
96 /** Contact is successfully loaded */
97 LOADED,
98 /** There was an error loading the contact */
99 ERROR,
100 /** Contact is not found */
101 NOT_FOUND,
102 }
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700103
Daniel Lehmann685157e2011-08-29 21:07:01 -0700104 private final Uri mRequestedUri;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700105 private final Uri mLookupUri;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700106 private final Uri mUri;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700107 private final long mDirectoryId;
108 private final String mLookupKey;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700109 private final long mId;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700110 private final long mNameRawContactId;
111 private final int mDisplayNameSource;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700112 private final long mPhotoId;
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700113 private final String mPhotoUri;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700114 private final String mDisplayName;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700115 private final String mAltDisplayName;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700116 private final String mPhoneticName;
117 private final boolean mStarred;
118 private final Integer mPresence;
119 private final ArrayList<Entity> mEntities;
Makoto Onuki870a87e2011-08-12 13:40:31 -0700120 private final ArrayList<StreamItemEntry> mStreamItems;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700121 private final HashMap<Long, DataStatus> mStatuses;
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700122 private final ArrayList<AccountType> mInvitableAccountTypes;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700123
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700124 private String mDirectoryDisplayName;
125 private String mDirectoryType;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700126 private String mDirectoryAccountType;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700127 private String mDirectoryAccountName;
128 private int mDirectoryExportSupport;
129
Dmitri Plotnikove843f912010-09-16 15:21:48 -0700130 private ArrayList<GroupMetaData> mGroups;
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700131
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800132 private boolean mLoadingPhoto;
133 private byte[] mPhotoBinaryData;
Makoto Onuki870a87e2011-08-12 13:40:31 -0700134 private final boolean mSendToVoicemail;
135 private final String mCustomRingtone;
136 private final boolean mIsUserProfile;
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800137
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700138 private final Status mStatus;
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700139 private final Exception mException;
140
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700141 /**
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700142 * Constructor for special results, namely "no contact found" and "error".
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700143 */
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700144 private Result(Uri requestedUri, Status status, Exception exception) {
145 if (status == Status.ERROR && exception == null) {
146 throw new IllegalArgumentException("ERROR result must have exception");
147 }
148 mStatus = status;
149 mException = exception;
150 mRequestedUri = requestedUri;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700151 mLookupUri = null;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700152 mUri = null;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700153 mDirectoryId = -1;
154 mLookupKey = null;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700155 mId = -1;
156 mEntities = null;
Dave Santoro39156002011-07-19 01:18:14 -0700157 mStreamItems = new ArrayList<StreamItemEntry>();
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700158 mStatuses = null;
159 mNameRawContactId = -1;
160 mDisplayNameSource = DisplayNameSources.UNDEFINED;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700161 mPhotoId = -1;
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700162 mPhotoUri = null;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700163 mDisplayName = null;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700164 mAltDisplayName = null;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700165 mPhoneticName = null;
166 mStarred = false;
167 mPresence = null;
Makoto Onuki69b4a882011-07-22 10:05:10 -0700168 mInvitableAccountTypes = null;
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700169 mSendToVoicemail = false;
170 mCustomRingtone = null;
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700171 mIsUserProfile = false;
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700172 }
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700173
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700174 private static Result forError(Uri requestedUri, Exception exception) {
175 return new Result(requestedUri, Status.ERROR, exception);
176 }
177
178 private static Result forNotFound(Uri requestedUri) {
179 return new Result(requestedUri, Status.NOT_FOUND, null);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700180 }
181
182 /**
183 * Constructor to call when contact was found
184 */
Daniel Lehmann685157e2011-08-29 21:07:01 -0700185 private Result(Uri requestedUri, Uri uri, Uri lookupUri, long directoryId, String lookupKey,
186 long id, long nameRawContactId, int displayNameSource, long photoId,
187 String photoUri, String displayName, String altDisplayName, String phoneticName,
188 boolean starred, Integer presence, boolean sendToVoicemail, String customRingtone,
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700189 boolean isUserProfile) {
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700190 mStatus = Status.LOADED;
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700191 mException = null;
Daniel Lehmann685157e2011-08-29 21:07:01 -0700192 mRequestedUri = requestedUri;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700193 mLookupUri = lookupUri;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700194 mUri = uri;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700195 mDirectoryId = directoryId;
196 mLookupKey = lookupKey;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700197 mId = id;
198 mEntities = new ArrayList<Entity>();
Dave Santoro39156002011-07-19 01:18:14 -0700199 mStreamItems = new ArrayList<StreamItemEntry>();
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700200 mStatuses = new HashMap<Long, DataStatus>();
201 mNameRawContactId = nameRawContactId;
202 mDisplayNameSource = displayNameSource;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700203 mPhotoId = photoId;
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700204 mPhotoUri = photoUri;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700205 mDisplayName = displayName;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700206 mAltDisplayName = altDisplayName;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700207 mPhoneticName = phoneticName;
208 mStarred = starred;
209 mPresence = presence;
Makoto Onuki69b4a882011-07-22 10:05:10 -0700210 mInvitableAccountTypes = Lists.newArrayList();
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700211 mSendToVoicemail = sendToVoicemail;
212 mCustomRingtone = customRingtone;
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700213 mIsUserProfile = isUserProfile;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700214 }
215
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800216 private Result(Result from) {
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700217 mStatus = from.mStatus;
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700218 mException = from.mException;
Daniel Lehmann685157e2011-08-29 21:07:01 -0700219 mRequestedUri = from.mRequestedUri;
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800220 mLookupUri = from.mLookupUri;
221 mUri = from.mUri;
222 mDirectoryId = from.mDirectoryId;
223 mLookupKey = from.mLookupKey;
224 mId = from.mId;
225 mNameRawContactId = from.mNameRawContactId;
226 mDisplayNameSource = from.mDisplayNameSource;
227 mPhotoId = from.mPhotoId;
228 mPhotoUri = from.mPhotoUri;
229 mDisplayName = from.mDisplayName;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700230 mAltDisplayName = from.mAltDisplayName;
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800231 mPhoneticName = from.mPhoneticName;
232 mStarred = from.mStarred;
233 mPresence = from.mPresence;
234 mEntities = from.mEntities;
Dave Santoro39156002011-07-19 01:18:14 -0700235 mStreamItems = from.mStreamItems;
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800236 mStatuses = from.mStatuses;
Makoto Onuki69b4a882011-07-22 10:05:10 -0700237 mInvitableAccountTypes = from.mInvitableAccountTypes;
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800238
239 mDirectoryDisplayName = from.mDirectoryDisplayName;
240 mDirectoryType = from.mDirectoryType;
241 mDirectoryAccountType = from.mDirectoryAccountType;
242 mDirectoryAccountName = from.mDirectoryAccountName;
243 mDirectoryExportSupport = from.mDirectoryExportSupport;
244
245 mGroups = from.mGroups;
246
247 mLoadingPhoto = from.mLoadingPhoto;
248 mPhotoBinaryData = from.mPhotoBinaryData;
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700249 mSendToVoicemail = from.mSendToVoicemail;
250 mCustomRingtone = from.mCustomRingtone;
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700251 mIsUserProfile = from.mIsUserProfile;
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800252 }
253
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700254 /**
255 * @param exportSupport See {@link Directory#EXPORT_SUPPORT}.
256 */
Daniel Lehmann1ad4d1b2010-10-18 19:20:41 -0700257 private void setDirectoryMetaData(String displayName, String directoryType,
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700258 String accountType, String accountName, int exportSupport) {
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700259 mDirectoryDisplayName = displayName;
260 mDirectoryType = directoryType;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700261 mDirectoryAccountType = accountType;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700262 mDirectoryAccountName = accountName;
263 mDirectoryExportSupport = exportSupport;
264 }
265
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800266 private void setLoadingPhoto(boolean flag) {
267 mLoadingPhoto = flag;
268 }
269
270 private void setPhotoBinaryData(byte[] photoBinaryData) {
271 mPhotoBinaryData = photoBinaryData;
272 }
273
Daniel Lehmann685157e2011-08-29 21:07:01 -0700274 /**
275 * Returns the URI for the contact that contains both the lookup key and the ID. This is
276 * the best URI to reference a contact.
277 * For directory contacts, this is the same a the URI as returned by {@link #getUri()}
278 */
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700279 public Uri getLookupUri() {
280 return mLookupUri;
281 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800282
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700283 public String getLookupKey() {
284 return mLookupKey;
285 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800286
Daniel Lehmann685157e2011-08-29 21:07:01 -0700287 /**
288 * Returns the contact Uri that was passed to the provider to make the query. This is
289 * the same as the requested Uri, unless the requested Uri doesn't specify a Contact:
290 * If it either references a Raw-Contact or a Person (a pre-Eclair style Uri), this Uri will
291 * always reference the full aggregate contact.
292 */
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700293 public Uri getUri() {
294 return mUri;
295 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800296
Daniel Lehmann685157e2011-08-29 21:07:01 -0700297 /**
298 * Returns the URI for which this {@link ContactLoader) was initially requested.
299 */
300 public Uri getRequestedUri() {
301 return mRequestedUri;
302 }
303
Dave Santoro6fa73842011-09-28 14:37:06 -0700304 /**
305 * Returns the contact ID.
306 */
307 public long getId() {
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700308 return mId;
309 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800310
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700311 /**
312 * @return true when an exception happened during loading, in which case
313 * {@link #getException} returns the actual exception object.
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700314 * Note {@link #isNotFound()} and {@link #isError()} are mutually exclusive; If
315 * {@link #isError()} is {@code true}, {@link #isNotFound()} is always {@code false},
316 * and vice versa.
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700317 */
318 public boolean isError() {
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700319 return mStatus == Status.ERROR;
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700320 }
321
322 public Exception getException() {
323 return mException;
324 }
325
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700326 /**
327 * @return true when the specified contact is not found.
328 * Note {@link #isNotFound()} and {@link #isError()} are mutually exclusive; If
329 * {@link #isError()} is {@code true}, {@link #isNotFound()} is always {@code false},
330 * and vice versa.
331 */
332 public boolean isNotFound() {
333 return mStatus == Status.NOT_FOUND;
334 }
335
336 /**
337 * @return true if the specified contact is successfully loaded.
338 * i.e. neither {@link #isError()} nor {@link #isNotFound()}.
339 */
340 public boolean isLoaded() {
341 return mStatus == Status.LOADED;
342 }
343
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700344 public long getNameRawContactId() {
345 return mNameRawContactId;
346 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800347
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700348 public int getDisplayNameSource() {
349 return mDisplayNameSource;
350 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800351
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700352 public long getPhotoId() {
353 return mPhotoId;
354 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800355
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700356 public String getPhotoUri() {
357 return mPhotoUri;
358 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800359
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700360 public String getDisplayName() {
361 return mDisplayName;
362 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800363
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700364 public String getAltDisplayName() {
365 return mAltDisplayName;
366 }
367
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700368 public String getPhoneticName() {
369 return mPhoneticName;
370 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800371
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700372 public boolean getStarred() {
373 return mStarred;
374 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800375
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700376 public Integer getPresence() {
377 return mPresence;
378 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800379
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700380 public ArrayList<AccountType> getInvitableAccountTypes() {
Makoto Onuki69b4a882011-07-22 10:05:10 -0700381 return mInvitableAccountTypes;
382 }
383
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700384 public ArrayList<Entity> getEntities() {
385 return mEntities;
386 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800387
Dave Santoro39156002011-07-19 01:18:14 -0700388 public ArrayList<StreamItemEntry> getStreamItems() {
389 return mStreamItems;
390 }
391
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700392 public HashMap<Long, DataStatus> getStatuses() {
393 return mStatuses;
394 }
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700395
396 public long getDirectoryId() {
397 return mDirectoryId;
398 }
399
400 public boolean isDirectoryEntry() {
Dmitri Plotnikov5f72c1f2010-09-01 21:21:04 -0700401 return mDirectoryId != -1 && mDirectoryId != Directory.DEFAULT
402 && mDirectoryId != Directory.LOCAL_INVISIBLE;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700403 }
404
405 public int getDirectoryExportSupport() {
406 return mDirectoryExportSupport;
407 }
408
409 public String getDirectoryDisplayName() {
410 return mDirectoryDisplayName;
411 }
412
413 public String getDirectoryType() {
414 return mDirectoryType;
415 }
416
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700417 public String getDirectoryAccountType() {
418 return mDirectoryAccountType;
419 }
420
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700421 public String getDirectoryAccountName() {
422 return mDirectoryAccountName;
423 }
424
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800425 public boolean isLoadingPhoto() {
426 return mLoadingPhoto;
427 }
428
429 public byte[] getPhotoBinaryData() {
430 return mPhotoBinaryData;
431 }
432
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700433 public ArrayList<ContentValues> getContentValues() {
434 if (mEntities.size() != 1) {
435 throw new IllegalStateException(
436 "Cannot extract content values from an aggregated contact");
437 }
438
439 Entity entity = mEntities.get(0);
440 ArrayList<ContentValues> result = new ArrayList<ContentValues>();
441 ArrayList<NamedContentValues> subValues = entity.getSubValues();
442 if (subValues != null) {
443 int size = subValues.size();
444 for (int i = 0; i < size; i++) {
445 NamedContentValues pair = subValues.get(i);
446 if (Data.CONTENT_URI.equals(pair.uri)) {
447 result.add(pair.values);
448 }
449 }
450 }
Dmitri Plotnikov40ec3a82010-11-10 11:25:33 -0800451
452 // If the photo was loaded using the URI, create an entry for the photo
453 // binary data.
454 if (mPhotoId == 0 && mPhotoBinaryData != null) {
455 ContentValues photo = new ContentValues();
456 photo.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
457 photo.put(Photo.PHOTO, mPhotoBinaryData);
458 result.add(photo);
459 }
460
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700461 return result;
462 }
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700463
Daniel Lehmann1ad4d1b2010-10-18 19:20:41 -0700464 private void addGroupMetaData(GroupMetaData group) {
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700465 if (mGroups == null) {
Dmitri Plotnikove843f912010-09-16 15:21:48 -0700466 mGroups = new ArrayList<GroupMetaData>();
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700467 }
468 mGroups.add(group);
469 }
470
Dmitri Plotnikove843f912010-09-16 15:21:48 -0700471 public List<GroupMetaData> getGroupMetaData() {
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700472 return mGroups;
473 }
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700474
475 public boolean isSendToVoicemail() {
476 return mSendToVoicemail;
477 }
478
479 public String getCustomRingtone() {
480 return mCustomRingtone;
481 }
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700482
483 public boolean isUserProfile() {
484 return mIsUserProfile;
485 }
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700486 }
487
Dave Santoro39156002011-07-19 01:18:14 -0700488 /**
489 * Projection used for the query that loads all data for the entire contact (except for
490 * social stream items).
491 */
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700492 private static class ContactQuery {
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700493 final static String[] COLUMNS = new String[] {
494 Contacts.NAME_RAW_CONTACT_ID,
495 Contacts.DISPLAY_NAME_SOURCE,
496 Contacts.LOOKUP_KEY,
497 Contacts.DISPLAY_NAME,
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700498 Contacts.DISPLAY_NAME_ALTERNATIVE,
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700499 Contacts.PHONETIC_NAME,
500 Contacts.PHOTO_ID,
501 Contacts.STARRED,
502 Contacts.CONTACT_PRESENCE,
503 Contacts.CONTACT_STATUS,
504 Contacts.CONTACT_STATUS_TIMESTAMP,
505 Contacts.CONTACT_STATUS_RES_PACKAGE,
506 Contacts.CONTACT_STATUS_LABEL,
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700507 Contacts.Entity.CONTACT_ID,
508 Contacts.Entity.RAW_CONTACT_ID,
509
510 RawContacts.ACCOUNT_NAME,
511 RawContacts.ACCOUNT_TYPE,
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700512 RawContacts.DATA_SET,
513 RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700514 RawContacts.DIRTY,
515 RawContacts.VERSION,
516 RawContacts.SOURCE_ID,
517 RawContacts.SYNC1,
518 RawContacts.SYNC2,
519 RawContacts.SYNC3,
520 RawContacts.SYNC4,
521 RawContacts.DELETED,
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700522 RawContacts.NAME_VERIFIED,
523
524 Contacts.Entity.DATA_ID,
525 Data.DATA1,
526 Data.DATA2,
527 Data.DATA3,
528 Data.DATA4,
529 Data.DATA5,
530 Data.DATA6,
531 Data.DATA7,
532 Data.DATA8,
533 Data.DATA9,
534 Data.DATA10,
535 Data.DATA11,
536 Data.DATA12,
537 Data.DATA13,
538 Data.DATA14,
539 Data.DATA15,
540 Data.SYNC1,
541 Data.SYNC2,
542 Data.SYNC3,
543 Data.SYNC4,
544 Data.DATA_VERSION,
545 Data.IS_PRIMARY,
546 Data.IS_SUPER_PRIMARY,
547 Data.MIMETYPE,
548 Data.RES_PACKAGE,
549
550 GroupMembership.GROUP_SOURCE_ID,
551
552 Data.PRESENCE,
Daniel Lehmann8fd7bb62010-08-13 20:50:31 -0700553 Data.CHAT_CAPABILITY,
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700554 Data.STATUS,
555 Data.STATUS_RES_PACKAGE,
556 Data.STATUS_ICON,
557 Data.STATUS_LABEL,
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700558 Data.STATUS_TIMESTAMP,
559
560 Contacts.PHOTO_URI,
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700561 Contacts.SEND_TO_VOICEMAIL,
562 Contacts.CUSTOM_RINGTONE,
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700563 Contacts.IS_USER_PROFILE,
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700564 };
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700565
566 public final static int NAME_RAW_CONTACT_ID = 0;
567 public final static int DISPLAY_NAME_SOURCE = 1;
568 public final static int LOOKUP_KEY = 2;
569 public final static int DISPLAY_NAME = 3;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700570 public final static int ALT_DISPLAY_NAME = 4;
571 public final static int PHONETIC_NAME = 5;
572 public final static int PHOTO_ID = 6;
573 public final static int STARRED = 7;
574 public final static int CONTACT_PRESENCE = 8;
575 public final static int CONTACT_STATUS = 9;
576 public final static int CONTACT_STATUS_TIMESTAMP = 10;
577 public final static int CONTACT_STATUS_RES_PACKAGE = 11;
578 public final static int CONTACT_STATUS_LABEL = 12;
579 public final static int CONTACT_ID = 13;
580 public final static int RAW_CONTACT_ID = 14;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700581
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700582 public final static int ACCOUNT_NAME = 15;
583 public final static int ACCOUNT_TYPE = 16;
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700584 public final static int DATA_SET = 17;
585 public final static int ACCOUNT_TYPE_AND_DATA_SET = 18;
586 public final static int DIRTY = 19;
587 public final static int VERSION = 20;
588 public final static int SOURCE_ID = 21;
589 public final static int SYNC1 = 22;
590 public final static int SYNC2 = 23;
591 public final static int SYNC3 = 24;
592 public final static int SYNC4 = 25;
593 public final static int DELETED = 26;
594 public final static int NAME_VERIFIED = 27;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700595
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700596 public final static int DATA_ID = 28;
597 public final static int DATA1 = 29;
598 public final static int DATA2 = 30;
599 public final static int DATA3 = 31;
600 public final static int DATA4 = 32;
601 public final static int DATA5 = 33;
602 public final static int DATA6 = 34;
603 public final static int DATA7 = 35;
604 public final static int DATA8 = 36;
605 public final static int DATA9 = 37;
606 public final static int DATA10 = 38;
607 public final static int DATA11 = 39;
608 public final static int DATA12 = 40;
609 public final static int DATA13 = 41;
610 public final static int DATA14 = 42;
611 public final static int DATA15 = 43;
612 public final static int DATA_SYNC1 = 44;
613 public final static int DATA_SYNC2 = 45;
614 public final static int DATA_SYNC3 = 46;
615 public final static int DATA_SYNC4 = 47;
616 public final static int DATA_VERSION = 48;
617 public final static int IS_PRIMARY = 49;
618 public final static int IS_SUPERPRIMARY = 50;
619 public final static int MIMETYPE = 51;
620 public final static int RES_PACKAGE = 52;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700621
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700622 public final static int GROUP_SOURCE_ID = 53;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700623
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700624 public final static int PRESENCE = 54;
625 public final static int CHAT_CAPABILITY = 55;
626 public final static int STATUS = 56;
627 public final static int STATUS_RES_PACKAGE = 57;
628 public final static int STATUS_ICON = 58;
629 public final static int STATUS_LABEL = 59;
630 public final static int STATUS_TIMESTAMP = 60;
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700631
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700632 public final static int PHOTO_URI = 61;
633 public final static int SEND_TO_VOICEMAIL = 62;
634 public final static int CUSTOM_RINGTONE = 63;
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700635 public final static int IS_USER_PROFILE = 64;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700636 }
Daniel Lehmann1316b132010-04-13 15:08:53 -0700637
Dave Santoro39156002011-07-19 01:18:14 -0700638 /**
639 * Projection used for the query that loads all data for the entire contact.
640 */
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700641 private static class DirectoryQuery {
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700642 final static String[] COLUMNS = new String[] {
643 Directory.DISPLAY_NAME,
644 Directory.PACKAGE_NAME,
645 Directory.TYPE_RESOURCE_ID,
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700646 Directory.ACCOUNT_TYPE,
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700647 Directory.ACCOUNT_NAME,
648 Directory.EXPORT_SUPPORT,
649 };
650
651 public final static int DISPLAY_NAME = 0;
652 public final static int PACKAGE_NAME = 1;
653 public final static int TYPE_RESOURCE_ID = 2;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700654 public final static int ACCOUNT_TYPE = 3;
655 public final static int ACCOUNT_NAME = 4;
656 public final static int EXPORT_SUPPORT = 5;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700657 }
658
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700659 private static class GroupQuery {
660 final static String[] COLUMNS = new String[] {
661 Groups.ACCOUNT_NAME,
662 Groups.ACCOUNT_TYPE,
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700663 Groups.DATA_SET,
664 Groups.ACCOUNT_TYPE_AND_DATA_SET,
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700665 Groups._ID,
666 Groups.TITLE,
667 Groups.AUTO_ADD,
668 Groups.FAVORITES,
669 };
670
671 public final static int ACCOUNT_NAME = 0;
672 public final static int ACCOUNT_TYPE = 1;
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700673 public final static int DATA_SET = 2;
674 public final static int ACCOUNT_TYPE_AND_DATA_SET = 3;
675 public final static int ID = 4;
676 public final static int TITLE = 5;
677 public final static int AUTO_ADD = 6;
678 public final static int FAVORITES = 7;
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700679 }
680
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700681 private final class LoadContactTask extends AsyncTask<Void, Void, Result> {
Daniel Lehmann1316b132010-04-13 15:08:53 -0700682
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700683 @Override
684 protected Result doInBackground(Void... args) {
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700685 try {
686 final ContentResolver resolver = getContext().getContentResolver();
Katherine Kuan6cd5b0a2011-09-16 11:46:01 -0700687 final Uri uriCurrentFormat = ContactLoaderUtils.ensureIsContactUri(
688 resolver, mLookupUri);
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700689 Result result = loadContactEntity(resolver, uriCurrentFormat);
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700690 if (!result.isNotFound()) {
Dmitri Plotnikov217245c2010-09-18 13:04:50 -0700691 if (result.isDirectoryEntry()) {
692 loadDirectoryMetaData(result);
693 } else if (mLoadGroupMetaData) {
694 loadGroupMetaData(result);
695 }
Dave Santoro39156002011-07-19 01:18:14 -0700696 if (mLoadStreamItems) {
697 loadStreamItems(result);
698 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800699 loadPhotoBinaryData(result);
Makoto Onuki870a87e2011-08-12 13:40:31 -0700700
701 // Note ME profile should never have "Add connection"
702 if (mLoadInvitableAccountTypes && !result.isUserProfile()) {
Makoto Onuki69b4a882011-07-22 10:05:10 -0700703 loadInvitableAccountTypes(result);
704 }
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700705 }
706 return result;
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700707 } catch (Exception e) {
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700708 Log.e(TAG, "Error loading the contact: " + mLookupUri, e);
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700709 return Result.forError(mRequestedUri, e);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700710 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700711 }
712
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700713 private Result loadContactEntity(ContentResolver resolver, Uri contactUri) {
714 Uri entityUri = Uri.withAppendedPath(contactUri, Contacts.Entity.CONTENT_DIRECTORY);
715 Cursor cursor = resolver.query(entityUri, ContactQuery.COLUMNS, null, null,
716 Contacts.Entity.RAW_CONTACT_ID);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700717 if (cursor == null) {
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700718 Log.e(TAG, "No cursor returned in loadContactEntity");
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700719 return Result.forNotFound(mRequestedUri);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700720 }
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700721
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700722 try {
723 if (!cursor.moveToFirst()) {
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700724 cursor.close();
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700725 return Result.forNotFound(mRequestedUri);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700726 }
727
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700728 long currentRawContactId = -1;
729 Entity entity = null;
730 Result result = loadContactHeaderData(cursor, contactUri);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700731 ArrayList<Entity> entities = result.getEntities();
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700732 HashMap<Long, DataStatus> statuses = result.getStatuses();
733 for (; !cursor.isAfterLast(); cursor.moveToNext()) {
734 long rawContactId = cursor.getLong(ContactQuery.RAW_CONTACT_ID);
735 if (rawContactId != currentRawContactId) {
736 currentRawContactId = rawContactId;
737 entity = new android.content.Entity(loadRawContact(cursor));
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700738 entities.add(entity);
739 }
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700740 if (!cursor.isNull(ContactQuery.DATA_ID)) {
741 ContentValues data = loadData(cursor);
742 entity.addSubValue(ContactsContract.Data.CONTENT_URI, data);
743
744 if (!cursor.isNull(ContactQuery.PRESENCE)
745 || !cursor.isNull(ContactQuery.STATUS)) {
746 final DataStatus status = new DataStatus(cursor);
747 final long dataId = cursor.getLong(ContactQuery.DATA_ID);
748 statuses.put(dataId, status);
749 }
750 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700751 }
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700752
753 return result;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700754 } finally {
755 cursor.close();
756 }
757 }
758
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700759 /**
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800760 * Looks for the photo data item in entities. If found, creates a new Bitmap instance. If
761 * not found, returns null
762 */
763 private void loadPhotoBinaryData(Result contactData) {
Dave Santoro0a2a5db2011-06-29 00:37:06 -0700764
765 // If we have a photo URI, try loading that first.
766 String photoUri = contactData.getPhotoUri();
767 if (photoUri != null) {
768 try {
769 AssetFileDescriptor fd = getContext().getContentResolver()
770 .openAssetFileDescriptor(Uri.parse(photoUri), "r");
771 byte[] buffer = new byte[16 * 1024];
772 FileInputStream fis = fd.createInputStream();
773 ByteArrayOutputStream baos = new ByteArrayOutputStream();
774 try {
775 int size;
776 while ((size = fis.read(buffer)) != -1) {
777 baos.write(buffer, 0, size);
778 }
779 contactData.setPhotoBinaryData(baos.toByteArray());
780 } finally {
781 fis.close();
782 fd.close();
783 }
784 return;
785 } catch (IOException ioe) {
786 // Just fall back to the case below.
787 }
788 }
789
790 // If we couldn't load from a file, fall back to the data blob.
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800791 final long photoId = contactData.getPhotoId();
792 if (photoId <= 0) {
793 // No photo ID
794 return;
795 }
796
797 for (Entity entity : contactData.getEntities()) {
798 for (NamedContentValues subValue : entity.getSubValues()) {
799 final ContentValues entryValues = subValue.values;
800 final long dataId = entryValues.getAsLong(Data._ID);
801 if (dataId == photoId) {
802 final String mimeType = entryValues.getAsString(Data.MIMETYPE);
803 // Correct Data Id but incorrect MimeType? Don't load
804 if (!Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
805 return;
806 }
807 contactData.setPhotoBinaryData(entryValues.getAsByteArray(Photo.PHOTO));
808 break;
809 }
810 }
811 }
812 }
813
Makoto Onuki3e6991e2011-07-24 14:51:20 -0700814 /**
815 * Sets the "invitable" account types to {@link Result#mInvitableAccountTypes}.
Makoto Onuki3e6991e2011-07-24 14:51:20 -0700816 */
Makoto Onuki69b4a882011-07-22 10:05:10 -0700817 private void loadInvitableAccountTypes(Result contactData) {
Katherine Kuan08bcf712011-10-09 13:43:53 -0700818 Map<AccountTypeWithDataSet, AccountType> invitables =
819 AccountTypeManager.getInstance(getContext()).getUsableInvitableAccountTypes();
820 if (invitables.isEmpty()) {
Makoto Onuki69b4a882011-07-22 10:05:10 -0700821 return;
822 }
823
Katherine Kuan08bcf712011-10-09 13:43:53 -0700824 HashMap<AccountTypeWithDataSet, AccountType> result = Maps.newHashMap(invitables);
Makoto Onuki69b4a882011-07-22 10:05:10 -0700825
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -0700826 // Remove the ones that already have a raw contact in the current contact
Makoto Onuki69b4a882011-07-22 10:05:10 -0700827 for (Entity entity : contactData.getEntities()) {
Makoto Onuki6ad227f2011-08-15 13:46:59 -0700828 final ContentValues values = entity.getEntityValues();
829 final AccountTypeWithDataSet type = AccountTypeWithDataSet.get(
830 values.getAsString(RawContacts.ACCOUNT_TYPE),
831 values.getAsString(RawContacts.DATA_SET));
832 result.remove(type);
Makoto Onuki69b4a882011-07-22 10:05:10 -0700833 }
834
835 // Set to mInvitableAccountTypes
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700836 contactData.mInvitableAccountTypes.addAll(result.values());
Makoto Onuki69b4a882011-07-22 10:05:10 -0700837 }
838
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800839 /**
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700840 * Extracts Contact level columns from the cursor.
841 */
842 private Result loadContactHeaderData(final Cursor cursor, Uri contactUri) {
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700843 final String directoryParameter =
844 contactUri.getQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY);
845 final long directoryId = directoryParameter == null
846 ? Directory.DEFAULT
847 : Long.parseLong(directoryParameter);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700848 final long contactId = cursor.getLong(ContactQuery.CONTACT_ID);
849 final String lookupKey = cursor.getString(ContactQuery.LOOKUP_KEY);
850 final long nameRawContactId = cursor.getLong(ContactQuery.NAME_RAW_CONTACT_ID);
851 final int displayNameSource = cursor.getInt(ContactQuery.DISPLAY_NAME_SOURCE);
852 final String displayName = cursor.getString(ContactQuery.DISPLAY_NAME);
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700853 final String altDisplayName = cursor.getString(ContactQuery.ALT_DISPLAY_NAME);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700854 final String phoneticName = cursor.getString(ContactQuery.PHONETIC_NAME);
855 final long photoId = cursor.getLong(ContactQuery.PHOTO_ID);
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700856 final String photoUri = cursor.getString(ContactQuery.PHOTO_URI);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700857 final boolean starred = cursor.getInt(ContactQuery.STARRED) != 0;
858 final Integer presence = cursor.isNull(ContactQuery.CONTACT_PRESENCE)
859 ? null
860 : cursor.getInt(ContactQuery.CONTACT_PRESENCE);
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700861 final boolean sendToVoicemail = cursor.getInt(ContactQuery.SEND_TO_VOICEMAIL) == 1;
862 final String customRingtone = cursor.getString(ContactQuery.CUSTOM_RINGTONE);
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700863 final boolean isUserProfile = cursor.getInt(ContactQuery.IS_USER_PROFILE) == 1;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700864
Dmitri Plotnikov1536ea12010-10-29 11:51:05 -0700865 Uri lookupUri;
866 if (directoryId == Directory.DEFAULT || directoryId == Directory.LOCAL_INVISIBLE) {
867 lookupUri = ContentUris.withAppendedId(
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700868 Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey), contactId);
Dmitri Plotnikov1536ea12010-10-29 11:51:05 -0700869 } else {
870 lookupUri = contactUri;
871 }
872
Daniel Lehmann685157e2011-08-29 21:07:01 -0700873 return new Result(mRequestedUri, contactUri, lookupUri, directoryId, lookupKey,
874 contactId, nameRawContactId, displayNameSource, photoId, photoUri, displayName,
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700875 altDisplayName, phoneticName, starred, presence, sendToVoicemail,
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700876 customRingtone, isUserProfile);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700877 }
878
879 /**
880 * Extracts RawContact level columns from the cursor.
881 */
882 private ContentValues loadRawContact(Cursor cursor) {
883 ContentValues cv = new ContentValues();
884
885 cv.put(RawContacts._ID, cursor.getLong(ContactQuery.RAW_CONTACT_ID));
886
887 cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_NAME);
888 cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_TYPE);
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700889 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SET);
890 cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_TYPE_AND_DATA_SET);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700891 cursorColumnToContentValues(cursor, cv, ContactQuery.DIRTY);
892 cursorColumnToContentValues(cursor, cv, ContactQuery.VERSION);
893 cursorColumnToContentValues(cursor, cv, ContactQuery.SOURCE_ID);
894 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC1);
895 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC2);
896 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC3);
897 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC4);
898 cursorColumnToContentValues(cursor, cv, ContactQuery.DELETED);
899 cursorColumnToContentValues(cursor, cv, ContactQuery.CONTACT_ID);
900 cursorColumnToContentValues(cursor, cv, ContactQuery.STARRED);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700901 cursorColumnToContentValues(cursor, cv, ContactQuery.NAME_VERIFIED);
902
903 return cv;
904 }
905
906 /**
907 * Extracts Data level columns from the cursor.
908 */
909 private ContentValues loadData(Cursor cursor) {
910 ContentValues cv = new ContentValues();
911
912 cv.put(Data._ID, cursor.getLong(ContactQuery.DATA_ID));
913
914 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA1);
915 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA2);
916 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA3);
917 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA4);
918 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA5);
919 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA6);
920 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA7);
921 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA8);
922 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA9);
923 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA10);
924 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA11);
925 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA12);
926 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA13);
927 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA14);
928 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA15);
929 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC1);
930 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC2);
931 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC3);
932 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC4);
933 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_VERSION);
934 cursorColumnToContentValues(cursor, cv, ContactQuery.IS_PRIMARY);
935 cursorColumnToContentValues(cursor, cv, ContactQuery.IS_SUPERPRIMARY);
936 cursorColumnToContentValues(cursor, cv, ContactQuery.MIMETYPE);
937 cursorColumnToContentValues(cursor, cv, ContactQuery.RES_PACKAGE);
938 cursorColumnToContentValues(cursor, cv, ContactQuery.GROUP_SOURCE_ID);
Daniel Lehmann8fd7bb62010-08-13 20:50:31 -0700939 cursorColumnToContentValues(cursor, cv, ContactQuery.CHAT_CAPABILITY);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700940
941 return cv;
942 }
943
944 private void cursorColumnToContentValues(
945 Cursor cursor, ContentValues values, int index) {
946 switch (cursor.getType(index)) {
947 case Cursor.FIELD_TYPE_NULL:
948 // don't put anything in the content values
949 break;
950 case Cursor.FIELD_TYPE_INTEGER:
951 values.put(ContactQuery.COLUMNS[index], cursor.getLong(index));
952 break;
953 case Cursor.FIELD_TYPE_STRING:
954 values.put(ContactQuery.COLUMNS[index], cursor.getString(index));
955 break;
956 case Cursor.FIELD_TYPE_BLOB:
957 values.put(ContactQuery.COLUMNS[index], cursor.getBlob(index));
958 break;
959 default:
960 throw new IllegalStateException("Invalid or unhandled data type");
961 }
962 }
963
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700964 private void loadDirectoryMetaData(Result result) {
965 long directoryId = result.getDirectoryId();
966
967 Cursor cursor = getContext().getContentResolver().query(
968 ContentUris.withAppendedId(Directory.CONTENT_URI, directoryId),
969 DirectoryQuery.COLUMNS, null, null, null);
970 if (cursor == null) {
971 return;
972 }
973 try {
974 if (cursor.moveToFirst()) {
975 final String displayName = cursor.getString(DirectoryQuery.DISPLAY_NAME);
976 final String packageName = cursor.getString(DirectoryQuery.PACKAGE_NAME);
977 final int typeResourceId = cursor.getInt(DirectoryQuery.TYPE_RESOURCE_ID);
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700978 final String accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE);
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700979 final String accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME);
980 final int exportSupport = cursor.getInt(DirectoryQuery.EXPORT_SUPPORT);
981 String directoryType = null;
982 if (!TextUtils.isEmpty(packageName)) {
983 PackageManager pm = getContext().getPackageManager();
984 try {
985 Resources resources = pm.getResourcesForApplication(packageName);
986 directoryType = resources.getString(typeResourceId);
987 } catch (NameNotFoundException e) {
988 Log.w(TAG, "Contact directory resource not found: "
989 + packageName + "." + typeResourceId);
990 }
991 }
992
993 result.setDirectoryMetaData(
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700994 displayName, directoryType, accountType, accountName, exportSupport);
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700995 }
996 } finally {
997 cursor.close();
998 }
999 }
1000
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001001 /**
1002 * Loads groups meta-data for all groups associated with all constituent raw contacts'
1003 * accounts.
1004 */
1005 private void loadGroupMetaData(Result result) {
1006 StringBuilder selection = new StringBuilder();
1007 ArrayList<String> selectionArgs = new ArrayList<String>();
1008 for (Entity entity : result.mEntities) {
1009 ContentValues values = entity.getEntityValues();
1010 String accountName = values.getAsString(RawContacts.ACCOUNT_NAME);
1011 String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
Dave Santoro2b3f3c52011-07-26 17:35:42 -07001012 String dataSet = values.getAsString(RawContacts.DATA_SET);
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001013 if (accountName != null && accountType != null) {
1014 if (selection.length() != 0) {
1015 selection.append(" OR ");
1016 }
1017 selection.append(
Dave Santoro2b3f3c52011-07-26 17:35:42 -07001018 "(" + Groups.ACCOUNT_NAME + "=? AND " + Groups.ACCOUNT_TYPE + "=?");
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001019 selectionArgs.add(accountName);
1020 selectionArgs.add(accountType);
Dave Santoro2b3f3c52011-07-26 17:35:42 -07001021
1022 if (dataSet != null) {
1023 selection.append(" AND " + Groups.DATA_SET + "=?");
1024 selectionArgs.add(dataSet);
1025 } else {
1026 selection.append(" AND " + Groups.DATA_SET + " IS NULL");
1027 }
1028 selection.append(")");
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001029 }
1030 }
1031 Cursor cursor = getContext().getContentResolver().query(Groups.CONTENT_URI,
1032 GroupQuery.COLUMNS, selection.toString(), selectionArgs.toArray(new String[0]),
1033 null);
1034 try {
1035 while (cursor.moveToNext()) {
1036 final String accountName = cursor.getString(GroupQuery.ACCOUNT_NAME);
1037 final String accountType = cursor.getString(GroupQuery.ACCOUNT_TYPE);
Dave Santoro2b3f3c52011-07-26 17:35:42 -07001038 final String dataSet = cursor.getString(GroupQuery.DATA_SET);
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001039 final long groupId = cursor.getLong(GroupQuery.ID);
1040 final String title = cursor.getString(GroupQuery.TITLE);
1041 final boolean defaultGroup = cursor.isNull(GroupQuery.AUTO_ADD)
1042 ? false
1043 : cursor.getInt(GroupQuery.AUTO_ADD) != 0;
1044 final boolean favorites = cursor.isNull(GroupQuery.FAVORITES)
1045 ? false
1046 : cursor.getInt(GroupQuery.FAVORITES) != 0;
1047
Dmitri Plotnikove843f912010-09-16 15:21:48 -07001048 result.addGroupMetaData(new GroupMetaData(
Dave Santoro2b3f3c52011-07-26 17:35:42 -07001049 accountName, accountType, dataSet, groupId, title, defaultGroup,
1050 favorites));
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001051 }
1052 } finally {
1053 cursor.close();
1054 }
1055 }
1056
Dave Santoro39156002011-07-19 01:18:14 -07001057 /**
1058 * Loads all stream items and stream item photos belonging to this contact.
1059 */
1060 private void loadStreamItems(Result result) {
1061 Cursor cursor = getContext().getContentResolver().query(
1062 Contacts.CONTENT_LOOKUP_URI.buildUpon()
1063 .appendPath(result.getLookupKey())
1064 .appendPath(Contacts.StreamItems.CONTENT_DIRECTORY).build(),
1065 null, null, null, null);
1066 Map<Long, StreamItemEntry> streamItemsById = new HashMap<Long, StreamItemEntry>();
1067 ArrayList<StreamItemEntry> streamItems = new ArrayList<StreamItemEntry>();
1068 try {
1069 while (cursor.moveToNext()) {
1070 StreamItemEntry streamItem = new StreamItemEntry(cursor);
1071 streamItemsById.put(streamItem.getId(), streamItem);
1072 streamItems.add(streamItem);
1073 }
1074 } finally {
1075 cursor.close();
1076 }
1077
1078 // Now retrieve any photo records associated with the stream items.
Dave Santoro39156002011-07-19 01:18:14 -07001079 if (!streamItems.isEmpty()) {
Dave Santoroa4400d52011-09-02 16:14:53 -07001080 if (result.isUserProfile()) {
1081 // If the stream items we're loading are for the profile, we can't bulk-load the
1082 // stream items with a custom selection.
1083 for (StreamItemEntry entry : streamItems) {
1084 Cursor siCursor = getContext().getContentResolver().query(
1085 Uri.withAppendedPath(
1086 ContentUris.withAppendedId(
1087 StreamItems.CONTENT_URI, entry.getId()),
1088 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY),
1089 null, null, null, null);
1090 try {
1091 while (siCursor.moveToNext()) {
1092 entry.addPhoto(new StreamItemPhotoEntry(siCursor));
1093 }
1094 } finally {
1095 siCursor.close();
1096 }
Dave Santoro39156002011-07-19 01:18:14 -07001097 }
Dave Santoroa4400d52011-09-02 16:14:53 -07001098 } else {
1099 String[] streamItemIdArr = new String[streamItems.size()];
1100 StringBuilder streamItemPhotoSelection = new StringBuilder();
1101 streamItemPhotoSelection.append(StreamItemPhotos.STREAM_ITEM_ID + " IN (");
1102 for (int i = 0; i < streamItems.size(); i++) {
1103 if (i > 0) {
1104 streamItemPhotoSelection.append(",");
1105 }
1106 streamItemPhotoSelection.append("?");
1107 streamItemIdArr[i] = String.valueOf(streamItems.get(i).getId());
Dave Santoro39156002011-07-19 01:18:14 -07001108 }
Dave Santoroa4400d52011-09-02 16:14:53 -07001109 streamItemPhotoSelection.append(")");
1110 Cursor sipCursor = getContext().getContentResolver().query(
1111 StreamItems.CONTENT_PHOTO_URI,
1112 null, streamItemPhotoSelection.toString(), streamItemIdArr,
1113 StreamItemPhotos.STREAM_ITEM_ID);
1114 try {
1115 while (sipCursor.moveToNext()) {
1116 long streamItemId = sipCursor.getLong(
1117 sipCursor.getColumnIndex(StreamItemPhotos.STREAM_ITEM_ID));
1118 StreamItemEntry streamItem = streamItemsById.get(streamItemId);
1119 streamItem.addPhoto(new StreamItemPhotoEntry(sipCursor));
1120 }
1121 } finally {
1122 sipCursor.close();
1123 }
Dave Santoro39156002011-07-19 01:18:14 -07001124 }
1125 }
1126
1127 // Set the sorted stream items on the result.
1128 Collections.sort(streamItems);
1129 result.mStreamItems.addAll(streamItems);
1130 }
1131
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001132 @Override
1133 protected void onPostExecute(Result result) {
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001134 unregisterObserver();
1135
Daniel Lehmann1316b132010-04-13 15:08:53 -07001136 // The creator isn't interested in any further updates
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001137 if (mDestroyed || result == null) {
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001138 return;
1139 }
1140
1141 mContact = result;
Daniel Lehmann18f104f2010-05-07 15:41:11 -07001142
Makoto Onuki2621c5b2011-10-03 12:56:16 -07001143 if (result.isLoaded()) {
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001144 mLookupUri = result.getLookupUri();
1145
1146 if (!result.isDirectoryEntry()) {
1147 Log.i(TAG, "Registering content observer for " + mLookupUri);
1148 if (mObserver == null) {
1149 mObserver = new ForceLoadContentObserver();
1150 }
1151 getContext().getContentResolver().registerContentObserver(
1152 mLookupUri, true, mObserver);
Daniel Lehmann18f104f2010-05-07 15:41:11 -07001153 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -08001154
1155 if (mContact.getPhotoBinaryData() == null && mContact.getPhotoUri() != null) {
1156 mContact.setLoadingPhoto(true);
1157 new AsyncPhotoLoader().execute(mContact.getPhotoUri());
1158 }
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001159
1160 // inform the source of the data that this contact is being looked at
1161 postViewNotificationToSyncAdapter();
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001162 }
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001163
1164 deliverResult(mContact);
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001165 }
1166 }
1167
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001168 /**
1169 * Posts a message to the contributing sync adapters that have opted-in, notifying them
1170 * that the contact has just been loaded
1171 */
1172 private void postViewNotificationToSyncAdapter() {
1173 Context context = getContext();
1174 for (Entity entity : mContact.getEntities()) {
1175 final ContentValues entityValues = entity.getEntityValues();
Makoto Onukiaba2b832011-08-12 15:44:53 -07001176 final long rawContactId = entityValues.getAsLong(RawContacts.Entity._ID);
1177 if (mNotifiedRawContactIds.contains(rawContactId)) {
1178 continue; // Already notified for this raw contact.
1179 }
1180 mNotifiedRawContactIds.add(rawContactId);
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001181 final String type = entityValues.getAsString(RawContacts.ACCOUNT_TYPE);
1182 final String dataSet = entityValues.getAsString(RawContacts.DATA_SET);
Flavio Lerda59a887e2011-08-14 18:13:17 +01001183 final AccountType accountType = AccountTypeManager.getInstance(context).getAccountType(
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001184 type, dataSet);
1185 final String serviceName = accountType.getViewContactNotifyServiceClassName();
1186 final String resPackageName = accountType.resPackageName;
1187 if (!TextUtils.isEmpty(serviceName) && !TextUtils.isEmpty(resPackageName)) {
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001188 final Uri uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
1189 final Intent intent = new Intent();
1190 intent.setClassName(resPackageName, serviceName);
1191 intent.setAction(Intent.ACTION_VIEW);
1192 intent.setDataAndType(uri, RawContacts.CONTENT_ITEM_TYPE);
1193 try {
1194 context.startService(intent);
1195 } catch (Exception e) {
1196 Log.e(TAG, "Error sending message to source-app", e);
1197 }
1198 }
1199 }
1200 }
1201
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -08001202 private class AsyncPhotoLoader extends AsyncTask<String, Void, byte[]> {
1203
1204 private static final int BUFFER_SIZE = 1024*16;
1205
1206 @Override
1207 protected byte[] doInBackground(String... params) {
1208 Uri uri = Uri.parse(params[0]);
1209 byte[] data = null;
1210 try {
1211 InputStream is = getContext().getContentResolver().openInputStream(uri);
1212 if (is != null) {
1213 ByteArrayOutputStream baos = new ByteArrayOutputStream();
1214 try {
1215 byte[] mBuffer = new byte[BUFFER_SIZE];
1216
1217 int size;
1218 while ((size = is.read(mBuffer)) != -1) {
1219 baos.write(mBuffer, 0, size);
1220 }
1221 data = baos.toByteArray();
1222 } finally {
1223 is.close();
1224 }
1225 } else {
1226 Log.v(TAG, "Cannot load photo " + uri);
1227 }
1228 } catch (IOException e) {
1229 Log.e(TAG, "Cannot load photo " + uri, e);
1230 }
1231
1232 return data;
1233 }
1234
1235 @Override
1236 protected void onPostExecute(byte[] data) {
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001237 if (mContact != null) {
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -08001238 mContact = new Result(mContact);
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001239 mContact.setPhotoBinaryData(data);
1240 mContact.setLoadingPhoto(false);
1241 deliverResult(mContact);
1242 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -08001243 }
1244 }
1245
Daniel Lehmann3a120772010-06-21 16:21:35 -07001246 private void unregisterObserver() {
1247 if (mObserver != null) {
1248 getContext().getContentResolver().unregisterContentObserver(mObserver);
1249 mObserver = null;
1250 }
1251 }
1252
Daniel Lehmanncdef2b62010-06-06 18:25:49 -07001253 public ContactLoader(Context context, Uri lookupUri) {
Makoto Onuki69b4a882011-07-22 10:05:10 -07001254 this(context, lookupUri, false, false, false);
Dmitri Plotnikove843f912010-09-16 15:21:48 -07001255 }
1256
Dave Santoro39156002011-07-19 01:18:14 -07001257 public ContactLoader(Context context, Uri lookupUri, boolean loadGroupMetaData,
Makoto Onuki69b4a882011-07-22 10:05:10 -07001258 boolean loadStreamItems, boolean loadInvitableAccountTypes) {
Daniel Lehmann74a2dc52010-04-15 16:52:33 -07001259 super(context);
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001260 mLookupUri = lookupUri;
Daniel Lehmann685157e2011-08-29 21:07:01 -07001261 mRequestedUri = lookupUri;
Dmitri Plotnikove843f912010-09-16 15:21:48 -07001262 mLoadGroupMetaData = loadGroupMetaData;
Dave Santoro39156002011-07-19 01:18:14 -07001263 mLoadStreamItems = loadStreamItems;
Makoto Onuki69b4a882011-07-22 10:05:10 -07001264 mLoadInvitableAccountTypes = loadInvitableAccountTypes;
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001265 }
1266
Dmitri Plotnikov5a30d9a2010-11-23 14:59:50 -08001267 public Uri getLookupUri() {
1268 return mLookupUri;
1269 }
1270
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001271 @Override
Dianne Hackbornc04fc272010-12-20 23:13:10 -08001272 protected void onStartLoading() {
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001273 if (mContact != null) {
Daniel Lehmanncbcc4492010-04-12 18:03:54 -07001274 deliverResult(mContact);
Dmitri Plotnikov97e90c62011-01-03 11:58:13 -08001275 }
1276
1277 if (takeContentChanged() || mContact == null) {
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001278 forceLoad();
1279 }
1280 }
1281
1282 @Override
Dianne Hackbornc04fc272010-12-20 23:13:10 -08001283 protected void onForceLoad() {
Daniel Lehmann18f104f2010-05-07 15:41:11 -07001284 final LoadContactTask task = new LoadContactTask();
Makoto Onuki73c341f2011-08-29 09:31:44 -07001285 task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[])null);
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001286 }
1287
1288 @Override
Dianne Hackbornc04fc272010-12-20 23:13:10 -08001289 protected void onReset() {
Dianne Hackborn4ef95cc2010-12-16 00:44:33 -08001290 unregisterObserver();
1291 mContact = null;
1292 mDestroyed = true;
1293 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001294}