blob: 471183efc0074e7538731b83321b76cadc783271 [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
Daniel Lehmann4cd94412010-04-08 16:44:36 -070019import com.android.contacts.util.DataStatus;
20
21import android.content.ContentResolver;
22import android.content.ContentUris;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -070023import android.content.ContentValues;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070024import android.content.Context;
25import android.content.Entity;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -070026import android.content.Entity.NamedContentValues;
Jeff Hamilton3c462912010-05-15 02:20:01 -050027import android.content.Loader;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -070028import android.content.pm.PackageManager;
29import android.content.pm.PackageManager.NameNotFoundException;
30import android.content.res.Resources;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070031import android.database.Cursor;
32import android.net.Uri;
33import android.os.AsyncTask;
Daniel Lehmann1316b132010-04-13 15:08:53 -070034import android.provider.ContactsContract;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -070035import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -080036import android.provider.ContactsContract.CommonDataKinds.Photo;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070037import android.provider.ContactsContract.Contacts;
38import android.provider.ContactsContract.Data;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -070039import android.provider.ContactsContract.Directory;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070040import android.provider.ContactsContract.DisplayNameSources;
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -070041import android.provider.ContactsContract.Groups;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070042import android.provider.ContactsContract.RawContacts;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -070043import android.text.TextUtils;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070044import android.util.Log;
45
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -080046import java.io.ByteArrayOutputStream;
47import java.io.IOException;
48import java.io.InputStream;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070049import java.util.ArrayList;
50import java.util.HashMap;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -070051import java.util.List;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070052
53/**
54 * Loads a single Contact and all it constituent RawContacts.
55 */
Daniel Lehmanncdef2b62010-06-06 18:25:49 -070056public class ContactLoader extends Loader<ContactLoader.Result> {
Daniel Lehmann18f104f2010-05-07 15:41:11 -070057 private static final String TAG = "ContactLoader";
58
Daniel Lehmann4cd94412010-04-08 16:44:36 -070059 private Uri mLookupUri;
Dmitri Plotnikove843f912010-09-16 15:21:48 -070060 private boolean mLoadGroupMetaData;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070061 private Result mContact;
62 private ForceLoadContentObserver mObserver;
63 private boolean mDestroyed;
64
Dmitri Plotnikove843f912010-09-16 15:21:48 -070065
Daniel Lehmanncdef2b62010-06-06 18:25:49 -070066 public interface Listener {
Daniel Lehmann4cd94412010-04-08 16:44:36 -070067 public void onContactLoaded(Result contact);
68 }
69
70 /**
71 * The result of a load operation. Contains all data necessary to display the contact.
72 */
73 public static final class Result {
74 /**
75 * Singleton instance that represents "No Contact Found"
76 */
77 public static final Result NOT_FOUND = new Result();
78
Daniel Lehmann18f104f2010-05-07 15:41:11 -070079 /**
80 * Singleton instance that represents an error, e.g. because of an invalid Uri
81 * TODO: We should come up with something nicer here. Maybe use an Either type so
82 * that we can capture the Exception?
83 */
84 public static final Result ERROR = new Result();
85
Daniel Lehmann4cd94412010-04-08 16:44:36 -070086 private final Uri mLookupUri;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070087 private final Uri mUri;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -070088 private final long mDirectoryId;
89 private final String mLookupKey;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070090 private final long mId;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070091 private final long mNameRawContactId;
92 private final int mDisplayNameSource;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -070093 private final long mPhotoId;
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -070094 private final String mPhotoUri;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -070095 private final String mDisplayName;
96 private final String mPhoneticName;
97 private final boolean mStarred;
98 private final Integer mPresence;
99 private final ArrayList<Entity> mEntities;
100 private final HashMap<Long, DataStatus> mStatuses;
101 private final String mStatus;
102 private final Long mStatusTimestamp;
103 private final Integer mStatusLabel;
104 private final String mStatusResPackage;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700105
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700106 private String mDirectoryDisplayName;
107 private String mDirectoryType;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700108 private String mDirectoryAccountType;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700109 private String mDirectoryAccountName;
110 private int mDirectoryExportSupport;
111
Dmitri Plotnikove843f912010-09-16 15:21:48 -0700112 private ArrayList<GroupMetaData> mGroups;
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700113
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800114 private boolean mLoadingPhoto;
115 private byte[] mPhotoBinaryData;
116
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700117 /**
118 * Constructor for case "no contact found". This must only be used for the
119 * final {@link Result#NOT_FOUND} singleton
120 */
121 private Result() {
122 mLookupUri = null;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700123 mUri = null;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700124 mDirectoryId = -1;
125 mLookupKey = null;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700126 mId = -1;
127 mEntities = null;
128 mStatuses = null;
129 mNameRawContactId = -1;
130 mDisplayNameSource = DisplayNameSources.UNDEFINED;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700131 mPhotoId = -1;
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700132 mPhotoUri = null;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700133 mDisplayName = null;
134 mPhoneticName = null;
135 mStarred = false;
136 mPresence = null;
137 mStatus = null;
138 mStatusTimestamp = null;
139 mStatusLabel = null;
140 mStatusResPackage = null;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700141 }
142
143 /**
144 * Constructor to call when contact was found
145 */
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700146 private Result(Uri uri, Uri lookupUri, long directoryId, String lookupKey, long id,
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700147 long nameRawContactId, int displayNameSource, long photoId, String photoUri,
148 String displayName, String phoneticName, boolean starred, Integer presence,
149 String status, Long statusTimestamp, Integer statusLabel, String statusResPackage) {
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700150 mLookupUri = lookupUri;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700151 mUri = uri;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700152 mDirectoryId = directoryId;
153 mLookupKey = lookupKey;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700154 mId = id;
155 mEntities = new ArrayList<Entity>();
156 mStatuses = new HashMap<Long, DataStatus>();
157 mNameRawContactId = nameRawContactId;
158 mDisplayNameSource = displayNameSource;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700159 mPhotoId = photoId;
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700160 mPhotoUri = photoUri;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700161 mDisplayName = displayName;
162 mPhoneticName = phoneticName;
163 mStarred = starred;
164 mPresence = presence;
165 mStatus = status;
166 mStatusTimestamp = statusTimestamp;
167 mStatusLabel = statusLabel;
168 mStatusResPackage = statusResPackage;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700169 }
170
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700171 /**
172 * @param exportSupport See {@link Directory#EXPORT_SUPPORT}.
173 */
Daniel Lehmann1ad4d1b2010-10-18 19:20:41 -0700174 private void setDirectoryMetaData(String displayName, String directoryType,
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700175 String accountType, String accountName, int exportSupport) {
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700176 mDirectoryDisplayName = displayName;
177 mDirectoryType = directoryType;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700178 mDirectoryAccountType = accountType;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700179 mDirectoryAccountName = accountName;
180 mDirectoryExportSupport = exportSupport;
181 }
182
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800183 private void setLoadingPhoto(boolean flag) {
184 mLoadingPhoto = flag;
185 }
186
187 private void setPhotoBinaryData(byte[] photoBinaryData) {
188 mPhotoBinaryData = photoBinaryData;
189 }
190
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700191 public Uri getLookupUri() {
192 return mLookupUri;
193 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800194
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700195 public String getLookupKey() {
196 return mLookupKey;
197 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800198
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700199 public Uri getUri() {
200 return mUri;
201 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800202
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700203 public long getId() {
204 return mId;
205 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800206
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700207 public long getNameRawContactId() {
208 return mNameRawContactId;
209 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800210
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700211 public int getDisplayNameSource() {
212 return mDisplayNameSource;
213 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800214
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700215 public long getPhotoId() {
216 return mPhotoId;
217 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800218
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700219 public String getPhotoUri() {
220 return mPhotoUri;
221 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800222
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700223 public String getDisplayName() {
224 return mDisplayName;
225 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800226
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700227 public String getPhoneticName() {
228 return mPhoneticName;
229 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800230
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700231 public boolean getStarred() {
232 return mStarred;
233 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800234
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700235 public Integer getPresence() {
236 return mPresence;
237 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800238
Daniel Lehmann2d4f7592010-10-12 23:31:31 -0700239 public String getSocialSnippet() {
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700240 return mStatus;
241 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800242
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700243 public Long getStatusTimestamp() {
244 return mStatusTimestamp;
245 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800246
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700247 public Integer getStatusLabel() {
248 return mStatusLabel;
249 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800250
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700251 public String getStatusResPackage() {
252 return mStatusResPackage;
253 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800254
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700255 public ArrayList<Entity> getEntities() {
256 return mEntities;
257 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800258
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700259 public HashMap<Long, DataStatus> getStatuses() {
260 return mStatuses;
261 }
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700262
263 public long getDirectoryId() {
264 return mDirectoryId;
265 }
266
267 public boolean isDirectoryEntry() {
Dmitri Plotnikov5f72c1f2010-09-01 21:21:04 -0700268 return mDirectoryId != -1 && mDirectoryId != Directory.DEFAULT
269 && mDirectoryId != Directory.LOCAL_INVISIBLE;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700270 }
271
272 public int getDirectoryExportSupport() {
273 return mDirectoryExportSupport;
274 }
275
276 public String getDirectoryDisplayName() {
277 return mDirectoryDisplayName;
278 }
279
280 public String getDirectoryType() {
281 return mDirectoryType;
282 }
283
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700284 public String getDirectoryAccountType() {
285 return mDirectoryAccountType;
286 }
287
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700288 public String getDirectoryAccountName() {
289 return mDirectoryAccountName;
290 }
291
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800292 public boolean isLoadingPhoto() {
293 return mLoadingPhoto;
294 }
295
296 public byte[] getPhotoBinaryData() {
297 return mPhotoBinaryData;
298 }
299
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700300 public ArrayList<ContentValues> getContentValues() {
301 if (mEntities.size() != 1) {
302 throw new IllegalStateException(
303 "Cannot extract content values from an aggregated contact");
304 }
305
306 Entity entity = mEntities.get(0);
307 ArrayList<ContentValues> result = new ArrayList<ContentValues>();
308 ArrayList<NamedContentValues> subValues = entity.getSubValues();
309 if (subValues != null) {
310 int size = subValues.size();
311 for (int i = 0; i < size; i++) {
312 NamedContentValues pair = subValues.get(i);
313 if (Data.CONTENT_URI.equals(pair.uri)) {
314 result.add(pair.values);
315 }
316 }
317 }
Dmitri Plotnikov40ec3a82010-11-10 11:25:33 -0800318
319 // If the photo was loaded using the URI, create an entry for the photo
320 // binary data.
321 if (mPhotoId == 0 && mPhotoBinaryData != null) {
322 ContentValues photo = new ContentValues();
323 photo.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
324 photo.put(Photo.PHOTO, mPhotoBinaryData);
325 result.add(photo);
326 }
327
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700328 return result;
329 }
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700330
Daniel Lehmann1ad4d1b2010-10-18 19:20:41 -0700331 private void addGroupMetaData(GroupMetaData group) {
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700332 if (mGroups == null) {
Dmitri Plotnikove843f912010-09-16 15:21:48 -0700333 mGroups = new ArrayList<GroupMetaData>();
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700334 }
335 mGroups.add(group);
336 }
337
Dmitri Plotnikove843f912010-09-16 15:21:48 -0700338 public List<GroupMetaData> getGroupMetaData() {
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700339 return mGroups;
340 }
341 }
342
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700343 private static class ContactQuery {
344 // Projection used for the query that loads all data for the entire contact.
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700345 final static String[] COLUMNS = new String[] {
346 Contacts.NAME_RAW_CONTACT_ID,
347 Contacts.DISPLAY_NAME_SOURCE,
348 Contacts.LOOKUP_KEY,
349 Contacts.DISPLAY_NAME,
350 Contacts.PHONETIC_NAME,
351 Contacts.PHOTO_ID,
352 Contacts.STARRED,
353 Contacts.CONTACT_PRESENCE,
354 Contacts.CONTACT_STATUS,
355 Contacts.CONTACT_STATUS_TIMESTAMP,
356 Contacts.CONTACT_STATUS_RES_PACKAGE,
357 Contacts.CONTACT_STATUS_LABEL,
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700358 Contacts.Entity.CONTACT_ID,
359 Contacts.Entity.RAW_CONTACT_ID,
360
361 RawContacts.ACCOUNT_NAME,
362 RawContacts.ACCOUNT_TYPE,
363 RawContacts.DIRTY,
364 RawContacts.VERSION,
365 RawContacts.SOURCE_ID,
366 RawContacts.SYNC1,
367 RawContacts.SYNC2,
368 RawContacts.SYNC3,
369 RawContacts.SYNC4,
370 RawContacts.DELETED,
371 RawContacts.IS_RESTRICTED,
372 RawContacts.NAME_VERIFIED,
373
374 Contacts.Entity.DATA_ID,
375 Data.DATA1,
376 Data.DATA2,
377 Data.DATA3,
378 Data.DATA4,
379 Data.DATA5,
380 Data.DATA6,
381 Data.DATA7,
382 Data.DATA8,
383 Data.DATA9,
384 Data.DATA10,
385 Data.DATA11,
386 Data.DATA12,
387 Data.DATA13,
388 Data.DATA14,
389 Data.DATA15,
390 Data.SYNC1,
391 Data.SYNC2,
392 Data.SYNC3,
393 Data.SYNC4,
394 Data.DATA_VERSION,
395 Data.IS_PRIMARY,
396 Data.IS_SUPER_PRIMARY,
397 Data.MIMETYPE,
398 Data.RES_PACKAGE,
399
400 GroupMembership.GROUP_SOURCE_ID,
401
402 Data.PRESENCE,
Daniel Lehmann8fd7bb62010-08-13 20:50:31 -0700403 Data.CHAT_CAPABILITY,
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700404 Data.STATUS,
405 Data.STATUS_RES_PACKAGE,
406 Data.STATUS_ICON,
407 Data.STATUS_LABEL,
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700408 Data.STATUS_TIMESTAMP,
409
410 Contacts.PHOTO_URI,
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700411 };
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700412
413 public final static int NAME_RAW_CONTACT_ID = 0;
414 public final static int DISPLAY_NAME_SOURCE = 1;
415 public final static int LOOKUP_KEY = 2;
416 public final static int DISPLAY_NAME = 3;
417 public final static int PHONETIC_NAME = 4;
418 public final static int PHOTO_ID = 5;
419 public final static int STARRED = 6;
420 public final static int CONTACT_PRESENCE = 7;
421 public final static int CONTACT_STATUS = 8;
422 public final static int CONTACT_STATUS_TIMESTAMP = 9;
423 public final static int CONTACT_STATUS_RES_PACKAGE = 10;
424 public final static int CONTACT_STATUS_LABEL = 11;
425 public final static int CONTACT_ID = 12;
426 public final static int RAW_CONTACT_ID = 13;
427
428 public final static int ACCOUNT_NAME = 14;
429 public final static int ACCOUNT_TYPE = 15;
430 public final static int DIRTY = 16;
431 public final static int VERSION = 17;
432 public final static int SOURCE_ID = 18;
433 public final static int SYNC1 = 19;
434 public final static int SYNC2 = 20;
435 public final static int SYNC3 = 21;
436 public final static int SYNC4 = 22;
437 public final static int DELETED = 23;
438 public final static int IS_RESTRICTED = 24;
439 public final static int NAME_VERIFIED = 25;
440
441 public final static int DATA_ID = 26;
442 public final static int DATA1 = 27;
443 public final static int DATA2 = 28;
444 public final static int DATA3 = 29;
445 public final static int DATA4 = 30;
446 public final static int DATA5 = 31;
447 public final static int DATA6 = 32;
448 public final static int DATA7 = 33;
449 public final static int DATA8 = 34;
450 public final static int DATA9 = 35;
451 public final static int DATA10 = 36;
452 public final static int DATA11 = 37;
453 public final static int DATA12 = 38;
454 public final static int DATA13 = 39;
455 public final static int DATA14 = 40;
456 public final static int DATA15 = 41;
457 public final static int DATA_SYNC1 = 42;
458 public final static int DATA_SYNC2 = 43;
459 public final static int DATA_SYNC3 = 44;
460 public final static int DATA_SYNC4 = 45;
461 public final static int DATA_VERSION = 46;
462 public final static int IS_PRIMARY = 47;
463 public final static int IS_SUPERPRIMARY = 48;
464 public final static int MIMETYPE = 49;
465 public final static int RES_PACKAGE = 50;
466
467 public final static int GROUP_SOURCE_ID = 51;
468
469 public final static int PRESENCE = 52;
Daniel Lehmann8fd7bb62010-08-13 20:50:31 -0700470 public final static int CHAT_CAPABILITY = 53;
471 public final static int STATUS = 54;
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700472 public final static int STATUS_RES_PACKAGE = 55;
473 public final static int STATUS_ICON = 56;
474 public final static int STATUS_LABEL = 57;
475 public final static int STATUS_TIMESTAMP = 58;
476
477 public final static int PHOTO_URI = 59;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700478 }
Daniel Lehmann1316b132010-04-13 15:08:53 -0700479
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700480 private static class DirectoryQuery {
481 // Projection used for the query that loads all data for the entire contact.
482 final static String[] COLUMNS = new String[] {
483 Directory.DISPLAY_NAME,
484 Directory.PACKAGE_NAME,
485 Directory.TYPE_RESOURCE_ID,
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700486 Directory.ACCOUNT_TYPE,
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700487 Directory.ACCOUNT_NAME,
488 Directory.EXPORT_SUPPORT,
489 };
490
491 public final static int DISPLAY_NAME = 0;
492 public final static int PACKAGE_NAME = 1;
493 public final static int TYPE_RESOURCE_ID = 2;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700494 public final static int ACCOUNT_TYPE = 3;
495 public final static int ACCOUNT_NAME = 4;
496 public final static int EXPORT_SUPPORT = 5;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700497 }
498
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700499 private static class GroupQuery {
500 final static String[] COLUMNS = new String[] {
501 Groups.ACCOUNT_NAME,
502 Groups.ACCOUNT_TYPE,
503 Groups._ID,
504 Groups.TITLE,
505 Groups.AUTO_ADD,
506 Groups.FAVORITES,
507 };
508
509 public final static int ACCOUNT_NAME = 0;
510 public final static int ACCOUNT_TYPE = 1;
511 public final static int ID = 2;
512 public final static int TITLE = 3;
513 public final static int AUTO_ADD = 4;
514 public final static int FAVORITES = 5;
515 }
516
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700517 private final class LoadContactTask extends AsyncTask<Void, Void, Result> {
Daniel Lehmann1316b132010-04-13 15:08:53 -0700518
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700519 @Override
520 protected Result doInBackground(Void... args) {
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700521 try {
522 final ContentResolver resolver = getContext().getContentResolver();
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700523 final Uri uriCurrentFormat = ensureIsContactUri(resolver, mLookupUri);
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700524 Result result = loadContactEntity(resolver, uriCurrentFormat);
Dmitri Plotnikov217245c2010-09-18 13:04:50 -0700525 if (result != Result.NOT_FOUND) {
526 if (result.isDirectoryEntry()) {
527 loadDirectoryMetaData(result);
528 } else if (mLoadGroupMetaData) {
529 loadGroupMetaData(result);
530 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800531 loadPhotoBinaryData(result);
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700532 }
533 return result;
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700534 } catch (Exception e) {
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700535 Log.e(TAG, "Error loading the contact: " + mLookupUri, e);
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700536 return Result.ERROR;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700537 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700538 }
539
540 /**
Daniel Lehmann1316b132010-04-13 15:08:53 -0700541 * Transforms the given Uri and returns a Lookup-Uri that represents the contact.
542 * For legacy contacts, a raw-contact lookup is performed.
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700543 * @param resolver
Daniel Lehmann1316b132010-04-13 15:08:53 -0700544 */
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700545 private Uri ensureIsContactUri(final ContentResolver resolver, final Uri uri) {
Daniel Lehmann1316b132010-04-13 15:08:53 -0700546 if (uri == null) throw new IllegalArgumentException("uri must not be null");
547
548 final String authority = uri.getAuthority();
549
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700550 // Current Style Uri?
Daniel Lehmann1316b132010-04-13 15:08:53 -0700551 if (ContactsContract.AUTHORITY.equals(authority)) {
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700552 final String type = resolver.getType(uri);
553 // Contact-Uri? Good, return it
554 if (Contacts.CONTENT_ITEM_TYPE.equals(type)) {
555 return uri;
556 }
557
558 // RawContact-Uri? Transform it to ContactUri
559 if (RawContacts.CONTENT_ITEM_TYPE.equals(type)) {
560 final long rawContactId = ContentUris.parseId(uri);
561 return RawContacts.getContactLookupUri(getContext().getContentResolver(),
562 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId));
563 }
564
565 // Anything else? We don't know what this is
566 throw new IllegalArgumentException("uri format is unknown");
Daniel Lehmann1316b132010-04-13 15:08:53 -0700567 }
568
569 // Legacy Style? Convert to RawContact
570 final String OBSOLETE_AUTHORITY = "contacts";
571 if (OBSOLETE_AUTHORITY.equals(authority)) {
572 // Legacy Format. Convert to RawContact-Uri and then lookup the contact
573 final long rawContactId = ContentUris.parseId(uri);
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700574 return RawContacts.getContactLookupUri(resolver,
Daniel Lehmann1316b132010-04-13 15:08:53 -0700575 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId));
576 }
577
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700578 throw new IllegalArgumentException("uri authority is unknown");
Daniel Lehmann1316b132010-04-13 15:08:53 -0700579 }
580
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700581 private Result loadContactEntity(ContentResolver resolver, Uri contactUri) {
582 Uri entityUri = Uri.withAppendedPath(contactUri, Contacts.Entity.CONTENT_DIRECTORY);
583 Cursor cursor = resolver.query(entityUri, ContactQuery.COLUMNS, null, null,
584 Contacts.Entity.RAW_CONTACT_ID);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700585 if (cursor == null) {
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700586 Log.e(TAG, "No cursor returned in loadContactEntity");
587 return Result.NOT_FOUND;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700588 }
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700589
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700590 try {
591 if (!cursor.moveToFirst()) {
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700592 cursor.close();
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700593 return Result.NOT_FOUND;
594 }
595
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700596 long currentRawContactId = -1;
597 Entity entity = null;
598 Result result = loadContactHeaderData(cursor, contactUri);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700599 ArrayList<Entity> entities = result.getEntities();
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700600 HashMap<Long, DataStatus> statuses = result.getStatuses();
601 for (; !cursor.isAfterLast(); cursor.moveToNext()) {
602 long rawContactId = cursor.getLong(ContactQuery.RAW_CONTACT_ID);
603 if (rawContactId != currentRawContactId) {
604 currentRawContactId = rawContactId;
605 entity = new android.content.Entity(loadRawContact(cursor));
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700606 entities.add(entity);
607 }
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700608 if (!cursor.isNull(ContactQuery.DATA_ID)) {
609 ContentValues data = loadData(cursor);
610 entity.addSubValue(ContactsContract.Data.CONTENT_URI, data);
611
612 if (!cursor.isNull(ContactQuery.PRESENCE)
613 || !cursor.isNull(ContactQuery.STATUS)) {
614 final DataStatus status = new DataStatus(cursor);
615 final long dataId = cursor.getLong(ContactQuery.DATA_ID);
616 statuses.put(dataId, status);
617 }
618 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700619 }
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700620
621 return result;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700622 } finally {
623 cursor.close();
624 }
625 }
626
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700627 /**
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800628 * Looks for the photo data item in entities. If found, creates a new Bitmap instance. If
629 * not found, returns null
630 */
631 private void loadPhotoBinaryData(Result contactData) {
632 final long photoId = contactData.getPhotoId();
633 if (photoId <= 0) {
634 // No photo ID
635 return;
636 }
637
638 for (Entity entity : contactData.getEntities()) {
639 for (NamedContentValues subValue : entity.getSubValues()) {
640 final ContentValues entryValues = subValue.values;
641 final long dataId = entryValues.getAsLong(Data._ID);
642 if (dataId == photoId) {
643 final String mimeType = entryValues.getAsString(Data.MIMETYPE);
644 // Correct Data Id but incorrect MimeType? Don't load
645 if (!Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
646 return;
647 }
648 contactData.setPhotoBinaryData(entryValues.getAsByteArray(Photo.PHOTO));
649 break;
650 }
651 }
652 }
653 }
654
655 /**
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700656 * Extracts Contact level columns from the cursor.
657 */
658 private Result loadContactHeaderData(final Cursor cursor, Uri contactUri) {
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700659 final String directoryParameter =
660 contactUri.getQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY);
661 final long directoryId = directoryParameter == null
662 ? Directory.DEFAULT
663 : Long.parseLong(directoryParameter);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700664 final long contactId = cursor.getLong(ContactQuery.CONTACT_ID);
665 final String lookupKey = cursor.getString(ContactQuery.LOOKUP_KEY);
666 final long nameRawContactId = cursor.getLong(ContactQuery.NAME_RAW_CONTACT_ID);
667 final int displayNameSource = cursor.getInt(ContactQuery.DISPLAY_NAME_SOURCE);
668 final String displayName = cursor.getString(ContactQuery.DISPLAY_NAME);
669 final String phoneticName = cursor.getString(ContactQuery.PHONETIC_NAME);
670 final long photoId = cursor.getLong(ContactQuery.PHOTO_ID);
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700671 final String photoUri = cursor.getString(ContactQuery.PHOTO_URI);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700672 final boolean starred = cursor.getInt(ContactQuery.STARRED) != 0;
673 final Integer presence = cursor.isNull(ContactQuery.CONTACT_PRESENCE)
674 ? null
675 : cursor.getInt(ContactQuery.CONTACT_PRESENCE);
676 final String status = cursor.getString(ContactQuery.CONTACT_STATUS);
677 final Long statusTimestamp = cursor.isNull(ContactQuery.CONTACT_STATUS_TIMESTAMP)
678 ? null
679 : cursor.getLong(ContactQuery.CONTACT_STATUS_TIMESTAMP);
680 final Integer statusLabel = cursor.isNull(ContactQuery.CONTACT_STATUS_LABEL)
681 ? null
682 : cursor.getInt(ContactQuery.CONTACT_STATUS_LABEL);
683 final String statusResPackage = cursor.getString(
684 ContactQuery.CONTACT_STATUS_RES_PACKAGE);
685
Dmitri Plotnikov1536ea12010-10-29 11:51:05 -0700686 Uri lookupUri;
687 if (directoryId == Directory.DEFAULT || directoryId == Directory.LOCAL_INVISIBLE) {
688 lookupUri = ContentUris.withAppendedId(
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700689 Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey), contactId);
Dmitri Plotnikov1536ea12010-10-29 11:51:05 -0700690 } else {
691 lookupUri = contactUri;
692 }
693
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700694 return new Result(contactUri, lookupUri, directoryId, lookupKey, contactId,
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700695 nameRawContactId, displayNameSource, photoId, photoUri, displayName,
696 phoneticName, starred, presence, status, statusTimestamp, statusLabel,
697 statusResPackage);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700698 }
699
700 /**
701 * Extracts RawContact level columns from the cursor.
702 */
703 private ContentValues loadRawContact(Cursor cursor) {
704 ContentValues cv = new ContentValues();
705
706 cv.put(RawContacts._ID, cursor.getLong(ContactQuery.RAW_CONTACT_ID));
707
708 cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_NAME);
709 cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_TYPE);
710 cursorColumnToContentValues(cursor, cv, ContactQuery.DIRTY);
711 cursorColumnToContentValues(cursor, cv, ContactQuery.VERSION);
712 cursorColumnToContentValues(cursor, cv, ContactQuery.SOURCE_ID);
713 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC1);
714 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC2);
715 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC3);
716 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC4);
717 cursorColumnToContentValues(cursor, cv, ContactQuery.DELETED);
718 cursorColumnToContentValues(cursor, cv, ContactQuery.CONTACT_ID);
719 cursorColumnToContentValues(cursor, cv, ContactQuery.STARRED);
720 cursorColumnToContentValues(cursor, cv, ContactQuery.IS_RESTRICTED);
721 cursorColumnToContentValues(cursor, cv, ContactQuery.NAME_VERIFIED);
722
723 return cv;
724 }
725
726 /**
727 * Extracts Data level columns from the cursor.
728 */
729 private ContentValues loadData(Cursor cursor) {
730 ContentValues cv = new ContentValues();
731
732 cv.put(Data._ID, cursor.getLong(ContactQuery.DATA_ID));
733
734 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA1);
735 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA2);
736 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA3);
737 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA4);
738 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA5);
739 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA6);
740 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA7);
741 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA8);
742 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA9);
743 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA10);
744 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA11);
745 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA12);
746 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA13);
747 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA14);
748 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA15);
749 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC1);
750 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC2);
751 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC3);
752 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC4);
753 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_VERSION);
754 cursorColumnToContentValues(cursor, cv, ContactQuery.IS_PRIMARY);
755 cursorColumnToContentValues(cursor, cv, ContactQuery.IS_SUPERPRIMARY);
756 cursorColumnToContentValues(cursor, cv, ContactQuery.MIMETYPE);
757 cursorColumnToContentValues(cursor, cv, ContactQuery.RES_PACKAGE);
758 cursorColumnToContentValues(cursor, cv, ContactQuery.GROUP_SOURCE_ID);
Daniel Lehmann8fd7bb62010-08-13 20:50:31 -0700759 cursorColumnToContentValues(cursor, cv, ContactQuery.CHAT_CAPABILITY);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700760
761 return cv;
762 }
763
764 private void cursorColumnToContentValues(
765 Cursor cursor, ContentValues values, int index) {
766 switch (cursor.getType(index)) {
767 case Cursor.FIELD_TYPE_NULL:
768 // don't put anything in the content values
769 break;
770 case Cursor.FIELD_TYPE_INTEGER:
771 values.put(ContactQuery.COLUMNS[index], cursor.getLong(index));
772 break;
773 case Cursor.FIELD_TYPE_STRING:
774 values.put(ContactQuery.COLUMNS[index], cursor.getString(index));
775 break;
776 case Cursor.FIELD_TYPE_BLOB:
777 values.put(ContactQuery.COLUMNS[index], cursor.getBlob(index));
778 break;
779 default:
780 throw new IllegalStateException("Invalid or unhandled data type");
781 }
782 }
783
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700784 private void loadDirectoryMetaData(Result result) {
785 long directoryId = result.getDirectoryId();
786
787 Cursor cursor = getContext().getContentResolver().query(
788 ContentUris.withAppendedId(Directory.CONTENT_URI, directoryId),
789 DirectoryQuery.COLUMNS, null, null, null);
790 if (cursor == null) {
791 return;
792 }
793 try {
794 if (cursor.moveToFirst()) {
795 final String displayName = cursor.getString(DirectoryQuery.DISPLAY_NAME);
796 final String packageName = cursor.getString(DirectoryQuery.PACKAGE_NAME);
797 final int typeResourceId = cursor.getInt(DirectoryQuery.TYPE_RESOURCE_ID);
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700798 final String accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE);
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700799 final String accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME);
800 final int exportSupport = cursor.getInt(DirectoryQuery.EXPORT_SUPPORT);
801 String directoryType = null;
802 if (!TextUtils.isEmpty(packageName)) {
803 PackageManager pm = getContext().getPackageManager();
804 try {
805 Resources resources = pm.getResourcesForApplication(packageName);
806 directoryType = resources.getString(typeResourceId);
807 } catch (NameNotFoundException e) {
808 Log.w(TAG, "Contact directory resource not found: "
809 + packageName + "." + typeResourceId);
810 }
811 }
812
813 result.setDirectoryMetaData(
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700814 displayName, directoryType, accountType, accountName, exportSupport);
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700815 }
816 } finally {
817 cursor.close();
818 }
819 }
820
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700821 /**
822 * Loads groups meta-data for all groups associated with all constituent raw contacts'
823 * accounts.
824 */
825 private void loadGroupMetaData(Result result) {
826 StringBuilder selection = new StringBuilder();
827 ArrayList<String> selectionArgs = new ArrayList<String>();
828 for (Entity entity : result.mEntities) {
829 ContentValues values = entity.getEntityValues();
830 String accountName = values.getAsString(RawContacts.ACCOUNT_NAME);
831 String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
832 if (accountName != null && accountType != null) {
833 if (selection.length() != 0) {
834 selection.append(" OR ");
835 }
836 selection.append(
837 "(" + Groups.ACCOUNT_NAME + "=? AND " + Groups.ACCOUNT_TYPE + "=?)");
838 selectionArgs.add(accountName);
839 selectionArgs.add(accountType);
840 }
841 }
842 Cursor cursor = getContext().getContentResolver().query(Groups.CONTENT_URI,
843 GroupQuery.COLUMNS, selection.toString(), selectionArgs.toArray(new String[0]),
844 null);
845 try {
846 while (cursor.moveToNext()) {
847 final String accountName = cursor.getString(GroupQuery.ACCOUNT_NAME);
848 final String accountType = cursor.getString(GroupQuery.ACCOUNT_TYPE);
849 final long groupId = cursor.getLong(GroupQuery.ID);
850 final String title = cursor.getString(GroupQuery.TITLE);
851 final boolean defaultGroup = cursor.isNull(GroupQuery.AUTO_ADD)
852 ? false
853 : cursor.getInt(GroupQuery.AUTO_ADD) != 0;
854 final boolean favorites = cursor.isNull(GroupQuery.FAVORITES)
855 ? false
856 : cursor.getInt(GroupQuery.FAVORITES) != 0;
857
Dmitri Plotnikove843f912010-09-16 15:21:48 -0700858 result.addGroupMetaData(new GroupMetaData(
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700859 accountName, accountType, groupId, title, defaultGroup, favorites));
860 }
861 } finally {
862 cursor.close();
863 }
864 }
865
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700866 @Override
867 protected void onPostExecute(Result result) {
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -0800868 unregisterObserver();
869
Daniel Lehmann1316b132010-04-13 15:08:53 -0700870 // The creator isn't interested in any further updates
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -0800871 if (mDestroyed || result == null) {
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700872 return;
873 }
874
875 mContact = result;
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700876
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -0800877 if (result != Result.ERROR && result != Result.NOT_FOUND) {
878 mLookupUri = result.getLookupUri();
879
880 if (!result.isDirectoryEntry()) {
881 Log.i(TAG, "Registering content observer for " + mLookupUri);
882 if (mObserver == null) {
883 mObserver = new ForceLoadContentObserver();
884 }
885 getContext().getContentResolver().registerContentObserver(
886 mLookupUri, true, mObserver);
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700887 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800888
889 if (mContact.getPhotoBinaryData() == null && mContact.getPhotoUri() != null) {
890 mContact.setLoadingPhoto(true);
891 new AsyncPhotoLoader().execute(mContact.getPhotoUri());
892 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700893 }
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -0800894
895 deliverResult(mContact);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700896 }
897 }
898
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800899 private class AsyncPhotoLoader extends AsyncTask<String, Void, byte[]> {
900
901 private static final int BUFFER_SIZE = 1024*16;
902
903 @Override
904 protected byte[] doInBackground(String... params) {
905 Uri uri = Uri.parse(params[0]);
906 byte[] data = null;
907 try {
908 InputStream is = getContext().getContentResolver().openInputStream(uri);
909 if (is != null) {
910 ByteArrayOutputStream baos = new ByteArrayOutputStream();
911 try {
912 byte[] mBuffer = new byte[BUFFER_SIZE];
913
914 int size;
915 while ((size = is.read(mBuffer)) != -1) {
916 baos.write(mBuffer, 0, size);
917 }
918 data = baos.toByteArray();
919 } finally {
920 is.close();
921 }
922 } else {
923 Log.v(TAG, "Cannot load photo " + uri);
924 }
925 } catch (IOException e) {
926 Log.e(TAG, "Cannot load photo " + uri, e);
927 }
928
929 return data;
930 }
931
932 @Override
933 protected void onPostExecute(byte[] data) {
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -0800934 if (mContact != null) {
935 mContact.setPhotoBinaryData(data);
936 mContact.setLoadingPhoto(false);
937 deliverResult(mContact);
938 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800939 }
940 }
941
Daniel Lehmann3a120772010-06-21 16:21:35 -0700942 private void unregisterObserver() {
943 if (mObserver != null) {
944 getContext().getContentResolver().unregisterContentObserver(mObserver);
945 mObserver = null;
946 }
947 }
948
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700949 public ContactLoader(Context context, Uri lookupUri) {
Dmitri Plotnikove843f912010-09-16 15:21:48 -0700950 this(context, lookupUri, false);
951 }
952
953 public ContactLoader(Context context, Uri lookupUri, boolean loadGroupMetaData) {
Daniel Lehmann74a2dc52010-04-15 16:52:33 -0700954 super(context);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700955 mLookupUri = lookupUri;
Dmitri Plotnikove843f912010-09-16 15:21:48 -0700956 mLoadGroupMetaData = loadGroupMetaData;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700957 }
958
Dmitri Plotnikov5a30d9a2010-11-23 14:59:50 -0800959 public Uri getLookupUri() {
960 return mLookupUri;
961 }
962
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700963 @Override
Dianne Hackbornc04fc272010-12-20 23:13:10 -0800964 protected void onStartLoading() {
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700965 if (mContact != null) {
Daniel Lehmanncbcc4492010-04-12 18:03:54 -0700966 deliverResult(mContact);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700967 } else {
968 forceLoad();
969 }
970 }
971
972 @Override
Dianne Hackbornc04fc272010-12-20 23:13:10 -0800973 protected void onForceLoad() {
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700974 final LoadContactTask task = new LoadContactTask();
Daniel Lehmann74a2dc52010-04-15 16:52:33 -0700975 task.execute((Void[])null);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700976 }
977
978 @Override
Dianne Hackbornc04fc272010-12-20 23:13:10 -0800979 protected void onStopLoading() {
Daniel Lehmann222744e2010-07-12 16:28:06 -0700980 unregisterObserver();
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700981 mContact = null;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700982 }
983
Dianne Hackbornc04fc272010-12-20 23:13:10 -0800984 protected void onReset() {
Dianne Hackborn4ef95cc2010-12-16 00:44:33 -0800985 unregisterObserver();
986 mContact = null;
987 mDestroyed = true;
988 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700989}