blob: c711b6c51ce16122b5b4caad7bff2ccf271e2c68 [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
Flavio Lerda37a26842011-06-27 11:36:52 +0100304 @VisibleForTesting
305 /*package*/ long getId() {
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700306 return mId;
307 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800308
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700309 /**
310 * @return true when an exception happened during loading, in which case
311 * {@link #getException} returns the actual exception object.
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700312 * Note {@link #isNotFound()} and {@link #isError()} are mutually exclusive; If
313 * {@link #isError()} is {@code true}, {@link #isNotFound()} is always {@code false},
314 * and vice versa.
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700315 */
316 public boolean isError() {
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700317 return mStatus == Status.ERROR;
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700318 }
319
320 public Exception getException() {
321 return mException;
322 }
323
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700324 /**
325 * @return true when the specified contact is not found.
326 * Note {@link #isNotFound()} and {@link #isError()} are mutually exclusive; If
327 * {@link #isError()} is {@code true}, {@link #isNotFound()} is always {@code false},
328 * and vice versa.
329 */
330 public boolean isNotFound() {
331 return mStatus == Status.NOT_FOUND;
332 }
333
334 /**
335 * @return true if the specified contact is successfully loaded.
336 * i.e. neither {@link #isError()} nor {@link #isNotFound()}.
337 */
338 public boolean isLoaded() {
339 return mStatus == Status.LOADED;
340 }
341
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700342 public long getNameRawContactId() {
343 return mNameRawContactId;
344 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800345
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700346 public int getDisplayNameSource() {
347 return mDisplayNameSource;
348 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800349
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700350 public long getPhotoId() {
351 return mPhotoId;
352 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800353
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700354 public String getPhotoUri() {
355 return mPhotoUri;
356 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800357
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700358 public String getDisplayName() {
359 return mDisplayName;
360 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800361
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700362 public String getAltDisplayName() {
363 return mAltDisplayName;
364 }
365
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700366 public String getPhoneticName() {
367 return mPhoneticName;
368 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800369
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700370 public boolean getStarred() {
371 return mStarred;
372 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800373
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700374 public Integer getPresence() {
375 return mPresence;
376 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800377
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700378 public ArrayList<AccountType> getInvitableAccountTypes() {
Makoto Onuki69b4a882011-07-22 10:05:10 -0700379 return mInvitableAccountTypes;
380 }
381
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700382 public ArrayList<Entity> getEntities() {
383 return mEntities;
384 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800385
Dave Santoro39156002011-07-19 01:18:14 -0700386 public ArrayList<StreamItemEntry> getStreamItems() {
387 return mStreamItems;
388 }
389
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700390 public HashMap<Long, DataStatus> getStatuses() {
391 return mStatuses;
392 }
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700393
394 public long getDirectoryId() {
395 return mDirectoryId;
396 }
397
398 public boolean isDirectoryEntry() {
Dmitri Plotnikov5f72c1f2010-09-01 21:21:04 -0700399 return mDirectoryId != -1 && mDirectoryId != Directory.DEFAULT
400 && mDirectoryId != Directory.LOCAL_INVISIBLE;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700401 }
402
403 public int getDirectoryExportSupport() {
404 return mDirectoryExportSupport;
405 }
406
407 public String getDirectoryDisplayName() {
408 return mDirectoryDisplayName;
409 }
410
411 public String getDirectoryType() {
412 return mDirectoryType;
413 }
414
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700415 public String getDirectoryAccountType() {
416 return mDirectoryAccountType;
417 }
418
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700419 public String getDirectoryAccountName() {
420 return mDirectoryAccountName;
421 }
422
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800423 public boolean isLoadingPhoto() {
424 return mLoadingPhoto;
425 }
426
427 public byte[] getPhotoBinaryData() {
428 return mPhotoBinaryData;
429 }
430
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700431 public ArrayList<ContentValues> getContentValues() {
432 if (mEntities.size() != 1) {
433 throw new IllegalStateException(
434 "Cannot extract content values from an aggregated contact");
435 }
436
437 Entity entity = mEntities.get(0);
438 ArrayList<ContentValues> result = new ArrayList<ContentValues>();
439 ArrayList<NamedContentValues> subValues = entity.getSubValues();
440 if (subValues != null) {
441 int size = subValues.size();
442 for (int i = 0; i < size; i++) {
443 NamedContentValues pair = subValues.get(i);
444 if (Data.CONTENT_URI.equals(pair.uri)) {
445 result.add(pair.values);
446 }
447 }
448 }
Dmitri Plotnikov40ec3a82010-11-10 11:25:33 -0800449
450 // If the photo was loaded using the URI, create an entry for the photo
451 // binary data.
452 if (mPhotoId == 0 && mPhotoBinaryData != null) {
453 ContentValues photo = new ContentValues();
454 photo.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
455 photo.put(Photo.PHOTO, mPhotoBinaryData);
456 result.add(photo);
457 }
458
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700459 return result;
460 }
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700461
Daniel Lehmann1ad4d1b2010-10-18 19:20:41 -0700462 private void addGroupMetaData(GroupMetaData group) {
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700463 if (mGroups == null) {
Dmitri Plotnikove843f912010-09-16 15:21:48 -0700464 mGroups = new ArrayList<GroupMetaData>();
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700465 }
466 mGroups.add(group);
467 }
468
Dmitri Plotnikove843f912010-09-16 15:21:48 -0700469 public List<GroupMetaData> getGroupMetaData() {
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700470 return mGroups;
471 }
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700472
473 public boolean isSendToVoicemail() {
474 return mSendToVoicemail;
475 }
476
477 public String getCustomRingtone() {
478 return mCustomRingtone;
479 }
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700480
481 public boolean isUserProfile() {
482 return mIsUserProfile;
483 }
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700484 }
485
Dave Santoro39156002011-07-19 01:18:14 -0700486 /**
487 * Projection used for the query that loads all data for the entire contact (except for
488 * social stream items).
489 */
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700490 private static class ContactQuery {
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700491 final static String[] COLUMNS = new String[] {
492 Contacts.NAME_RAW_CONTACT_ID,
493 Contacts.DISPLAY_NAME_SOURCE,
494 Contacts.LOOKUP_KEY,
495 Contacts.DISPLAY_NAME,
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700496 Contacts.DISPLAY_NAME_ALTERNATIVE,
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700497 Contacts.PHONETIC_NAME,
498 Contacts.PHOTO_ID,
499 Contacts.STARRED,
500 Contacts.CONTACT_PRESENCE,
501 Contacts.CONTACT_STATUS,
502 Contacts.CONTACT_STATUS_TIMESTAMP,
503 Contacts.CONTACT_STATUS_RES_PACKAGE,
504 Contacts.CONTACT_STATUS_LABEL,
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700505 Contacts.Entity.CONTACT_ID,
506 Contacts.Entity.RAW_CONTACT_ID,
507
508 RawContacts.ACCOUNT_NAME,
509 RawContacts.ACCOUNT_TYPE,
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700510 RawContacts.DATA_SET,
511 RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700512 RawContacts.DIRTY,
513 RawContacts.VERSION,
514 RawContacts.SOURCE_ID,
515 RawContacts.SYNC1,
516 RawContacts.SYNC2,
517 RawContacts.SYNC3,
518 RawContacts.SYNC4,
519 RawContacts.DELETED,
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700520 RawContacts.NAME_VERIFIED,
521
522 Contacts.Entity.DATA_ID,
523 Data.DATA1,
524 Data.DATA2,
525 Data.DATA3,
526 Data.DATA4,
527 Data.DATA5,
528 Data.DATA6,
529 Data.DATA7,
530 Data.DATA8,
531 Data.DATA9,
532 Data.DATA10,
533 Data.DATA11,
534 Data.DATA12,
535 Data.DATA13,
536 Data.DATA14,
537 Data.DATA15,
538 Data.SYNC1,
539 Data.SYNC2,
540 Data.SYNC3,
541 Data.SYNC4,
542 Data.DATA_VERSION,
543 Data.IS_PRIMARY,
544 Data.IS_SUPER_PRIMARY,
545 Data.MIMETYPE,
546 Data.RES_PACKAGE,
547
548 GroupMembership.GROUP_SOURCE_ID,
549
550 Data.PRESENCE,
Daniel Lehmann8fd7bb62010-08-13 20:50:31 -0700551 Data.CHAT_CAPABILITY,
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700552 Data.STATUS,
553 Data.STATUS_RES_PACKAGE,
554 Data.STATUS_ICON,
555 Data.STATUS_LABEL,
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700556 Data.STATUS_TIMESTAMP,
557
558 Contacts.PHOTO_URI,
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700559 Contacts.SEND_TO_VOICEMAIL,
560 Contacts.CUSTOM_RINGTONE,
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700561 Contacts.IS_USER_PROFILE,
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700562 };
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700563
564 public final static int NAME_RAW_CONTACT_ID = 0;
565 public final static int DISPLAY_NAME_SOURCE = 1;
566 public final static int LOOKUP_KEY = 2;
567 public final static int DISPLAY_NAME = 3;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700568 public final static int ALT_DISPLAY_NAME = 4;
569 public final static int PHONETIC_NAME = 5;
570 public final static int PHOTO_ID = 6;
571 public final static int STARRED = 7;
572 public final static int CONTACT_PRESENCE = 8;
573 public final static int CONTACT_STATUS = 9;
574 public final static int CONTACT_STATUS_TIMESTAMP = 10;
575 public final static int CONTACT_STATUS_RES_PACKAGE = 11;
576 public final static int CONTACT_STATUS_LABEL = 12;
577 public final static int CONTACT_ID = 13;
578 public final static int RAW_CONTACT_ID = 14;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700579
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700580 public final static int ACCOUNT_NAME = 15;
581 public final static int ACCOUNT_TYPE = 16;
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700582 public final static int DATA_SET = 17;
583 public final static int ACCOUNT_TYPE_AND_DATA_SET = 18;
584 public final static int DIRTY = 19;
585 public final static int VERSION = 20;
586 public final static int SOURCE_ID = 21;
587 public final static int SYNC1 = 22;
588 public final static int SYNC2 = 23;
589 public final static int SYNC3 = 24;
590 public final static int SYNC4 = 25;
591 public final static int DELETED = 26;
592 public final static int NAME_VERIFIED = 27;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700593
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700594 public final static int DATA_ID = 28;
595 public final static int DATA1 = 29;
596 public final static int DATA2 = 30;
597 public final static int DATA3 = 31;
598 public final static int DATA4 = 32;
599 public final static int DATA5 = 33;
600 public final static int DATA6 = 34;
601 public final static int DATA7 = 35;
602 public final static int DATA8 = 36;
603 public final static int DATA9 = 37;
604 public final static int DATA10 = 38;
605 public final static int DATA11 = 39;
606 public final static int DATA12 = 40;
607 public final static int DATA13 = 41;
608 public final static int DATA14 = 42;
609 public final static int DATA15 = 43;
610 public final static int DATA_SYNC1 = 44;
611 public final static int DATA_SYNC2 = 45;
612 public final static int DATA_SYNC3 = 46;
613 public final static int DATA_SYNC4 = 47;
614 public final static int DATA_VERSION = 48;
615 public final static int IS_PRIMARY = 49;
616 public final static int IS_SUPERPRIMARY = 50;
617 public final static int MIMETYPE = 51;
618 public final static int RES_PACKAGE = 52;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700619
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700620 public final static int GROUP_SOURCE_ID = 53;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700621
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700622 public final static int PRESENCE = 54;
623 public final static int CHAT_CAPABILITY = 55;
624 public final static int STATUS = 56;
625 public final static int STATUS_RES_PACKAGE = 57;
626 public final static int STATUS_ICON = 58;
627 public final static int STATUS_LABEL = 59;
628 public final static int STATUS_TIMESTAMP = 60;
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700629
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700630 public final static int PHOTO_URI = 61;
631 public final static int SEND_TO_VOICEMAIL = 62;
632 public final static int CUSTOM_RINGTONE = 63;
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700633 public final static int IS_USER_PROFILE = 64;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700634 }
Daniel Lehmann1316b132010-04-13 15:08:53 -0700635
Dave Santoro39156002011-07-19 01:18:14 -0700636 /**
637 * Projection used for the query that loads all data for the entire contact.
638 */
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700639 private static class DirectoryQuery {
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700640 final static String[] COLUMNS = new String[] {
641 Directory.DISPLAY_NAME,
642 Directory.PACKAGE_NAME,
643 Directory.TYPE_RESOURCE_ID,
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700644 Directory.ACCOUNT_TYPE,
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700645 Directory.ACCOUNT_NAME,
646 Directory.EXPORT_SUPPORT,
647 };
648
649 public final static int DISPLAY_NAME = 0;
650 public final static int PACKAGE_NAME = 1;
651 public final static int TYPE_RESOURCE_ID = 2;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700652 public final static int ACCOUNT_TYPE = 3;
653 public final static int ACCOUNT_NAME = 4;
654 public final static int EXPORT_SUPPORT = 5;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700655 }
656
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700657 private static class GroupQuery {
658 final static String[] COLUMNS = new String[] {
659 Groups.ACCOUNT_NAME,
660 Groups.ACCOUNT_TYPE,
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700661 Groups.DATA_SET,
662 Groups.ACCOUNT_TYPE_AND_DATA_SET,
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700663 Groups._ID,
664 Groups.TITLE,
665 Groups.AUTO_ADD,
666 Groups.FAVORITES,
667 };
668
669 public final static int ACCOUNT_NAME = 0;
670 public final static int ACCOUNT_TYPE = 1;
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700671 public final static int DATA_SET = 2;
672 public final static int ACCOUNT_TYPE_AND_DATA_SET = 3;
673 public final static int ID = 4;
674 public final static int TITLE = 5;
675 public final static int AUTO_ADD = 6;
676 public final static int FAVORITES = 7;
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700677 }
678
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700679 private final class LoadContactTask extends AsyncTask<Void, Void, Result> {
Daniel Lehmann1316b132010-04-13 15:08:53 -0700680
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700681 @Override
682 protected Result doInBackground(Void... args) {
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700683 try {
684 final ContentResolver resolver = getContext().getContentResolver();
Katherine Kuan6cd5b0a2011-09-16 11:46:01 -0700685 final Uri uriCurrentFormat = ContactLoaderUtils.ensureIsContactUri(
686 resolver, mLookupUri);
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700687 Result result = loadContactEntity(resolver, uriCurrentFormat);
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700688 if (!result.isNotFound()) {
Dmitri Plotnikov217245c2010-09-18 13:04:50 -0700689 if (result.isDirectoryEntry()) {
690 loadDirectoryMetaData(result);
691 } else if (mLoadGroupMetaData) {
692 loadGroupMetaData(result);
693 }
Dave Santoro39156002011-07-19 01:18:14 -0700694 if (mLoadStreamItems) {
695 loadStreamItems(result);
696 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800697 loadPhotoBinaryData(result);
Makoto Onuki870a87e2011-08-12 13:40:31 -0700698
699 // Note ME profile should never have "Add connection"
700 if (mLoadInvitableAccountTypes && !result.isUserProfile()) {
Makoto Onuki69b4a882011-07-22 10:05:10 -0700701 loadInvitableAccountTypes(result);
702 }
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700703 }
704 return result;
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700705 } catch (Exception e) {
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700706 Log.e(TAG, "Error loading the contact: " + mLookupUri, e);
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700707 return Result.forError(mRequestedUri, e);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700708 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700709 }
710
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700711 private Result loadContactEntity(ContentResolver resolver, Uri contactUri) {
712 Uri entityUri = Uri.withAppendedPath(contactUri, Contacts.Entity.CONTENT_DIRECTORY);
713 Cursor cursor = resolver.query(entityUri, ContactQuery.COLUMNS, null, null,
714 Contacts.Entity.RAW_CONTACT_ID);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700715 if (cursor == null) {
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700716 Log.e(TAG, "No cursor returned in loadContactEntity");
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700717 return Result.forNotFound(mRequestedUri);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700718 }
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700719
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700720 try {
721 if (!cursor.moveToFirst()) {
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700722 cursor.close();
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700723 return Result.forNotFound(mRequestedUri);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700724 }
725
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700726 long currentRawContactId = -1;
727 Entity entity = null;
728 Result result = loadContactHeaderData(cursor, contactUri);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700729 ArrayList<Entity> entities = result.getEntities();
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700730 HashMap<Long, DataStatus> statuses = result.getStatuses();
731 for (; !cursor.isAfterLast(); cursor.moveToNext()) {
732 long rawContactId = cursor.getLong(ContactQuery.RAW_CONTACT_ID);
733 if (rawContactId != currentRawContactId) {
734 currentRawContactId = rawContactId;
735 entity = new android.content.Entity(loadRawContact(cursor));
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700736 entities.add(entity);
737 }
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700738 if (!cursor.isNull(ContactQuery.DATA_ID)) {
739 ContentValues data = loadData(cursor);
740 entity.addSubValue(ContactsContract.Data.CONTENT_URI, data);
741
742 if (!cursor.isNull(ContactQuery.PRESENCE)
743 || !cursor.isNull(ContactQuery.STATUS)) {
744 final DataStatus status = new DataStatus(cursor);
745 final long dataId = cursor.getLong(ContactQuery.DATA_ID);
746 statuses.put(dataId, status);
747 }
748 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700749 }
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700750
751 return result;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700752 } finally {
753 cursor.close();
754 }
755 }
756
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700757 /**
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800758 * Looks for the photo data item in entities. If found, creates a new Bitmap instance. If
759 * not found, returns null
760 */
761 private void loadPhotoBinaryData(Result contactData) {
Dave Santoro0a2a5db2011-06-29 00:37:06 -0700762
763 // If we have a photo URI, try loading that first.
764 String photoUri = contactData.getPhotoUri();
765 if (photoUri != null) {
766 try {
767 AssetFileDescriptor fd = getContext().getContentResolver()
768 .openAssetFileDescriptor(Uri.parse(photoUri), "r");
769 byte[] buffer = new byte[16 * 1024];
770 FileInputStream fis = fd.createInputStream();
771 ByteArrayOutputStream baos = new ByteArrayOutputStream();
772 try {
773 int size;
774 while ((size = fis.read(buffer)) != -1) {
775 baos.write(buffer, 0, size);
776 }
777 contactData.setPhotoBinaryData(baos.toByteArray());
778 } finally {
779 fis.close();
780 fd.close();
781 }
782 return;
783 } catch (IOException ioe) {
784 // Just fall back to the case below.
785 }
786 }
787
788 // If we couldn't load from a file, fall back to the data blob.
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800789 final long photoId = contactData.getPhotoId();
790 if (photoId <= 0) {
791 // No photo ID
792 return;
793 }
794
795 for (Entity entity : contactData.getEntities()) {
796 for (NamedContentValues subValue : entity.getSubValues()) {
797 final ContentValues entryValues = subValue.values;
798 final long dataId = entryValues.getAsLong(Data._ID);
799 if (dataId == photoId) {
800 final String mimeType = entryValues.getAsString(Data.MIMETYPE);
801 // Correct Data Id but incorrect MimeType? Don't load
802 if (!Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
803 return;
804 }
805 contactData.setPhotoBinaryData(entryValues.getAsByteArray(Photo.PHOTO));
806 break;
807 }
808 }
809 }
810 }
811
Makoto Onuki3e6991e2011-07-24 14:51:20 -0700812 /**
813 * Sets the "invitable" account types to {@link Result#mInvitableAccountTypes}.
Makoto Onuki3e6991e2011-07-24 14:51:20 -0700814 */
Makoto Onuki69b4a882011-07-22 10:05:10 -0700815 private void loadInvitableAccountTypes(Result contactData) {
Katherine Kuan08bcf712011-10-09 13:43:53 -0700816 Map<AccountTypeWithDataSet, AccountType> invitables =
817 AccountTypeManager.getInstance(getContext()).getUsableInvitableAccountTypes();
818 if (invitables.isEmpty()) {
Makoto Onuki69b4a882011-07-22 10:05:10 -0700819 return;
820 }
821
Katherine Kuan08bcf712011-10-09 13:43:53 -0700822 HashMap<AccountTypeWithDataSet, AccountType> result = Maps.newHashMap(invitables);
Makoto Onuki69b4a882011-07-22 10:05:10 -0700823
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -0700824 // Remove the ones that already have a raw contact in the current contact
Makoto Onuki69b4a882011-07-22 10:05:10 -0700825 for (Entity entity : contactData.getEntities()) {
Makoto Onuki6ad227f2011-08-15 13:46:59 -0700826 final ContentValues values = entity.getEntityValues();
827 final AccountTypeWithDataSet type = AccountTypeWithDataSet.get(
828 values.getAsString(RawContacts.ACCOUNT_TYPE),
829 values.getAsString(RawContacts.DATA_SET));
830 result.remove(type);
Makoto Onuki69b4a882011-07-22 10:05:10 -0700831 }
832
833 // Set to mInvitableAccountTypes
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700834 contactData.mInvitableAccountTypes.addAll(result.values());
Makoto Onuki69b4a882011-07-22 10:05:10 -0700835 }
836
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800837 /**
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700838 * Extracts Contact level columns from the cursor.
839 */
840 private Result loadContactHeaderData(final Cursor cursor, Uri contactUri) {
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700841 final String directoryParameter =
842 contactUri.getQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY);
843 final long directoryId = directoryParameter == null
844 ? Directory.DEFAULT
845 : Long.parseLong(directoryParameter);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700846 final long contactId = cursor.getLong(ContactQuery.CONTACT_ID);
847 final String lookupKey = cursor.getString(ContactQuery.LOOKUP_KEY);
848 final long nameRawContactId = cursor.getLong(ContactQuery.NAME_RAW_CONTACT_ID);
849 final int displayNameSource = cursor.getInt(ContactQuery.DISPLAY_NAME_SOURCE);
850 final String displayName = cursor.getString(ContactQuery.DISPLAY_NAME);
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700851 final String altDisplayName = cursor.getString(ContactQuery.ALT_DISPLAY_NAME);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700852 final String phoneticName = cursor.getString(ContactQuery.PHONETIC_NAME);
853 final long photoId = cursor.getLong(ContactQuery.PHOTO_ID);
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700854 final String photoUri = cursor.getString(ContactQuery.PHOTO_URI);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700855 final boolean starred = cursor.getInt(ContactQuery.STARRED) != 0;
856 final Integer presence = cursor.isNull(ContactQuery.CONTACT_PRESENCE)
857 ? null
858 : cursor.getInt(ContactQuery.CONTACT_PRESENCE);
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700859 final boolean sendToVoicemail = cursor.getInt(ContactQuery.SEND_TO_VOICEMAIL) == 1;
860 final String customRingtone = cursor.getString(ContactQuery.CUSTOM_RINGTONE);
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700861 final boolean isUserProfile = cursor.getInt(ContactQuery.IS_USER_PROFILE) == 1;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700862
Dmitri Plotnikov1536ea12010-10-29 11:51:05 -0700863 Uri lookupUri;
864 if (directoryId == Directory.DEFAULT || directoryId == Directory.LOCAL_INVISIBLE) {
865 lookupUri = ContentUris.withAppendedId(
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700866 Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey), contactId);
Dmitri Plotnikov1536ea12010-10-29 11:51:05 -0700867 } else {
868 lookupUri = contactUri;
869 }
870
Daniel Lehmann685157e2011-08-29 21:07:01 -0700871 return new Result(mRequestedUri, contactUri, lookupUri, directoryId, lookupKey,
872 contactId, nameRawContactId, displayNameSource, photoId, photoUri, displayName,
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700873 altDisplayName, phoneticName, starred, presence, sendToVoicemail,
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700874 customRingtone, isUserProfile);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700875 }
876
877 /**
878 * Extracts RawContact level columns from the cursor.
879 */
880 private ContentValues loadRawContact(Cursor cursor) {
881 ContentValues cv = new ContentValues();
882
883 cv.put(RawContacts._ID, cursor.getLong(ContactQuery.RAW_CONTACT_ID));
884
885 cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_NAME);
886 cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_TYPE);
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700887 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SET);
888 cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_TYPE_AND_DATA_SET);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700889 cursorColumnToContentValues(cursor, cv, ContactQuery.DIRTY);
890 cursorColumnToContentValues(cursor, cv, ContactQuery.VERSION);
891 cursorColumnToContentValues(cursor, cv, ContactQuery.SOURCE_ID);
892 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC1);
893 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC2);
894 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC3);
895 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC4);
896 cursorColumnToContentValues(cursor, cv, ContactQuery.DELETED);
897 cursorColumnToContentValues(cursor, cv, ContactQuery.CONTACT_ID);
898 cursorColumnToContentValues(cursor, cv, ContactQuery.STARRED);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700899 cursorColumnToContentValues(cursor, cv, ContactQuery.NAME_VERIFIED);
900
901 return cv;
902 }
903
904 /**
905 * Extracts Data level columns from the cursor.
906 */
907 private ContentValues loadData(Cursor cursor) {
908 ContentValues cv = new ContentValues();
909
910 cv.put(Data._ID, cursor.getLong(ContactQuery.DATA_ID));
911
912 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA1);
913 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA2);
914 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA3);
915 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA4);
916 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA5);
917 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA6);
918 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA7);
919 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA8);
920 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA9);
921 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA10);
922 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA11);
923 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA12);
924 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA13);
925 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA14);
926 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA15);
927 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC1);
928 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC2);
929 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC3);
930 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC4);
931 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_VERSION);
932 cursorColumnToContentValues(cursor, cv, ContactQuery.IS_PRIMARY);
933 cursorColumnToContentValues(cursor, cv, ContactQuery.IS_SUPERPRIMARY);
934 cursorColumnToContentValues(cursor, cv, ContactQuery.MIMETYPE);
935 cursorColumnToContentValues(cursor, cv, ContactQuery.RES_PACKAGE);
936 cursorColumnToContentValues(cursor, cv, ContactQuery.GROUP_SOURCE_ID);
Daniel Lehmann8fd7bb62010-08-13 20:50:31 -0700937 cursorColumnToContentValues(cursor, cv, ContactQuery.CHAT_CAPABILITY);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700938
939 return cv;
940 }
941
942 private void cursorColumnToContentValues(
943 Cursor cursor, ContentValues values, int index) {
944 switch (cursor.getType(index)) {
945 case Cursor.FIELD_TYPE_NULL:
946 // don't put anything in the content values
947 break;
948 case Cursor.FIELD_TYPE_INTEGER:
949 values.put(ContactQuery.COLUMNS[index], cursor.getLong(index));
950 break;
951 case Cursor.FIELD_TYPE_STRING:
952 values.put(ContactQuery.COLUMNS[index], cursor.getString(index));
953 break;
954 case Cursor.FIELD_TYPE_BLOB:
955 values.put(ContactQuery.COLUMNS[index], cursor.getBlob(index));
956 break;
957 default:
958 throw new IllegalStateException("Invalid or unhandled data type");
959 }
960 }
961
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700962 private void loadDirectoryMetaData(Result result) {
963 long directoryId = result.getDirectoryId();
964
965 Cursor cursor = getContext().getContentResolver().query(
966 ContentUris.withAppendedId(Directory.CONTENT_URI, directoryId),
967 DirectoryQuery.COLUMNS, null, null, null);
968 if (cursor == null) {
969 return;
970 }
971 try {
972 if (cursor.moveToFirst()) {
973 final String displayName = cursor.getString(DirectoryQuery.DISPLAY_NAME);
974 final String packageName = cursor.getString(DirectoryQuery.PACKAGE_NAME);
975 final int typeResourceId = cursor.getInt(DirectoryQuery.TYPE_RESOURCE_ID);
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700976 final String accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE);
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700977 final String accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME);
978 final int exportSupport = cursor.getInt(DirectoryQuery.EXPORT_SUPPORT);
979 String directoryType = null;
980 if (!TextUtils.isEmpty(packageName)) {
981 PackageManager pm = getContext().getPackageManager();
982 try {
983 Resources resources = pm.getResourcesForApplication(packageName);
984 directoryType = resources.getString(typeResourceId);
985 } catch (NameNotFoundException e) {
986 Log.w(TAG, "Contact directory resource not found: "
987 + packageName + "." + typeResourceId);
988 }
989 }
990
991 result.setDirectoryMetaData(
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700992 displayName, directoryType, accountType, accountName, exportSupport);
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700993 }
994 } finally {
995 cursor.close();
996 }
997 }
998
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700999 /**
1000 * Loads groups meta-data for all groups associated with all constituent raw contacts'
1001 * accounts.
1002 */
1003 private void loadGroupMetaData(Result result) {
1004 StringBuilder selection = new StringBuilder();
1005 ArrayList<String> selectionArgs = new ArrayList<String>();
1006 for (Entity entity : result.mEntities) {
1007 ContentValues values = entity.getEntityValues();
1008 String accountName = values.getAsString(RawContacts.ACCOUNT_NAME);
1009 String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
Dave Santoro2b3f3c52011-07-26 17:35:42 -07001010 String dataSet = values.getAsString(RawContacts.DATA_SET);
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001011 if (accountName != null && accountType != null) {
1012 if (selection.length() != 0) {
1013 selection.append(" OR ");
1014 }
1015 selection.append(
Dave Santoro2b3f3c52011-07-26 17:35:42 -07001016 "(" + Groups.ACCOUNT_NAME + "=? AND " + Groups.ACCOUNT_TYPE + "=?");
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001017 selectionArgs.add(accountName);
1018 selectionArgs.add(accountType);
Dave Santoro2b3f3c52011-07-26 17:35:42 -07001019
1020 if (dataSet != null) {
1021 selection.append(" AND " + Groups.DATA_SET + "=?");
1022 selectionArgs.add(dataSet);
1023 } else {
1024 selection.append(" AND " + Groups.DATA_SET + " IS NULL");
1025 }
1026 selection.append(")");
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001027 }
1028 }
1029 Cursor cursor = getContext().getContentResolver().query(Groups.CONTENT_URI,
1030 GroupQuery.COLUMNS, selection.toString(), selectionArgs.toArray(new String[0]),
1031 null);
1032 try {
1033 while (cursor.moveToNext()) {
1034 final String accountName = cursor.getString(GroupQuery.ACCOUNT_NAME);
1035 final String accountType = cursor.getString(GroupQuery.ACCOUNT_TYPE);
Dave Santoro2b3f3c52011-07-26 17:35:42 -07001036 final String dataSet = cursor.getString(GroupQuery.DATA_SET);
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001037 final long groupId = cursor.getLong(GroupQuery.ID);
1038 final String title = cursor.getString(GroupQuery.TITLE);
1039 final boolean defaultGroup = cursor.isNull(GroupQuery.AUTO_ADD)
1040 ? false
1041 : cursor.getInt(GroupQuery.AUTO_ADD) != 0;
1042 final boolean favorites = cursor.isNull(GroupQuery.FAVORITES)
1043 ? false
1044 : cursor.getInt(GroupQuery.FAVORITES) != 0;
1045
Dmitri Plotnikove843f912010-09-16 15:21:48 -07001046 result.addGroupMetaData(new GroupMetaData(
Dave Santoro2b3f3c52011-07-26 17:35:42 -07001047 accountName, accountType, dataSet, groupId, title, defaultGroup,
1048 favorites));
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001049 }
1050 } finally {
1051 cursor.close();
1052 }
1053 }
1054
Dave Santoro39156002011-07-19 01:18:14 -07001055 /**
1056 * Loads all stream items and stream item photos belonging to this contact.
1057 */
1058 private void loadStreamItems(Result result) {
1059 Cursor cursor = getContext().getContentResolver().query(
1060 Contacts.CONTENT_LOOKUP_URI.buildUpon()
1061 .appendPath(result.getLookupKey())
1062 .appendPath(Contacts.StreamItems.CONTENT_DIRECTORY).build(),
1063 null, null, null, null);
1064 Map<Long, StreamItemEntry> streamItemsById = new HashMap<Long, StreamItemEntry>();
1065 ArrayList<StreamItemEntry> streamItems = new ArrayList<StreamItemEntry>();
1066 try {
1067 while (cursor.moveToNext()) {
1068 StreamItemEntry streamItem = new StreamItemEntry(cursor);
1069 streamItemsById.put(streamItem.getId(), streamItem);
1070 streamItems.add(streamItem);
1071 }
1072 } finally {
1073 cursor.close();
1074 }
1075
1076 // Now retrieve any photo records associated with the stream items.
Dave Santoro39156002011-07-19 01:18:14 -07001077 if (!streamItems.isEmpty()) {
Dave Santoroa4400d52011-09-02 16:14:53 -07001078 if (result.isUserProfile()) {
1079 // If the stream items we're loading are for the profile, we can't bulk-load the
1080 // stream items with a custom selection.
1081 for (StreamItemEntry entry : streamItems) {
1082 Cursor siCursor = getContext().getContentResolver().query(
1083 Uri.withAppendedPath(
1084 ContentUris.withAppendedId(
1085 StreamItems.CONTENT_URI, entry.getId()),
1086 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY),
1087 null, null, null, null);
1088 try {
1089 while (siCursor.moveToNext()) {
1090 entry.addPhoto(new StreamItemPhotoEntry(siCursor));
1091 }
1092 } finally {
1093 siCursor.close();
1094 }
Dave Santoro39156002011-07-19 01:18:14 -07001095 }
Dave Santoroa4400d52011-09-02 16:14:53 -07001096 } else {
1097 String[] streamItemIdArr = new String[streamItems.size()];
1098 StringBuilder streamItemPhotoSelection = new StringBuilder();
1099 streamItemPhotoSelection.append(StreamItemPhotos.STREAM_ITEM_ID + " IN (");
1100 for (int i = 0; i < streamItems.size(); i++) {
1101 if (i > 0) {
1102 streamItemPhotoSelection.append(",");
1103 }
1104 streamItemPhotoSelection.append("?");
1105 streamItemIdArr[i] = String.valueOf(streamItems.get(i).getId());
Dave Santoro39156002011-07-19 01:18:14 -07001106 }
Dave Santoroa4400d52011-09-02 16:14:53 -07001107 streamItemPhotoSelection.append(")");
1108 Cursor sipCursor = getContext().getContentResolver().query(
1109 StreamItems.CONTENT_PHOTO_URI,
1110 null, streamItemPhotoSelection.toString(), streamItemIdArr,
1111 StreamItemPhotos.STREAM_ITEM_ID);
1112 try {
1113 while (sipCursor.moveToNext()) {
1114 long streamItemId = sipCursor.getLong(
1115 sipCursor.getColumnIndex(StreamItemPhotos.STREAM_ITEM_ID));
1116 StreamItemEntry streamItem = streamItemsById.get(streamItemId);
1117 streamItem.addPhoto(new StreamItemPhotoEntry(sipCursor));
1118 }
1119 } finally {
1120 sipCursor.close();
1121 }
Dave Santoro39156002011-07-19 01:18:14 -07001122 }
1123 }
1124
1125 // Set the sorted stream items on the result.
1126 Collections.sort(streamItems);
1127 result.mStreamItems.addAll(streamItems);
1128 }
1129
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001130 @Override
1131 protected void onPostExecute(Result result) {
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001132 unregisterObserver();
1133
Daniel Lehmann1316b132010-04-13 15:08:53 -07001134 // The creator isn't interested in any further updates
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001135 if (mDestroyed || result == null) {
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001136 return;
1137 }
1138
1139 mContact = result;
Daniel Lehmann18f104f2010-05-07 15:41:11 -07001140
Makoto Onuki2621c5b2011-10-03 12:56:16 -07001141 if (result.isLoaded()) {
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001142 mLookupUri = result.getLookupUri();
1143
1144 if (!result.isDirectoryEntry()) {
1145 Log.i(TAG, "Registering content observer for " + mLookupUri);
1146 if (mObserver == null) {
1147 mObserver = new ForceLoadContentObserver();
1148 }
1149 getContext().getContentResolver().registerContentObserver(
1150 mLookupUri, true, mObserver);
Daniel Lehmann18f104f2010-05-07 15:41:11 -07001151 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -08001152
1153 if (mContact.getPhotoBinaryData() == null && mContact.getPhotoUri() != null) {
1154 mContact.setLoadingPhoto(true);
1155 new AsyncPhotoLoader().execute(mContact.getPhotoUri());
1156 }
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001157
1158 // inform the source of the data that this contact is being looked at
1159 postViewNotificationToSyncAdapter();
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001160 }
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001161
1162 deliverResult(mContact);
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001163 }
1164 }
1165
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001166 /**
1167 * Posts a message to the contributing sync adapters that have opted-in, notifying them
1168 * that the contact has just been loaded
1169 */
1170 private void postViewNotificationToSyncAdapter() {
1171 Context context = getContext();
1172 for (Entity entity : mContact.getEntities()) {
1173 final ContentValues entityValues = entity.getEntityValues();
Makoto Onukiaba2b832011-08-12 15:44:53 -07001174 final long rawContactId = entityValues.getAsLong(RawContacts.Entity._ID);
1175 if (mNotifiedRawContactIds.contains(rawContactId)) {
1176 continue; // Already notified for this raw contact.
1177 }
1178 mNotifiedRawContactIds.add(rawContactId);
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001179 final String type = entityValues.getAsString(RawContacts.ACCOUNT_TYPE);
1180 final String dataSet = entityValues.getAsString(RawContacts.DATA_SET);
Flavio Lerda59a887e2011-08-14 18:13:17 +01001181 final AccountType accountType = AccountTypeManager.getInstance(context).getAccountType(
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001182 type, dataSet);
1183 final String serviceName = accountType.getViewContactNotifyServiceClassName();
1184 final String resPackageName = accountType.resPackageName;
1185 if (!TextUtils.isEmpty(serviceName) && !TextUtils.isEmpty(resPackageName)) {
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001186 final Uri uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
1187 final Intent intent = new Intent();
1188 intent.setClassName(resPackageName, serviceName);
1189 intent.setAction(Intent.ACTION_VIEW);
1190 intent.setDataAndType(uri, RawContacts.CONTENT_ITEM_TYPE);
1191 try {
1192 context.startService(intent);
1193 } catch (Exception e) {
1194 Log.e(TAG, "Error sending message to source-app", e);
1195 }
1196 }
1197 }
1198 }
1199
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -08001200 private class AsyncPhotoLoader extends AsyncTask<String, Void, byte[]> {
1201
1202 private static final int BUFFER_SIZE = 1024*16;
1203
1204 @Override
1205 protected byte[] doInBackground(String... params) {
1206 Uri uri = Uri.parse(params[0]);
1207 byte[] data = null;
1208 try {
1209 InputStream is = getContext().getContentResolver().openInputStream(uri);
1210 if (is != null) {
1211 ByteArrayOutputStream baos = new ByteArrayOutputStream();
1212 try {
1213 byte[] mBuffer = new byte[BUFFER_SIZE];
1214
1215 int size;
1216 while ((size = is.read(mBuffer)) != -1) {
1217 baos.write(mBuffer, 0, size);
1218 }
1219 data = baos.toByteArray();
1220 } finally {
1221 is.close();
1222 }
1223 } else {
1224 Log.v(TAG, "Cannot load photo " + uri);
1225 }
1226 } catch (IOException e) {
1227 Log.e(TAG, "Cannot load photo " + uri, e);
1228 }
1229
1230 return data;
1231 }
1232
1233 @Override
1234 protected void onPostExecute(byte[] data) {
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001235 if (mContact != null) {
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -08001236 mContact = new Result(mContact);
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001237 mContact.setPhotoBinaryData(data);
1238 mContact.setLoadingPhoto(false);
1239 deliverResult(mContact);
1240 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -08001241 }
1242 }
1243
Daniel Lehmann3a120772010-06-21 16:21:35 -07001244 private void unregisterObserver() {
1245 if (mObserver != null) {
1246 getContext().getContentResolver().unregisterContentObserver(mObserver);
1247 mObserver = null;
1248 }
1249 }
1250
Daniel Lehmanncdef2b62010-06-06 18:25:49 -07001251 public ContactLoader(Context context, Uri lookupUri) {
Makoto Onuki69b4a882011-07-22 10:05:10 -07001252 this(context, lookupUri, false, false, false);
Dmitri Plotnikove843f912010-09-16 15:21:48 -07001253 }
1254
Dave Santoro39156002011-07-19 01:18:14 -07001255 public ContactLoader(Context context, Uri lookupUri, boolean loadGroupMetaData,
Makoto Onuki69b4a882011-07-22 10:05:10 -07001256 boolean loadStreamItems, boolean loadInvitableAccountTypes) {
Daniel Lehmann74a2dc52010-04-15 16:52:33 -07001257 super(context);
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001258 mLookupUri = lookupUri;
Daniel Lehmann685157e2011-08-29 21:07:01 -07001259 mRequestedUri = lookupUri;
Dmitri Plotnikove843f912010-09-16 15:21:48 -07001260 mLoadGroupMetaData = loadGroupMetaData;
Dave Santoro39156002011-07-19 01:18:14 -07001261 mLoadStreamItems = loadStreamItems;
Makoto Onuki69b4a882011-07-22 10:05:10 -07001262 mLoadInvitableAccountTypes = loadInvitableAccountTypes;
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001263 }
1264
Dmitri Plotnikov5a30d9a2010-11-23 14:59:50 -08001265 public Uri getLookupUri() {
1266 return mLookupUri;
1267 }
1268
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001269 @Override
Dianne Hackbornc04fc272010-12-20 23:13:10 -08001270 protected void onStartLoading() {
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001271 if (mContact != null) {
Daniel Lehmanncbcc4492010-04-12 18:03:54 -07001272 deliverResult(mContact);
Dmitri Plotnikov97e90c62011-01-03 11:58:13 -08001273 }
1274
1275 if (takeContentChanged() || mContact == null) {
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001276 forceLoad();
1277 }
1278 }
1279
1280 @Override
Dianne Hackbornc04fc272010-12-20 23:13:10 -08001281 protected void onForceLoad() {
Daniel Lehmann18f104f2010-05-07 15:41:11 -07001282 final LoadContactTask task = new LoadContactTask();
Makoto Onuki73c341f2011-08-29 09:31:44 -07001283 task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[])null);
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001284 }
1285
1286 @Override
Dianne Hackbornc04fc272010-12-20 23:13:10 -08001287 protected void onReset() {
Dianne Hackborn4ef95cc2010-12-16 00:44:33 -08001288 unregisterObserver();
1289 mContact = null;
1290 mDestroyed = true;
1291 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001292}