blob: f4baf3b8c991b1303b6c74d652238501ddd9573a [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 Plotnikov7cee7742011-01-13 17:11:06 -0800171 private Result(Result from) {
172 mLookupUri = from.mLookupUri;
173 mUri = from.mUri;
174 mDirectoryId = from.mDirectoryId;
175 mLookupKey = from.mLookupKey;
176 mId = from.mId;
177 mNameRawContactId = from.mNameRawContactId;
178 mDisplayNameSource = from.mDisplayNameSource;
179 mPhotoId = from.mPhotoId;
180 mPhotoUri = from.mPhotoUri;
181 mDisplayName = from.mDisplayName;
182 mPhoneticName = from.mPhoneticName;
183 mStarred = from.mStarred;
184 mPresence = from.mPresence;
185 mEntities = from.mEntities;
186 mStatuses = from.mStatuses;
187 mStatus = from.mStatus;
188 mStatusTimestamp = from.mStatusTimestamp;
189 mStatusLabel = from.mStatusLabel;
190 mStatusResPackage = from.mStatusResPackage;
191
192 mDirectoryDisplayName = from.mDirectoryDisplayName;
193 mDirectoryType = from.mDirectoryType;
194 mDirectoryAccountType = from.mDirectoryAccountType;
195 mDirectoryAccountName = from.mDirectoryAccountName;
196 mDirectoryExportSupport = from.mDirectoryExportSupport;
197
198 mGroups = from.mGroups;
199
200 mLoadingPhoto = from.mLoadingPhoto;
201 mPhotoBinaryData = from.mPhotoBinaryData;
202 }
203
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700204 /**
205 * @param exportSupport See {@link Directory#EXPORT_SUPPORT}.
206 */
Daniel Lehmann1ad4d1b2010-10-18 19:20:41 -0700207 private void setDirectoryMetaData(String displayName, String directoryType,
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700208 String accountType, String accountName, int exportSupport) {
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700209 mDirectoryDisplayName = displayName;
210 mDirectoryType = directoryType;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700211 mDirectoryAccountType = accountType;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700212 mDirectoryAccountName = accountName;
213 mDirectoryExportSupport = exportSupport;
214 }
215
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800216 private void setLoadingPhoto(boolean flag) {
217 mLoadingPhoto = flag;
218 }
219
220 private void setPhotoBinaryData(byte[] photoBinaryData) {
221 mPhotoBinaryData = photoBinaryData;
222 }
223
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700224 public Uri getLookupUri() {
225 return mLookupUri;
226 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800227
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700228 public String getLookupKey() {
229 return mLookupKey;
230 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800231
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700232 public Uri getUri() {
233 return mUri;
234 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800235
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700236 public long getId() {
237 return mId;
238 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800239
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700240 public long getNameRawContactId() {
241 return mNameRawContactId;
242 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800243
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700244 public int getDisplayNameSource() {
245 return mDisplayNameSource;
246 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800247
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700248 public long getPhotoId() {
249 return mPhotoId;
250 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800251
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700252 public String getPhotoUri() {
253 return mPhotoUri;
254 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800255
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700256 public String getDisplayName() {
257 return mDisplayName;
258 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800259
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700260 public String getPhoneticName() {
261 return mPhoneticName;
262 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800263
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700264 public boolean getStarred() {
265 return mStarred;
266 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800267
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700268 public Integer getPresence() {
269 return mPresence;
270 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800271
Daniel Lehmann2d4f7592010-10-12 23:31:31 -0700272 public String getSocialSnippet() {
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700273 return mStatus;
274 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800275
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700276 public Long getStatusTimestamp() {
277 return mStatusTimestamp;
278 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800279
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700280 public Integer getStatusLabel() {
281 return mStatusLabel;
282 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800283
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700284 public String getStatusResPackage() {
285 return mStatusResPackage;
286 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800287
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700288 public ArrayList<Entity> getEntities() {
289 return mEntities;
290 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800291
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700292 public HashMap<Long, DataStatus> getStatuses() {
293 return mStatuses;
294 }
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700295
296 public long getDirectoryId() {
297 return mDirectoryId;
298 }
299
300 public boolean isDirectoryEntry() {
Dmitri Plotnikov5f72c1f2010-09-01 21:21:04 -0700301 return mDirectoryId != -1 && mDirectoryId != Directory.DEFAULT
302 && mDirectoryId != Directory.LOCAL_INVISIBLE;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700303 }
304
305 public int getDirectoryExportSupport() {
306 return mDirectoryExportSupport;
307 }
308
309 public String getDirectoryDisplayName() {
310 return mDirectoryDisplayName;
311 }
312
313 public String getDirectoryType() {
314 return mDirectoryType;
315 }
316
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700317 public String getDirectoryAccountType() {
318 return mDirectoryAccountType;
319 }
320
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700321 public String getDirectoryAccountName() {
322 return mDirectoryAccountName;
323 }
324
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800325 public boolean isLoadingPhoto() {
326 return mLoadingPhoto;
327 }
328
329 public byte[] getPhotoBinaryData() {
330 return mPhotoBinaryData;
331 }
332
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700333 public ArrayList<ContentValues> getContentValues() {
334 if (mEntities.size() != 1) {
335 throw new IllegalStateException(
336 "Cannot extract content values from an aggregated contact");
337 }
338
339 Entity entity = mEntities.get(0);
340 ArrayList<ContentValues> result = new ArrayList<ContentValues>();
341 ArrayList<NamedContentValues> subValues = entity.getSubValues();
342 if (subValues != null) {
343 int size = subValues.size();
344 for (int i = 0; i < size; i++) {
345 NamedContentValues pair = subValues.get(i);
346 if (Data.CONTENT_URI.equals(pair.uri)) {
347 result.add(pair.values);
348 }
349 }
350 }
Dmitri Plotnikov40ec3a82010-11-10 11:25:33 -0800351
352 // If the photo was loaded using the URI, create an entry for the photo
353 // binary data.
354 if (mPhotoId == 0 && mPhotoBinaryData != null) {
355 ContentValues photo = new ContentValues();
356 photo.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
357 photo.put(Photo.PHOTO, mPhotoBinaryData);
358 result.add(photo);
359 }
360
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700361 return result;
362 }
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700363
Daniel Lehmann1ad4d1b2010-10-18 19:20:41 -0700364 private void addGroupMetaData(GroupMetaData group) {
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700365 if (mGroups == null) {
Dmitri Plotnikove843f912010-09-16 15:21:48 -0700366 mGroups = new ArrayList<GroupMetaData>();
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700367 }
368 mGroups.add(group);
369 }
370
Dmitri Plotnikove843f912010-09-16 15:21:48 -0700371 public List<GroupMetaData> getGroupMetaData() {
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700372 return mGroups;
373 }
374 }
375
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700376 private static class ContactQuery {
377 // Projection used for the query that loads all data for the entire contact.
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700378 final static String[] COLUMNS = new String[] {
379 Contacts.NAME_RAW_CONTACT_ID,
380 Contacts.DISPLAY_NAME_SOURCE,
381 Contacts.LOOKUP_KEY,
382 Contacts.DISPLAY_NAME,
383 Contacts.PHONETIC_NAME,
384 Contacts.PHOTO_ID,
385 Contacts.STARRED,
386 Contacts.CONTACT_PRESENCE,
387 Contacts.CONTACT_STATUS,
388 Contacts.CONTACT_STATUS_TIMESTAMP,
389 Contacts.CONTACT_STATUS_RES_PACKAGE,
390 Contacts.CONTACT_STATUS_LABEL,
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700391 Contacts.Entity.CONTACT_ID,
392 Contacts.Entity.RAW_CONTACT_ID,
393
394 RawContacts.ACCOUNT_NAME,
395 RawContacts.ACCOUNT_TYPE,
396 RawContacts.DIRTY,
397 RawContacts.VERSION,
398 RawContacts.SOURCE_ID,
399 RawContacts.SYNC1,
400 RawContacts.SYNC2,
401 RawContacts.SYNC3,
402 RawContacts.SYNC4,
403 RawContacts.DELETED,
404 RawContacts.IS_RESTRICTED,
405 RawContacts.NAME_VERIFIED,
406
407 Contacts.Entity.DATA_ID,
408 Data.DATA1,
409 Data.DATA2,
410 Data.DATA3,
411 Data.DATA4,
412 Data.DATA5,
413 Data.DATA6,
414 Data.DATA7,
415 Data.DATA8,
416 Data.DATA9,
417 Data.DATA10,
418 Data.DATA11,
419 Data.DATA12,
420 Data.DATA13,
421 Data.DATA14,
422 Data.DATA15,
423 Data.SYNC1,
424 Data.SYNC2,
425 Data.SYNC3,
426 Data.SYNC4,
427 Data.DATA_VERSION,
428 Data.IS_PRIMARY,
429 Data.IS_SUPER_PRIMARY,
430 Data.MIMETYPE,
431 Data.RES_PACKAGE,
432
433 GroupMembership.GROUP_SOURCE_ID,
434
435 Data.PRESENCE,
Daniel Lehmann8fd7bb62010-08-13 20:50:31 -0700436 Data.CHAT_CAPABILITY,
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700437 Data.STATUS,
438 Data.STATUS_RES_PACKAGE,
439 Data.STATUS_ICON,
440 Data.STATUS_LABEL,
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700441 Data.STATUS_TIMESTAMP,
442
443 Contacts.PHOTO_URI,
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700444 };
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700445
446 public final static int NAME_RAW_CONTACT_ID = 0;
447 public final static int DISPLAY_NAME_SOURCE = 1;
448 public final static int LOOKUP_KEY = 2;
449 public final static int DISPLAY_NAME = 3;
450 public final static int PHONETIC_NAME = 4;
451 public final static int PHOTO_ID = 5;
452 public final static int STARRED = 6;
453 public final static int CONTACT_PRESENCE = 7;
454 public final static int CONTACT_STATUS = 8;
455 public final static int CONTACT_STATUS_TIMESTAMP = 9;
456 public final static int CONTACT_STATUS_RES_PACKAGE = 10;
457 public final static int CONTACT_STATUS_LABEL = 11;
458 public final static int CONTACT_ID = 12;
459 public final static int RAW_CONTACT_ID = 13;
460
461 public final static int ACCOUNT_NAME = 14;
462 public final static int ACCOUNT_TYPE = 15;
463 public final static int DIRTY = 16;
464 public final static int VERSION = 17;
465 public final static int SOURCE_ID = 18;
466 public final static int SYNC1 = 19;
467 public final static int SYNC2 = 20;
468 public final static int SYNC3 = 21;
469 public final static int SYNC4 = 22;
470 public final static int DELETED = 23;
471 public final static int IS_RESTRICTED = 24;
472 public final static int NAME_VERIFIED = 25;
473
474 public final static int DATA_ID = 26;
475 public final static int DATA1 = 27;
476 public final static int DATA2 = 28;
477 public final static int DATA3 = 29;
478 public final static int DATA4 = 30;
479 public final static int DATA5 = 31;
480 public final static int DATA6 = 32;
481 public final static int DATA7 = 33;
482 public final static int DATA8 = 34;
483 public final static int DATA9 = 35;
484 public final static int DATA10 = 36;
485 public final static int DATA11 = 37;
486 public final static int DATA12 = 38;
487 public final static int DATA13 = 39;
488 public final static int DATA14 = 40;
489 public final static int DATA15 = 41;
490 public final static int DATA_SYNC1 = 42;
491 public final static int DATA_SYNC2 = 43;
492 public final static int DATA_SYNC3 = 44;
493 public final static int DATA_SYNC4 = 45;
494 public final static int DATA_VERSION = 46;
495 public final static int IS_PRIMARY = 47;
496 public final static int IS_SUPERPRIMARY = 48;
497 public final static int MIMETYPE = 49;
498 public final static int RES_PACKAGE = 50;
499
500 public final static int GROUP_SOURCE_ID = 51;
501
502 public final static int PRESENCE = 52;
Daniel Lehmann8fd7bb62010-08-13 20:50:31 -0700503 public final static int CHAT_CAPABILITY = 53;
504 public final static int STATUS = 54;
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700505 public final static int STATUS_RES_PACKAGE = 55;
506 public final static int STATUS_ICON = 56;
507 public final static int STATUS_LABEL = 57;
508 public final static int STATUS_TIMESTAMP = 58;
509
510 public final static int PHOTO_URI = 59;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700511 }
Daniel Lehmann1316b132010-04-13 15:08:53 -0700512
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700513 private static class DirectoryQuery {
514 // Projection used for the query that loads all data for the entire contact.
515 final static String[] COLUMNS = new String[] {
516 Directory.DISPLAY_NAME,
517 Directory.PACKAGE_NAME,
518 Directory.TYPE_RESOURCE_ID,
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700519 Directory.ACCOUNT_TYPE,
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700520 Directory.ACCOUNT_NAME,
521 Directory.EXPORT_SUPPORT,
522 };
523
524 public final static int DISPLAY_NAME = 0;
525 public final static int PACKAGE_NAME = 1;
526 public final static int TYPE_RESOURCE_ID = 2;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700527 public final static int ACCOUNT_TYPE = 3;
528 public final static int ACCOUNT_NAME = 4;
529 public final static int EXPORT_SUPPORT = 5;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700530 }
531
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700532 private static class GroupQuery {
533 final static String[] COLUMNS = new String[] {
534 Groups.ACCOUNT_NAME,
535 Groups.ACCOUNT_TYPE,
536 Groups._ID,
537 Groups.TITLE,
538 Groups.AUTO_ADD,
539 Groups.FAVORITES,
540 };
541
542 public final static int ACCOUNT_NAME = 0;
543 public final static int ACCOUNT_TYPE = 1;
544 public final static int ID = 2;
545 public final static int TITLE = 3;
546 public final static int AUTO_ADD = 4;
547 public final static int FAVORITES = 5;
548 }
549
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700550 private final class LoadContactTask extends AsyncTask<Void, Void, Result> {
Daniel Lehmann1316b132010-04-13 15:08:53 -0700551
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700552 @Override
553 protected Result doInBackground(Void... args) {
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700554 try {
555 final ContentResolver resolver = getContext().getContentResolver();
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700556 final Uri uriCurrentFormat = ensureIsContactUri(resolver, mLookupUri);
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700557 Result result = loadContactEntity(resolver, uriCurrentFormat);
Dmitri Plotnikov217245c2010-09-18 13:04:50 -0700558 if (result != Result.NOT_FOUND) {
559 if (result.isDirectoryEntry()) {
560 loadDirectoryMetaData(result);
561 } else if (mLoadGroupMetaData) {
562 loadGroupMetaData(result);
563 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800564 loadPhotoBinaryData(result);
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700565 }
566 return result;
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700567 } catch (Exception e) {
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700568 Log.e(TAG, "Error loading the contact: " + mLookupUri, e);
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700569 return Result.ERROR;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700570 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700571 }
572
573 /**
Daniel Lehmann1316b132010-04-13 15:08:53 -0700574 * Transforms the given Uri and returns a Lookup-Uri that represents the contact.
575 * For legacy contacts, a raw-contact lookup is performed.
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700576 * @param resolver
Daniel Lehmann1316b132010-04-13 15:08:53 -0700577 */
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700578 private Uri ensureIsContactUri(final ContentResolver resolver, final Uri uri) {
Daniel Lehmann1316b132010-04-13 15:08:53 -0700579 if (uri == null) throw new IllegalArgumentException("uri must not be null");
580
581 final String authority = uri.getAuthority();
582
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700583 // Current Style Uri?
Daniel Lehmann1316b132010-04-13 15:08:53 -0700584 if (ContactsContract.AUTHORITY.equals(authority)) {
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700585 final String type = resolver.getType(uri);
586 // Contact-Uri? Good, return it
587 if (Contacts.CONTENT_ITEM_TYPE.equals(type)) {
588 return uri;
589 }
590
591 // RawContact-Uri? Transform it to ContactUri
592 if (RawContacts.CONTENT_ITEM_TYPE.equals(type)) {
593 final long rawContactId = ContentUris.parseId(uri);
594 return RawContacts.getContactLookupUri(getContext().getContentResolver(),
595 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId));
596 }
597
598 // Anything else? We don't know what this is
599 throw new IllegalArgumentException("uri format is unknown");
Daniel Lehmann1316b132010-04-13 15:08:53 -0700600 }
601
602 // Legacy Style? Convert to RawContact
603 final String OBSOLETE_AUTHORITY = "contacts";
604 if (OBSOLETE_AUTHORITY.equals(authority)) {
605 // Legacy Format. Convert to RawContact-Uri and then lookup the contact
606 final long rawContactId = ContentUris.parseId(uri);
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700607 return RawContacts.getContactLookupUri(resolver,
Daniel Lehmann1316b132010-04-13 15:08:53 -0700608 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId));
609 }
610
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700611 throw new IllegalArgumentException("uri authority is unknown");
Daniel Lehmann1316b132010-04-13 15:08:53 -0700612 }
613
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700614 private Result loadContactEntity(ContentResolver resolver, Uri contactUri) {
615 Uri entityUri = Uri.withAppendedPath(contactUri, Contacts.Entity.CONTENT_DIRECTORY);
616 Cursor cursor = resolver.query(entityUri, ContactQuery.COLUMNS, null, null,
617 Contacts.Entity.RAW_CONTACT_ID);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700618 if (cursor == null) {
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700619 Log.e(TAG, "No cursor returned in loadContactEntity");
620 return Result.NOT_FOUND;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700621 }
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700622
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700623 try {
624 if (!cursor.moveToFirst()) {
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700625 cursor.close();
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700626 return Result.NOT_FOUND;
627 }
628
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700629 long currentRawContactId = -1;
630 Entity entity = null;
631 Result result = loadContactHeaderData(cursor, contactUri);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700632 ArrayList<Entity> entities = result.getEntities();
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700633 HashMap<Long, DataStatus> statuses = result.getStatuses();
634 for (; !cursor.isAfterLast(); cursor.moveToNext()) {
635 long rawContactId = cursor.getLong(ContactQuery.RAW_CONTACT_ID);
636 if (rawContactId != currentRawContactId) {
637 currentRawContactId = rawContactId;
638 entity = new android.content.Entity(loadRawContact(cursor));
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700639 entities.add(entity);
640 }
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700641 if (!cursor.isNull(ContactQuery.DATA_ID)) {
642 ContentValues data = loadData(cursor);
643 entity.addSubValue(ContactsContract.Data.CONTENT_URI, data);
644
645 if (!cursor.isNull(ContactQuery.PRESENCE)
646 || !cursor.isNull(ContactQuery.STATUS)) {
647 final DataStatus status = new DataStatus(cursor);
648 final long dataId = cursor.getLong(ContactQuery.DATA_ID);
649 statuses.put(dataId, status);
650 }
651 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700652 }
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700653
654 return result;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700655 } finally {
656 cursor.close();
657 }
658 }
659
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700660 /**
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800661 * Looks for the photo data item in entities. If found, creates a new Bitmap instance. If
662 * not found, returns null
663 */
664 private void loadPhotoBinaryData(Result contactData) {
665 final long photoId = contactData.getPhotoId();
666 if (photoId <= 0) {
667 // No photo ID
668 return;
669 }
670
671 for (Entity entity : contactData.getEntities()) {
672 for (NamedContentValues subValue : entity.getSubValues()) {
673 final ContentValues entryValues = subValue.values;
674 final long dataId = entryValues.getAsLong(Data._ID);
675 if (dataId == photoId) {
676 final String mimeType = entryValues.getAsString(Data.MIMETYPE);
677 // Correct Data Id but incorrect MimeType? Don't load
678 if (!Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
679 return;
680 }
681 contactData.setPhotoBinaryData(entryValues.getAsByteArray(Photo.PHOTO));
682 break;
683 }
684 }
685 }
686 }
687
688 /**
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700689 * Extracts Contact level columns from the cursor.
690 */
691 private Result loadContactHeaderData(final Cursor cursor, Uri contactUri) {
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700692 final String directoryParameter =
693 contactUri.getQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY);
694 final long directoryId = directoryParameter == null
695 ? Directory.DEFAULT
696 : Long.parseLong(directoryParameter);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700697 final long contactId = cursor.getLong(ContactQuery.CONTACT_ID);
698 final String lookupKey = cursor.getString(ContactQuery.LOOKUP_KEY);
699 final long nameRawContactId = cursor.getLong(ContactQuery.NAME_RAW_CONTACT_ID);
700 final int displayNameSource = cursor.getInt(ContactQuery.DISPLAY_NAME_SOURCE);
701 final String displayName = cursor.getString(ContactQuery.DISPLAY_NAME);
702 final String phoneticName = cursor.getString(ContactQuery.PHONETIC_NAME);
703 final long photoId = cursor.getLong(ContactQuery.PHOTO_ID);
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700704 final String photoUri = cursor.getString(ContactQuery.PHOTO_URI);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700705 final boolean starred = cursor.getInt(ContactQuery.STARRED) != 0;
706 final Integer presence = cursor.isNull(ContactQuery.CONTACT_PRESENCE)
707 ? null
708 : cursor.getInt(ContactQuery.CONTACT_PRESENCE);
709 final String status = cursor.getString(ContactQuery.CONTACT_STATUS);
710 final Long statusTimestamp = cursor.isNull(ContactQuery.CONTACT_STATUS_TIMESTAMP)
711 ? null
712 : cursor.getLong(ContactQuery.CONTACT_STATUS_TIMESTAMP);
713 final Integer statusLabel = cursor.isNull(ContactQuery.CONTACT_STATUS_LABEL)
714 ? null
715 : cursor.getInt(ContactQuery.CONTACT_STATUS_LABEL);
716 final String statusResPackage = cursor.getString(
717 ContactQuery.CONTACT_STATUS_RES_PACKAGE);
718
Dmitri Plotnikov1536ea12010-10-29 11:51:05 -0700719 Uri lookupUri;
720 if (directoryId == Directory.DEFAULT || directoryId == Directory.LOCAL_INVISIBLE) {
721 lookupUri = ContentUris.withAppendedId(
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700722 Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey), contactId);
Dmitri Plotnikov1536ea12010-10-29 11:51:05 -0700723 } else {
724 lookupUri = contactUri;
725 }
726
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700727 return new Result(contactUri, lookupUri, directoryId, lookupKey, contactId,
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700728 nameRawContactId, displayNameSource, photoId, photoUri, displayName,
729 phoneticName, starred, presence, status, statusTimestamp, statusLabel,
730 statusResPackage);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700731 }
732
733 /**
734 * Extracts RawContact level columns from the cursor.
735 */
736 private ContentValues loadRawContact(Cursor cursor) {
737 ContentValues cv = new ContentValues();
738
739 cv.put(RawContacts._ID, cursor.getLong(ContactQuery.RAW_CONTACT_ID));
740
741 cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_NAME);
742 cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_TYPE);
743 cursorColumnToContentValues(cursor, cv, ContactQuery.DIRTY);
744 cursorColumnToContentValues(cursor, cv, ContactQuery.VERSION);
745 cursorColumnToContentValues(cursor, cv, ContactQuery.SOURCE_ID);
746 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC1);
747 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC2);
748 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC3);
749 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC4);
750 cursorColumnToContentValues(cursor, cv, ContactQuery.DELETED);
751 cursorColumnToContentValues(cursor, cv, ContactQuery.CONTACT_ID);
752 cursorColumnToContentValues(cursor, cv, ContactQuery.STARRED);
753 cursorColumnToContentValues(cursor, cv, ContactQuery.IS_RESTRICTED);
754 cursorColumnToContentValues(cursor, cv, ContactQuery.NAME_VERIFIED);
755
756 return cv;
757 }
758
759 /**
760 * Extracts Data level columns from the cursor.
761 */
762 private ContentValues loadData(Cursor cursor) {
763 ContentValues cv = new ContentValues();
764
765 cv.put(Data._ID, cursor.getLong(ContactQuery.DATA_ID));
766
767 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA1);
768 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA2);
769 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA3);
770 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA4);
771 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA5);
772 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA6);
773 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA7);
774 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA8);
775 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA9);
776 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA10);
777 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA11);
778 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA12);
779 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA13);
780 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA14);
781 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA15);
782 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC1);
783 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC2);
784 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC3);
785 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC4);
786 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_VERSION);
787 cursorColumnToContentValues(cursor, cv, ContactQuery.IS_PRIMARY);
788 cursorColumnToContentValues(cursor, cv, ContactQuery.IS_SUPERPRIMARY);
789 cursorColumnToContentValues(cursor, cv, ContactQuery.MIMETYPE);
790 cursorColumnToContentValues(cursor, cv, ContactQuery.RES_PACKAGE);
791 cursorColumnToContentValues(cursor, cv, ContactQuery.GROUP_SOURCE_ID);
Daniel Lehmann8fd7bb62010-08-13 20:50:31 -0700792 cursorColumnToContentValues(cursor, cv, ContactQuery.CHAT_CAPABILITY);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700793
794 return cv;
795 }
796
797 private void cursorColumnToContentValues(
798 Cursor cursor, ContentValues values, int index) {
799 switch (cursor.getType(index)) {
800 case Cursor.FIELD_TYPE_NULL:
801 // don't put anything in the content values
802 break;
803 case Cursor.FIELD_TYPE_INTEGER:
804 values.put(ContactQuery.COLUMNS[index], cursor.getLong(index));
805 break;
806 case Cursor.FIELD_TYPE_STRING:
807 values.put(ContactQuery.COLUMNS[index], cursor.getString(index));
808 break;
809 case Cursor.FIELD_TYPE_BLOB:
810 values.put(ContactQuery.COLUMNS[index], cursor.getBlob(index));
811 break;
812 default:
813 throw new IllegalStateException("Invalid or unhandled data type");
814 }
815 }
816
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700817 private void loadDirectoryMetaData(Result result) {
818 long directoryId = result.getDirectoryId();
819
820 Cursor cursor = getContext().getContentResolver().query(
821 ContentUris.withAppendedId(Directory.CONTENT_URI, directoryId),
822 DirectoryQuery.COLUMNS, null, null, null);
823 if (cursor == null) {
824 return;
825 }
826 try {
827 if (cursor.moveToFirst()) {
828 final String displayName = cursor.getString(DirectoryQuery.DISPLAY_NAME);
829 final String packageName = cursor.getString(DirectoryQuery.PACKAGE_NAME);
830 final int typeResourceId = cursor.getInt(DirectoryQuery.TYPE_RESOURCE_ID);
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700831 final String accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE);
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700832 final String accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME);
833 final int exportSupport = cursor.getInt(DirectoryQuery.EXPORT_SUPPORT);
834 String directoryType = null;
835 if (!TextUtils.isEmpty(packageName)) {
836 PackageManager pm = getContext().getPackageManager();
837 try {
838 Resources resources = pm.getResourcesForApplication(packageName);
839 directoryType = resources.getString(typeResourceId);
840 } catch (NameNotFoundException e) {
841 Log.w(TAG, "Contact directory resource not found: "
842 + packageName + "." + typeResourceId);
843 }
844 }
845
846 result.setDirectoryMetaData(
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700847 displayName, directoryType, accountType, accountName, exportSupport);
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700848 }
849 } finally {
850 cursor.close();
851 }
852 }
853
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700854 /**
855 * Loads groups meta-data for all groups associated with all constituent raw contacts'
856 * accounts.
857 */
858 private void loadGroupMetaData(Result result) {
859 StringBuilder selection = new StringBuilder();
860 ArrayList<String> selectionArgs = new ArrayList<String>();
861 for (Entity entity : result.mEntities) {
862 ContentValues values = entity.getEntityValues();
863 String accountName = values.getAsString(RawContacts.ACCOUNT_NAME);
864 String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
865 if (accountName != null && accountType != null) {
866 if (selection.length() != 0) {
867 selection.append(" OR ");
868 }
869 selection.append(
870 "(" + Groups.ACCOUNT_NAME + "=? AND " + Groups.ACCOUNT_TYPE + "=?)");
871 selectionArgs.add(accountName);
872 selectionArgs.add(accountType);
873 }
874 }
875 Cursor cursor = getContext().getContentResolver().query(Groups.CONTENT_URI,
876 GroupQuery.COLUMNS, selection.toString(), selectionArgs.toArray(new String[0]),
877 null);
878 try {
879 while (cursor.moveToNext()) {
880 final String accountName = cursor.getString(GroupQuery.ACCOUNT_NAME);
881 final String accountType = cursor.getString(GroupQuery.ACCOUNT_TYPE);
882 final long groupId = cursor.getLong(GroupQuery.ID);
883 final String title = cursor.getString(GroupQuery.TITLE);
884 final boolean defaultGroup = cursor.isNull(GroupQuery.AUTO_ADD)
885 ? false
886 : cursor.getInt(GroupQuery.AUTO_ADD) != 0;
887 final boolean favorites = cursor.isNull(GroupQuery.FAVORITES)
888 ? false
889 : cursor.getInt(GroupQuery.FAVORITES) != 0;
890
Dmitri Plotnikove843f912010-09-16 15:21:48 -0700891 result.addGroupMetaData(new GroupMetaData(
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700892 accountName, accountType, groupId, title, defaultGroup, favorites));
893 }
894 } finally {
895 cursor.close();
896 }
897 }
898
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700899 @Override
900 protected void onPostExecute(Result result) {
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -0800901 unregisterObserver();
902
Daniel Lehmann1316b132010-04-13 15:08:53 -0700903 // The creator isn't interested in any further updates
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -0800904 if (mDestroyed || result == null) {
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700905 return;
906 }
907
908 mContact = result;
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700909
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -0800910 if (result != Result.ERROR && result != Result.NOT_FOUND) {
911 mLookupUri = result.getLookupUri();
912
913 if (!result.isDirectoryEntry()) {
914 Log.i(TAG, "Registering content observer for " + mLookupUri);
915 if (mObserver == null) {
916 mObserver = new ForceLoadContentObserver();
917 }
918 getContext().getContentResolver().registerContentObserver(
919 mLookupUri, true, mObserver);
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700920 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800921
922 if (mContact.getPhotoBinaryData() == null && mContact.getPhotoUri() != null) {
923 mContact.setLoadingPhoto(true);
924 new AsyncPhotoLoader().execute(mContact.getPhotoUri());
925 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700926 }
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -0800927
928 deliverResult(mContact);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700929 }
930 }
931
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800932 private class AsyncPhotoLoader extends AsyncTask<String, Void, byte[]> {
933
934 private static final int BUFFER_SIZE = 1024*16;
935
936 @Override
937 protected byte[] doInBackground(String... params) {
938 Uri uri = Uri.parse(params[0]);
939 byte[] data = null;
940 try {
941 InputStream is = getContext().getContentResolver().openInputStream(uri);
942 if (is != null) {
943 ByteArrayOutputStream baos = new ByteArrayOutputStream();
944 try {
945 byte[] mBuffer = new byte[BUFFER_SIZE];
946
947 int size;
948 while ((size = is.read(mBuffer)) != -1) {
949 baos.write(mBuffer, 0, size);
950 }
951 data = baos.toByteArray();
952 } finally {
953 is.close();
954 }
955 } else {
956 Log.v(TAG, "Cannot load photo " + uri);
957 }
958 } catch (IOException e) {
959 Log.e(TAG, "Cannot load photo " + uri, e);
960 }
961
962 return data;
963 }
964
965 @Override
966 protected void onPostExecute(byte[] data) {
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -0800967 if (mContact != null) {
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800968 mContact = new Result(mContact);
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -0800969 mContact.setPhotoBinaryData(data);
970 mContact.setLoadingPhoto(false);
971 deliverResult(mContact);
972 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800973 }
974 }
975
Daniel Lehmann3a120772010-06-21 16:21:35 -0700976 private void unregisterObserver() {
977 if (mObserver != null) {
978 getContext().getContentResolver().unregisterContentObserver(mObserver);
979 mObserver = null;
980 }
981 }
982
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700983 public ContactLoader(Context context, Uri lookupUri) {
Dmitri Plotnikove843f912010-09-16 15:21:48 -0700984 this(context, lookupUri, false);
985 }
986
987 public ContactLoader(Context context, Uri lookupUri, boolean loadGroupMetaData) {
Daniel Lehmann74a2dc52010-04-15 16:52:33 -0700988 super(context);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700989 mLookupUri = lookupUri;
Dmitri Plotnikove843f912010-09-16 15:21:48 -0700990 mLoadGroupMetaData = loadGroupMetaData;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700991 }
992
Dmitri Plotnikov5a30d9a2010-11-23 14:59:50 -0800993 public Uri getLookupUri() {
994 return mLookupUri;
995 }
996
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700997 @Override
Dianne Hackbornc04fc272010-12-20 23:13:10 -0800998 protected void onStartLoading() {
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700999 if (mContact != null) {
Daniel Lehmanncbcc4492010-04-12 18:03:54 -07001000 deliverResult(mContact);
Dmitri Plotnikov97e90c62011-01-03 11:58:13 -08001001 }
1002
1003 if (takeContentChanged() || mContact == null) {
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001004 forceLoad();
1005 }
1006 }
1007
1008 @Override
Dianne Hackbornc04fc272010-12-20 23:13:10 -08001009 protected void onForceLoad() {
Daniel Lehmann18f104f2010-05-07 15:41:11 -07001010 final LoadContactTask task = new LoadContactTask();
Daniel Lehmann74a2dc52010-04-15 16:52:33 -07001011 task.execute((Void[])null);
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001012 }
1013
1014 @Override
Dianne Hackbornc04fc272010-12-20 23:13:10 -08001015 protected void onReset() {
Dianne Hackborn4ef95cc2010-12-16 00:44:33 -08001016 unregisterObserver();
1017 mContact = null;
1018 mDestroyed = true;
1019 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001020}