blob: f10e6b87b0baa52f6650337e8273ff3bd83755a1 [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;
Flavio Lerda37a26842011-06-27 11:36:52 +010020import com.google.common.annotations.VisibleForTesting;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070021
22import android.content.ContentResolver;
23import android.content.ContentUris;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -070024import android.content.ContentValues;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070025import android.content.Context;
26import android.content.Entity;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -070027import android.content.Entity.NamedContentValues;
Jeff Hamilton3c462912010-05-15 02:20:01 -050028import android.content.Loader;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -070029import android.content.pm.PackageManager;
30import android.content.pm.PackageManager.NameNotFoundException;
Dave Santoro0a2a5db2011-06-29 00:37:06 -070031import android.content.res.AssetFileDescriptor;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -070032import android.content.res.Resources;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070033import android.database.Cursor;
34import android.net.Uri;
35import android.os.AsyncTask;
Daniel Lehmann1316b132010-04-13 15:08:53 -070036import android.provider.ContactsContract;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -070037import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -080038import android.provider.ContactsContract.CommonDataKinds.Photo;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070039import android.provider.ContactsContract.Contacts;
40import android.provider.ContactsContract.Data;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -070041import android.provider.ContactsContract.Directory;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070042import android.provider.ContactsContract.DisplayNameSources;
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -070043import android.provider.ContactsContract.Groups;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070044import android.provider.ContactsContract.RawContacts;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -070045import android.text.TextUtils;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070046import android.util.Log;
47
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -080048import java.io.ByteArrayOutputStream;
Dave Santoro0a2a5db2011-06-29 00:37:06 -070049import java.io.FileInputStream;
50import java.io.FileNotFoundException;
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -080051import java.io.IOException;
52import java.io.InputStream;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070053import java.util.ArrayList;
54import java.util.HashMap;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -070055import java.util.List;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070056
57/**
58 * Loads a single Contact and all it constituent RawContacts.
59 */
Daniel Lehmanncdef2b62010-06-06 18:25:49 -070060public class ContactLoader extends Loader<ContactLoader.Result> {
Daniel Lehmann18f104f2010-05-07 15:41:11 -070061 private static final String TAG = "ContactLoader";
62
Daniel Lehmann4cd94412010-04-08 16:44:36 -070063 private Uri mLookupUri;
Dmitri Plotnikove843f912010-09-16 15:21:48 -070064 private boolean mLoadGroupMetaData;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070065 private Result mContact;
66 private ForceLoadContentObserver mObserver;
67 private boolean mDestroyed;
68
Dmitri Plotnikove843f912010-09-16 15:21:48 -070069
Daniel Lehmanncdef2b62010-06-06 18:25:49 -070070 public interface Listener {
Daniel Lehmann4cd94412010-04-08 16:44:36 -070071 public void onContactLoaded(Result contact);
72 }
73
74 /**
75 * The result of a load operation. Contains all data necessary to display the contact.
76 */
77 public static final class Result {
78 /**
79 * Singleton instance that represents "No Contact Found"
80 */
81 public static final Result NOT_FOUND = new Result();
82
Daniel Lehmann18f104f2010-05-07 15:41:11 -070083 /**
84 * Singleton instance that represents an error, e.g. because of an invalid Uri
85 * TODO: We should come up with something nicer here. Maybe use an Either type so
86 * that we can capture the Exception?
87 */
88 public static final Result ERROR = new Result();
89
Daniel Lehmann4cd94412010-04-08 16:44:36 -070090 private final Uri mLookupUri;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070091 private final Uri mUri;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -070092 private final long mDirectoryId;
93 private final String mLookupKey;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070094 private final long mId;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070095 private final long mNameRawContactId;
96 private final int mDisplayNameSource;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -070097 private final long mPhotoId;
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -070098 private final String mPhotoUri;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -070099 private final String mDisplayName;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700100 private final String mAltDisplayName;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700101 private final String mPhoneticName;
102 private final boolean mStarred;
103 private final Integer mPresence;
104 private final ArrayList<Entity> mEntities;
105 private final HashMap<Long, DataStatus> mStatuses;
106 private final String mStatus;
107 private final Long mStatusTimestamp;
108 private final Integer mStatusLabel;
109 private final String mStatusResPackage;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700110
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700111 private String mDirectoryDisplayName;
112 private String mDirectoryType;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700113 private String mDirectoryAccountType;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700114 private String mDirectoryAccountName;
115 private int mDirectoryExportSupport;
116
Dmitri Plotnikove843f912010-09-16 15:21:48 -0700117 private ArrayList<GroupMetaData> mGroups;
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700118
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800119 private boolean mLoadingPhoto;
120 private byte[] mPhotoBinaryData;
121
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700122 /**
123 * Constructor for case "no contact found". This must only be used for the
124 * final {@link Result#NOT_FOUND} singleton
125 */
126 private Result() {
127 mLookupUri = null;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700128 mUri = null;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700129 mDirectoryId = -1;
130 mLookupKey = null;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700131 mId = -1;
132 mEntities = null;
133 mStatuses = null;
134 mNameRawContactId = -1;
135 mDisplayNameSource = DisplayNameSources.UNDEFINED;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700136 mPhotoId = -1;
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700137 mPhotoUri = null;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700138 mDisplayName = null;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700139 mAltDisplayName = null;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700140 mPhoneticName = null;
141 mStarred = false;
142 mPresence = null;
143 mStatus = null;
144 mStatusTimestamp = null;
145 mStatusLabel = null;
146 mStatusResPackage = null;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700147 }
148
149 /**
150 * Constructor to call when contact was found
151 */
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700152 private Result(Uri uri, Uri lookupUri, long directoryId, String lookupKey, long id,
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700153 long nameRawContactId, int displayNameSource, long photoId, String photoUri,
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700154 String displayName, String altDisplayName, String phoneticName, boolean starred,
155 Integer presence, String status, Long statusTimestamp, Integer statusLabel,
156 String statusResPackage) {
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700157 mLookupUri = lookupUri;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700158 mUri = uri;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700159 mDirectoryId = directoryId;
160 mLookupKey = lookupKey;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700161 mId = id;
162 mEntities = new ArrayList<Entity>();
163 mStatuses = new HashMap<Long, DataStatus>();
164 mNameRawContactId = nameRawContactId;
165 mDisplayNameSource = displayNameSource;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700166 mPhotoId = photoId;
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700167 mPhotoUri = photoUri;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700168 mDisplayName = displayName;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700169 mAltDisplayName = altDisplayName;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700170 mPhoneticName = phoneticName;
171 mStarred = starred;
172 mPresence = presence;
173 mStatus = status;
174 mStatusTimestamp = statusTimestamp;
175 mStatusLabel = statusLabel;
176 mStatusResPackage = statusResPackage;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700177 }
178
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800179 private Result(Result from) {
180 mLookupUri = from.mLookupUri;
181 mUri = from.mUri;
182 mDirectoryId = from.mDirectoryId;
183 mLookupKey = from.mLookupKey;
184 mId = from.mId;
185 mNameRawContactId = from.mNameRawContactId;
186 mDisplayNameSource = from.mDisplayNameSource;
187 mPhotoId = from.mPhotoId;
188 mPhotoUri = from.mPhotoUri;
189 mDisplayName = from.mDisplayName;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700190 mAltDisplayName = from.mAltDisplayName;
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800191 mPhoneticName = from.mPhoneticName;
192 mStarred = from.mStarred;
193 mPresence = from.mPresence;
194 mEntities = from.mEntities;
195 mStatuses = from.mStatuses;
196 mStatus = from.mStatus;
197 mStatusTimestamp = from.mStatusTimestamp;
198 mStatusLabel = from.mStatusLabel;
199 mStatusResPackage = from.mStatusResPackage;
200
201 mDirectoryDisplayName = from.mDirectoryDisplayName;
202 mDirectoryType = from.mDirectoryType;
203 mDirectoryAccountType = from.mDirectoryAccountType;
204 mDirectoryAccountName = from.mDirectoryAccountName;
205 mDirectoryExportSupport = from.mDirectoryExportSupport;
206
207 mGroups = from.mGroups;
208
209 mLoadingPhoto = from.mLoadingPhoto;
210 mPhotoBinaryData = from.mPhotoBinaryData;
211 }
212
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700213 /**
214 * @param exportSupport See {@link Directory#EXPORT_SUPPORT}.
215 */
Daniel Lehmann1ad4d1b2010-10-18 19:20:41 -0700216 private void setDirectoryMetaData(String displayName, String directoryType,
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700217 String accountType, String accountName, int exportSupport) {
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700218 mDirectoryDisplayName = displayName;
219 mDirectoryType = directoryType;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700220 mDirectoryAccountType = accountType;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700221 mDirectoryAccountName = accountName;
222 mDirectoryExportSupport = exportSupport;
223 }
224
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800225 private void setLoadingPhoto(boolean flag) {
226 mLoadingPhoto = flag;
227 }
228
229 private void setPhotoBinaryData(byte[] photoBinaryData) {
230 mPhotoBinaryData = photoBinaryData;
231 }
232
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700233 public Uri getLookupUri() {
234 return mLookupUri;
235 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800236
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700237 public String getLookupKey() {
238 return mLookupKey;
239 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800240
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700241 public Uri getUri() {
242 return mUri;
243 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800244
Flavio Lerda37a26842011-06-27 11:36:52 +0100245 @VisibleForTesting
246 /*package*/ long getId() {
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700247 return mId;
248 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800249
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700250 public long getNameRawContactId() {
251 return mNameRawContactId;
252 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800253
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700254 public int getDisplayNameSource() {
255 return mDisplayNameSource;
256 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800257
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700258 public long getPhotoId() {
259 return mPhotoId;
260 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800261
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700262 public String getPhotoUri() {
263 return mPhotoUri;
264 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800265
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700266 public String getDisplayName() {
267 return mDisplayName;
268 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800269
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700270 public String getAltDisplayName() {
271 return mAltDisplayName;
272 }
273
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700274 public String getPhoneticName() {
275 return mPhoneticName;
276 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800277
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700278 public boolean getStarred() {
279 return mStarred;
280 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800281
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700282 public Integer getPresence() {
283 return mPresence;
284 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800285
Daniel Lehmann2d4f7592010-10-12 23:31:31 -0700286 public String getSocialSnippet() {
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700287 return mStatus;
288 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800289
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700290 public Long getStatusTimestamp() {
291 return mStatusTimestamp;
292 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800293
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700294 public Integer getStatusLabel() {
295 return mStatusLabel;
296 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800297
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700298 public String getStatusResPackage() {
299 return mStatusResPackage;
300 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800301
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700302 public ArrayList<Entity> getEntities() {
303 return mEntities;
304 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800305
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700306 public HashMap<Long, DataStatus> getStatuses() {
307 return mStatuses;
308 }
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700309
310 public long getDirectoryId() {
311 return mDirectoryId;
312 }
313
314 public boolean isDirectoryEntry() {
Dmitri Plotnikov5f72c1f2010-09-01 21:21:04 -0700315 return mDirectoryId != -1 && mDirectoryId != Directory.DEFAULT
316 && mDirectoryId != Directory.LOCAL_INVISIBLE;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700317 }
318
319 public int getDirectoryExportSupport() {
320 return mDirectoryExportSupport;
321 }
322
323 public String getDirectoryDisplayName() {
324 return mDirectoryDisplayName;
325 }
326
327 public String getDirectoryType() {
328 return mDirectoryType;
329 }
330
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700331 public String getDirectoryAccountType() {
332 return mDirectoryAccountType;
333 }
334
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700335 public String getDirectoryAccountName() {
336 return mDirectoryAccountName;
337 }
338
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800339 public boolean isLoadingPhoto() {
340 return mLoadingPhoto;
341 }
342
343 public byte[] getPhotoBinaryData() {
344 return mPhotoBinaryData;
345 }
346
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700347 public ArrayList<ContentValues> getContentValues() {
348 if (mEntities.size() != 1) {
349 throw new IllegalStateException(
350 "Cannot extract content values from an aggregated contact");
351 }
352
353 Entity entity = mEntities.get(0);
354 ArrayList<ContentValues> result = new ArrayList<ContentValues>();
355 ArrayList<NamedContentValues> subValues = entity.getSubValues();
356 if (subValues != null) {
357 int size = subValues.size();
358 for (int i = 0; i < size; i++) {
359 NamedContentValues pair = subValues.get(i);
360 if (Data.CONTENT_URI.equals(pair.uri)) {
361 result.add(pair.values);
362 }
363 }
364 }
Dmitri Plotnikov40ec3a82010-11-10 11:25:33 -0800365
366 // If the photo was loaded using the URI, create an entry for the photo
367 // binary data.
368 if (mPhotoId == 0 && mPhotoBinaryData != null) {
369 ContentValues photo = new ContentValues();
370 photo.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
371 photo.put(Photo.PHOTO, mPhotoBinaryData);
372 result.add(photo);
373 }
374
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700375 return result;
376 }
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700377
Daniel Lehmann1ad4d1b2010-10-18 19:20:41 -0700378 private void addGroupMetaData(GroupMetaData group) {
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700379 if (mGroups == null) {
Dmitri Plotnikove843f912010-09-16 15:21:48 -0700380 mGroups = new ArrayList<GroupMetaData>();
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700381 }
382 mGroups.add(group);
383 }
384
Dmitri Plotnikove843f912010-09-16 15:21:48 -0700385 public List<GroupMetaData> getGroupMetaData() {
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700386 return mGroups;
387 }
388 }
389
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700390 private static class ContactQuery {
391 // Projection used for the query that loads all data for the entire contact.
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700392 final static String[] COLUMNS = new String[] {
393 Contacts.NAME_RAW_CONTACT_ID,
394 Contacts.DISPLAY_NAME_SOURCE,
395 Contacts.LOOKUP_KEY,
396 Contacts.DISPLAY_NAME,
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700397 Contacts.DISPLAY_NAME_ALTERNATIVE,
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700398 Contacts.PHONETIC_NAME,
399 Contacts.PHOTO_ID,
400 Contacts.STARRED,
401 Contacts.CONTACT_PRESENCE,
402 Contacts.CONTACT_STATUS,
403 Contacts.CONTACT_STATUS_TIMESTAMP,
404 Contacts.CONTACT_STATUS_RES_PACKAGE,
405 Contacts.CONTACT_STATUS_LABEL,
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700406 Contacts.Entity.CONTACT_ID,
407 Contacts.Entity.RAW_CONTACT_ID,
408
409 RawContacts.ACCOUNT_NAME,
410 RawContacts.ACCOUNT_TYPE,
411 RawContacts.DIRTY,
412 RawContacts.VERSION,
413 RawContacts.SOURCE_ID,
414 RawContacts.SYNC1,
415 RawContacts.SYNC2,
416 RawContacts.SYNC3,
417 RawContacts.SYNC4,
418 RawContacts.DELETED,
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700419 RawContacts.NAME_VERIFIED,
420
421 Contacts.Entity.DATA_ID,
422 Data.DATA1,
423 Data.DATA2,
424 Data.DATA3,
425 Data.DATA4,
426 Data.DATA5,
427 Data.DATA6,
428 Data.DATA7,
429 Data.DATA8,
430 Data.DATA9,
431 Data.DATA10,
432 Data.DATA11,
433 Data.DATA12,
434 Data.DATA13,
435 Data.DATA14,
436 Data.DATA15,
437 Data.SYNC1,
438 Data.SYNC2,
439 Data.SYNC3,
440 Data.SYNC4,
441 Data.DATA_VERSION,
442 Data.IS_PRIMARY,
443 Data.IS_SUPER_PRIMARY,
444 Data.MIMETYPE,
445 Data.RES_PACKAGE,
446
447 GroupMembership.GROUP_SOURCE_ID,
448
449 Data.PRESENCE,
Daniel Lehmann8fd7bb62010-08-13 20:50:31 -0700450 Data.CHAT_CAPABILITY,
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700451 Data.STATUS,
452 Data.STATUS_RES_PACKAGE,
453 Data.STATUS_ICON,
454 Data.STATUS_LABEL,
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700455 Data.STATUS_TIMESTAMP,
456
457 Contacts.PHOTO_URI,
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700458 };
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700459
460 public final static int NAME_RAW_CONTACT_ID = 0;
461 public final static int DISPLAY_NAME_SOURCE = 1;
462 public final static int LOOKUP_KEY = 2;
463 public final static int DISPLAY_NAME = 3;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700464 public final static int ALT_DISPLAY_NAME = 4;
465 public final static int PHONETIC_NAME = 5;
466 public final static int PHOTO_ID = 6;
467 public final static int STARRED = 7;
468 public final static int CONTACT_PRESENCE = 8;
469 public final static int CONTACT_STATUS = 9;
470 public final static int CONTACT_STATUS_TIMESTAMP = 10;
471 public final static int CONTACT_STATUS_RES_PACKAGE = 11;
472 public final static int CONTACT_STATUS_LABEL = 12;
473 public final static int CONTACT_ID = 13;
474 public final static int RAW_CONTACT_ID = 14;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700475
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700476 public final static int ACCOUNT_NAME = 15;
477 public final static int ACCOUNT_TYPE = 16;
478 public final static int DIRTY = 17;
479 public final static int VERSION = 18;
480 public final static int SOURCE_ID = 19;
481 public final static int SYNC1 = 20;
482 public final static int SYNC2 = 21;
483 public final static int SYNC3 = 22;
484 public final static int SYNC4 = 23;
485 public final static int DELETED = 24;
Daniel Lehmann4ff31282011-07-11 14:38:36 -0700486 public final static int NAME_VERIFIED = 25;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700487
Daniel Lehmann4ff31282011-07-11 14:38:36 -0700488 public final static int DATA_ID = 26;
489 public final static int DATA1 = 27;
490 public final static int DATA2 = 28;
491 public final static int DATA3 = 29;
492 public final static int DATA4 = 30;
493 public final static int DATA5 = 31;
494 public final static int DATA6 = 32;
495 public final static int DATA7 = 33;
496 public final static int DATA8 = 34;
497 public final static int DATA9 = 35;
498 public final static int DATA10 = 36;
499 public final static int DATA11 = 37;
500 public final static int DATA12 = 38;
501 public final static int DATA13 = 39;
502 public final static int DATA14 = 40;
503 public final static int DATA15 = 41;
504 public final static int DATA_SYNC1 = 42;
505 public final static int DATA_SYNC2 = 43;
506 public final static int DATA_SYNC3 = 44;
507 public final static int DATA_SYNC4 = 45;
508 public final static int DATA_VERSION = 46;
509 public final static int IS_PRIMARY = 47;
510 public final static int IS_SUPERPRIMARY = 48;
511 public final static int MIMETYPE = 49;
512 public final static int RES_PACKAGE = 50;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700513
Daniel Lehmann4ff31282011-07-11 14:38:36 -0700514 public final static int GROUP_SOURCE_ID = 51;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700515
Daniel Lehmann4ff31282011-07-11 14:38:36 -0700516 public final static int PRESENCE = 52;
517 public final static int CHAT_CAPABILITY = 53;
518 public final static int STATUS = 54;
519 public final static int STATUS_RES_PACKAGE = 55;
520 public final static int STATUS_ICON = 56;
521 public final static int STATUS_LABEL = 57;
522 public final static int STATUS_TIMESTAMP = 58;
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700523
Daniel Lehmann4ff31282011-07-11 14:38:36 -0700524 public final static int PHOTO_URI = 59;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700525 }
Daniel Lehmann1316b132010-04-13 15:08:53 -0700526
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700527 private static class DirectoryQuery {
528 // Projection used for the query that loads all data for the entire contact.
529 final static String[] COLUMNS = new String[] {
530 Directory.DISPLAY_NAME,
531 Directory.PACKAGE_NAME,
532 Directory.TYPE_RESOURCE_ID,
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700533 Directory.ACCOUNT_TYPE,
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700534 Directory.ACCOUNT_NAME,
535 Directory.EXPORT_SUPPORT,
536 };
537
538 public final static int DISPLAY_NAME = 0;
539 public final static int PACKAGE_NAME = 1;
540 public final static int TYPE_RESOURCE_ID = 2;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700541 public final static int ACCOUNT_TYPE = 3;
542 public final static int ACCOUNT_NAME = 4;
543 public final static int EXPORT_SUPPORT = 5;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700544 }
545
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700546 private static class GroupQuery {
547 final static String[] COLUMNS = new String[] {
548 Groups.ACCOUNT_NAME,
549 Groups.ACCOUNT_TYPE,
550 Groups._ID,
551 Groups.TITLE,
552 Groups.AUTO_ADD,
553 Groups.FAVORITES,
554 };
555
556 public final static int ACCOUNT_NAME = 0;
557 public final static int ACCOUNT_TYPE = 1;
558 public final static int ID = 2;
559 public final static int TITLE = 3;
560 public final static int AUTO_ADD = 4;
561 public final static int FAVORITES = 5;
562 }
563
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700564 private final class LoadContactTask extends AsyncTask<Void, Void, Result> {
Daniel Lehmann1316b132010-04-13 15:08:53 -0700565
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700566 @Override
567 protected Result doInBackground(Void... args) {
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700568 try {
569 final ContentResolver resolver = getContext().getContentResolver();
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700570 final Uri uriCurrentFormat = ensureIsContactUri(resolver, mLookupUri);
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700571 Result result = loadContactEntity(resolver, uriCurrentFormat);
Dmitri Plotnikov217245c2010-09-18 13:04:50 -0700572 if (result != Result.NOT_FOUND) {
573 if (result.isDirectoryEntry()) {
574 loadDirectoryMetaData(result);
575 } else if (mLoadGroupMetaData) {
576 loadGroupMetaData(result);
577 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800578 loadPhotoBinaryData(result);
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700579 }
580 return result;
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700581 } catch (Exception e) {
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700582 Log.e(TAG, "Error loading the contact: " + mLookupUri, e);
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700583 return Result.ERROR;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700584 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700585 }
586
587 /**
Daniel Lehmann1316b132010-04-13 15:08:53 -0700588 * Transforms the given Uri and returns a Lookup-Uri that represents the contact.
589 * For legacy contacts, a raw-contact lookup is performed.
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700590 * @param resolver
Daniel Lehmann1316b132010-04-13 15:08:53 -0700591 */
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700592 private Uri ensureIsContactUri(final ContentResolver resolver, final Uri uri) {
Daniel Lehmann1316b132010-04-13 15:08:53 -0700593 if (uri == null) throw new IllegalArgumentException("uri must not be null");
594
595 final String authority = uri.getAuthority();
596
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700597 // Current Style Uri?
Daniel Lehmann1316b132010-04-13 15:08:53 -0700598 if (ContactsContract.AUTHORITY.equals(authority)) {
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700599 final String type = resolver.getType(uri);
600 // Contact-Uri? Good, return it
601 if (Contacts.CONTENT_ITEM_TYPE.equals(type)) {
602 return uri;
603 }
604
605 // RawContact-Uri? Transform it to ContactUri
606 if (RawContacts.CONTENT_ITEM_TYPE.equals(type)) {
607 final long rawContactId = ContentUris.parseId(uri);
608 return RawContacts.getContactLookupUri(getContext().getContentResolver(),
609 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId));
610 }
611
612 // Anything else? We don't know what this is
613 throw new IllegalArgumentException("uri format is unknown");
Daniel Lehmann1316b132010-04-13 15:08:53 -0700614 }
615
616 // Legacy Style? Convert to RawContact
617 final String OBSOLETE_AUTHORITY = "contacts";
618 if (OBSOLETE_AUTHORITY.equals(authority)) {
619 // Legacy Format. Convert to RawContact-Uri and then lookup the contact
620 final long rawContactId = ContentUris.parseId(uri);
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700621 return RawContacts.getContactLookupUri(resolver,
Daniel Lehmann1316b132010-04-13 15:08:53 -0700622 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId));
623 }
624
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700625 throw new IllegalArgumentException("uri authority is unknown");
Daniel Lehmann1316b132010-04-13 15:08:53 -0700626 }
627
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700628 private Result loadContactEntity(ContentResolver resolver, Uri contactUri) {
629 Uri entityUri = Uri.withAppendedPath(contactUri, Contacts.Entity.CONTENT_DIRECTORY);
630 Cursor cursor = resolver.query(entityUri, ContactQuery.COLUMNS, null, null,
631 Contacts.Entity.RAW_CONTACT_ID);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700632 if (cursor == null) {
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700633 Log.e(TAG, "No cursor returned in loadContactEntity");
634 return Result.NOT_FOUND;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700635 }
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700636
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700637 try {
638 if (!cursor.moveToFirst()) {
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700639 cursor.close();
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700640 return Result.NOT_FOUND;
641 }
642
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700643 long currentRawContactId = -1;
644 Entity entity = null;
645 Result result = loadContactHeaderData(cursor, contactUri);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700646 ArrayList<Entity> entities = result.getEntities();
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700647 HashMap<Long, DataStatus> statuses = result.getStatuses();
648 for (; !cursor.isAfterLast(); cursor.moveToNext()) {
649 long rawContactId = cursor.getLong(ContactQuery.RAW_CONTACT_ID);
650 if (rawContactId != currentRawContactId) {
651 currentRawContactId = rawContactId;
652 entity = new android.content.Entity(loadRawContact(cursor));
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700653 entities.add(entity);
654 }
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700655 if (!cursor.isNull(ContactQuery.DATA_ID)) {
656 ContentValues data = loadData(cursor);
657 entity.addSubValue(ContactsContract.Data.CONTENT_URI, data);
658
659 if (!cursor.isNull(ContactQuery.PRESENCE)
660 || !cursor.isNull(ContactQuery.STATUS)) {
661 final DataStatus status = new DataStatus(cursor);
662 final long dataId = cursor.getLong(ContactQuery.DATA_ID);
663 statuses.put(dataId, status);
664 }
665 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700666 }
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700667
668 return result;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700669 } finally {
670 cursor.close();
671 }
672 }
673
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700674 /**
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800675 * Looks for the photo data item in entities. If found, creates a new Bitmap instance. If
676 * not found, returns null
677 */
678 private void loadPhotoBinaryData(Result contactData) {
Dave Santoro0a2a5db2011-06-29 00:37:06 -0700679
680 // If we have a photo URI, try loading that first.
681 String photoUri = contactData.getPhotoUri();
682 if (photoUri != null) {
683 try {
684 AssetFileDescriptor fd = getContext().getContentResolver()
685 .openAssetFileDescriptor(Uri.parse(photoUri), "r");
686 byte[] buffer = new byte[16 * 1024];
687 FileInputStream fis = fd.createInputStream();
688 ByteArrayOutputStream baos = new ByteArrayOutputStream();
689 try {
690 int size;
691 while ((size = fis.read(buffer)) != -1) {
692 baos.write(buffer, 0, size);
693 }
694 contactData.setPhotoBinaryData(baos.toByteArray());
695 } finally {
696 fis.close();
697 fd.close();
698 }
699 return;
700 } catch (IOException ioe) {
701 // Just fall back to the case below.
702 }
703 }
704
705 // If we couldn't load from a file, fall back to the data blob.
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800706 final long photoId = contactData.getPhotoId();
707 if (photoId <= 0) {
708 // No photo ID
709 return;
710 }
711
712 for (Entity entity : contactData.getEntities()) {
713 for (NamedContentValues subValue : entity.getSubValues()) {
714 final ContentValues entryValues = subValue.values;
715 final long dataId = entryValues.getAsLong(Data._ID);
716 if (dataId == photoId) {
717 final String mimeType = entryValues.getAsString(Data.MIMETYPE);
718 // Correct Data Id but incorrect MimeType? Don't load
719 if (!Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
720 return;
721 }
722 contactData.setPhotoBinaryData(entryValues.getAsByteArray(Photo.PHOTO));
723 break;
724 }
725 }
726 }
727 }
728
729 /**
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700730 * Extracts Contact level columns from the cursor.
731 */
732 private Result loadContactHeaderData(final Cursor cursor, Uri contactUri) {
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700733 final String directoryParameter =
734 contactUri.getQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY);
735 final long directoryId = directoryParameter == null
736 ? Directory.DEFAULT
737 : Long.parseLong(directoryParameter);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700738 final long contactId = cursor.getLong(ContactQuery.CONTACT_ID);
739 final String lookupKey = cursor.getString(ContactQuery.LOOKUP_KEY);
740 final long nameRawContactId = cursor.getLong(ContactQuery.NAME_RAW_CONTACT_ID);
741 final int displayNameSource = cursor.getInt(ContactQuery.DISPLAY_NAME_SOURCE);
742 final String displayName = cursor.getString(ContactQuery.DISPLAY_NAME);
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700743 final String altDisplayName = cursor.getString(ContactQuery.ALT_DISPLAY_NAME);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700744 final String phoneticName = cursor.getString(ContactQuery.PHONETIC_NAME);
745 final long photoId = cursor.getLong(ContactQuery.PHOTO_ID);
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700746 final String photoUri = cursor.getString(ContactQuery.PHOTO_URI);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700747 final boolean starred = cursor.getInt(ContactQuery.STARRED) != 0;
748 final Integer presence = cursor.isNull(ContactQuery.CONTACT_PRESENCE)
749 ? null
750 : cursor.getInt(ContactQuery.CONTACT_PRESENCE);
751 final String status = cursor.getString(ContactQuery.CONTACT_STATUS);
752 final Long statusTimestamp = cursor.isNull(ContactQuery.CONTACT_STATUS_TIMESTAMP)
753 ? null
754 : cursor.getLong(ContactQuery.CONTACT_STATUS_TIMESTAMP);
755 final Integer statusLabel = cursor.isNull(ContactQuery.CONTACT_STATUS_LABEL)
756 ? null
757 : cursor.getInt(ContactQuery.CONTACT_STATUS_LABEL);
758 final String statusResPackage = cursor.getString(
759 ContactQuery.CONTACT_STATUS_RES_PACKAGE);
760
Dmitri Plotnikov1536ea12010-10-29 11:51:05 -0700761 Uri lookupUri;
762 if (directoryId == Directory.DEFAULT || directoryId == Directory.LOCAL_INVISIBLE) {
763 lookupUri = ContentUris.withAppendedId(
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700764 Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey), contactId);
Dmitri Plotnikov1536ea12010-10-29 11:51:05 -0700765 } else {
766 lookupUri = contactUri;
767 }
768
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700769 return new Result(contactUri, lookupUri, directoryId, lookupKey, contactId,
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700770 nameRawContactId, displayNameSource, photoId, photoUri, displayName,
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700771 altDisplayName, phoneticName, starred, presence, status, statusTimestamp,
772 statusLabel, statusResPackage);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700773 }
774
775 /**
776 * Extracts RawContact level columns from the cursor.
777 */
778 private ContentValues loadRawContact(Cursor cursor) {
779 ContentValues cv = new ContentValues();
780
781 cv.put(RawContacts._ID, cursor.getLong(ContactQuery.RAW_CONTACT_ID));
782
783 cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_NAME);
784 cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_TYPE);
785 cursorColumnToContentValues(cursor, cv, ContactQuery.DIRTY);
786 cursorColumnToContentValues(cursor, cv, ContactQuery.VERSION);
787 cursorColumnToContentValues(cursor, cv, ContactQuery.SOURCE_ID);
788 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC1);
789 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC2);
790 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC3);
791 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC4);
792 cursorColumnToContentValues(cursor, cv, ContactQuery.DELETED);
793 cursorColumnToContentValues(cursor, cv, ContactQuery.CONTACT_ID);
794 cursorColumnToContentValues(cursor, cv, ContactQuery.STARRED);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700795 cursorColumnToContentValues(cursor, cv, ContactQuery.NAME_VERIFIED);
796
797 return cv;
798 }
799
800 /**
801 * Extracts Data level columns from the cursor.
802 */
803 private ContentValues loadData(Cursor cursor) {
804 ContentValues cv = new ContentValues();
805
806 cv.put(Data._ID, cursor.getLong(ContactQuery.DATA_ID));
807
808 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA1);
809 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA2);
810 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA3);
811 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA4);
812 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA5);
813 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA6);
814 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA7);
815 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA8);
816 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA9);
817 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA10);
818 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA11);
819 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA12);
820 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA13);
821 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA14);
822 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA15);
823 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC1);
824 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC2);
825 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC3);
826 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC4);
827 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_VERSION);
828 cursorColumnToContentValues(cursor, cv, ContactQuery.IS_PRIMARY);
829 cursorColumnToContentValues(cursor, cv, ContactQuery.IS_SUPERPRIMARY);
830 cursorColumnToContentValues(cursor, cv, ContactQuery.MIMETYPE);
831 cursorColumnToContentValues(cursor, cv, ContactQuery.RES_PACKAGE);
832 cursorColumnToContentValues(cursor, cv, ContactQuery.GROUP_SOURCE_ID);
Daniel Lehmann8fd7bb62010-08-13 20:50:31 -0700833 cursorColumnToContentValues(cursor, cv, ContactQuery.CHAT_CAPABILITY);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700834
835 return cv;
836 }
837
838 private void cursorColumnToContentValues(
839 Cursor cursor, ContentValues values, int index) {
840 switch (cursor.getType(index)) {
841 case Cursor.FIELD_TYPE_NULL:
842 // don't put anything in the content values
843 break;
844 case Cursor.FIELD_TYPE_INTEGER:
845 values.put(ContactQuery.COLUMNS[index], cursor.getLong(index));
846 break;
847 case Cursor.FIELD_TYPE_STRING:
848 values.put(ContactQuery.COLUMNS[index], cursor.getString(index));
849 break;
850 case Cursor.FIELD_TYPE_BLOB:
851 values.put(ContactQuery.COLUMNS[index], cursor.getBlob(index));
852 break;
853 default:
854 throw new IllegalStateException("Invalid or unhandled data type");
855 }
856 }
857
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700858 private void loadDirectoryMetaData(Result result) {
859 long directoryId = result.getDirectoryId();
860
861 Cursor cursor = getContext().getContentResolver().query(
862 ContentUris.withAppendedId(Directory.CONTENT_URI, directoryId),
863 DirectoryQuery.COLUMNS, null, null, null);
864 if (cursor == null) {
865 return;
866 }
867 try {
868 if (cursor.moveToFirst()) {
869 final String displayName = cursor.getString(DirectoryQuery.DISPLAY_NAME);
870 final String packageName = cursor.getString(DirectoryQuery.PACKAGE_NAME);
871 final int typeResourceId = cursor.getInt(DirectoryQuery.TYPE_RESOURCE_ID);
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700872 final String accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE);
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700873 final String accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME);
874 final int exportSupport = cursor.getInt(DirectoryQuery.EXPORT_SUPPORT);
875 String directoryType = null;
876 if (!TextUtils.isEmpty(packageName)) {
877 PackageManager pm = getContext().getPackageManager();
878 try {
879 Resources resources = pm.getResourcesForApplication(packageName);
880 directoryType = resources.getString(typeResourceId);
881 } catch (NameNotFoundException e) {
882 Log.w(TAG, "Contact directory resource not found: "
883 + packageName + "." + typeResourceId);
884 }
885 }
886
887 result.setDirectoryMetaData(
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700888 displayName, directoryType, accountType, accountName, exportSupport);
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700889 }
890 } finally {
891 cursor.close();
892 }
893 }
894
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700895 /**
896 * Loads groups meta-data for all groups associated with all constituent raw contacts'
897 * accounts.
898 */
899 private void loadGroupMetaData(Result result) {
900 StringBuilder selection = new StringBuilder();
901 ArrayList<String> selectionArgs = new ArrayList<String>();
902 for (Entity entity : result.mEntities) {
903 ContentValues values = entity.getEntityValues();
904 String accountName = values.getAsString(RawContacts.ACCOUNT_NAME);
905 String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
906 if (accountName != null && accountType != null) {
907 if (selection.length() != 0) {
908 selection.append(" OR ");
909 }
910 selection.append(
911 "(" + Groups.ACCOUNT_NAME + "=? AND " + Groups.ACCOUNT_TYPE + "=?)");
912 selectionArgs.add(accountName);
913 selectionArgs.add(accountType);
914 }
915 }
916 Cursor cursor = getContext().getContentResolver().query(Groups.CONTENT_URI,
917 GroupQuery.COLUMNS, selection.toString(), selectionArgs.toArray(new String[0]),
918 null);
919 try {
920 while (cursor.moveToNext()) {
921 final String accountName = cursor.getString(GroupQuery.ACCOUNT_NAME);
922 final String accountType = cursor.getString(GroupQuery.ACCOUNT_TYPE);
923 final long groupId = cursor.getLong(GroupQuery.ID);
924 final String title = cursor.getString(GroupQuery.TITLE);
925 final boolean defaultGroup = cursor.isNull(GroupQuery.AUTO_ADD)
926 ? false
927 : cursor.getInt(GroupQuery.AUTO_ADD) != 0;
928 final boolean favorites = cursor.isNull(GroupQuery.FAVORITES)
929 ? false
930 : cursor.getInt(GroupQuery.FAVORITES) != 0;
931
Dmitri Plotnikove843f912010-09-16 15:21:48 -0700932 result.addGroupMetaData(new GroupMetaData(
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700933 accountName, accountType, groupId, title, defaultGroup, favorites));
934 }
935 } finally {
936 cursor.close();
937 }
938 }
939
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700940 @Override
941 protected void onPostExecute(Result result) {
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -0800942 unregisterObserver();
943
Daniel Lehmann1316b132010-04-13 15:08:53 -0700944 // The creator isn't interested in any further updates
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -0800945 if (mDestroyed || result == null) {
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700946 return;
947 }
948
949 mContact = result;
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700950
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -0800951 if (result != Result.ERROR && result != Result.NOT_FOUND) {
952 mLookupUri = result.getLookupUri();
953
954 if (!result.isDirectoryEntry()) {
955 Log.i(TAG, "Registering content observer for " + mLookupUri);
956 if (mObserver == null) {
957 mObserver = new ForceLoadContentObserver();
958 }
959 getContext().getContentResolver().registerContentObserver(
960 mLookupUri, true, mObserver);
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700961 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800962
963 if (mContact.getPhotoBinaryData() == null && mContact.getPhotoUri() != null) {
964 mContact.setLoadingPhoto(true);
965 new AsyncPhotoLoader().execute(mContact.getPhotoUri());
966 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700967 }
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -0800968
969 deliverResult(mContact);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700970 }
971 }
972
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800973 private class AsyncPhotoLoader extends AsyncTask<String, Void, byte[]> {
974
975 private static final int BUFFER_SIZE = 1024*16;
976
977 @Override
978 protected byte[] doInBackground(String... params) {
979 Uri uri = Uri.parse(params[0]);
980 byte[] data = null;
981 try {
982 InputStream is = getContext().getContentResolver().openInputStream(uri);
983 if (is != null) {
984 ByteArrayOutputStream baos = new ByteArrayOutputStream();
985 try {
986 byte[] mBuffer = new byte[BUFFER_SIZE];
987
988 int size;
989 while ((size = is.read(mBuffer)) != -1) {
990 baos.write(mBuffer, 0, size);
991 }
992 data = baos.toByteArray();
993 } finally {
994 is.close();
995 }
996 } else {
997 Log.v(TAG, "Cannot load photo " + uri);
998 }
999 } catch (IOException e) {
1000 Log.e(TAG, "Cannot load photo " + uri, e);
1001 }
1002
1003 return data;
1004 }
1005
1006 @Override
1007 protected void onPostExecute(byte[] data) {
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001008 if (mContact != null) {
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -08001009 mContact = new Result(mContact);
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001010 mContact.setPhotoBinaryData(data);
1011 mContact.setLoadingPhoto(false);
1012 deliverResult(mContact);
1013 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -08001014 }
1015 }
1016
Daniel Lehmann3a120772010-06-21 16:21:35 -07001017 private void unregisterObserver() {
1018 if (mObserver != null) {
1019 getContext().getContentResolver().unregisterContentObserver(mObserver);
1020 mObserver = null;
1021 }
1022 }
1023
Daniel Lehmanncdef2b62010-06-06 18:25:49 -07001024 public ContactLoader(Context context, Uri lookupUri) {
Dmitri Plotnikove843f912010-09-16 15:21:48 -07001025 this(context, lookupUri, false);
1026 }
1027
1028 public ContactLoader(Context context, Uri lookupUri, boolean loadGroupMetaData) {
Daniel Lehmann74a2dc52010-04-15 16:52:33 -07001029 super(context);
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001030 mLookupUri = lookupUri;
Dmitri Plotnikove843f912010-09-16 15:21:48 -07001031 mLoadGroupMetaData = loadGroupMetaData;
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001032 }
1033
Dmitri Plotnikov5a30d9a2010-11-23 14:59:50 -08001034 public Uri getLookupUri() {
1035 return mLookupUri;
1036 }
1037
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001038 @Override
Dianne Hackbornc04fc272010-12-20 23:13:10 -08001039 protected void onStartLoading() {
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001040 if (mContact != null) {
Daniel Lehmanncbcc4492010-04-12 18:03:54 -07001041 deliverResult(mContact);
Dmitri Plotnikov97e90c62011-01-03 11:58:13 -08001042 }
1043
1044 if (takeContentChanged() || mContact == null) {
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001045 forceLoad();
1046 }
1047 }
1048
1049 @Override
Dianne Hackbornc04fc272010-12-20 23:13:10 -08001050 protected void onForceLoad() {
Daniel Lehmann18f104f2010-05-07 15:41:11 -07001051 final LoadContactTask task = new LoadContactTask();
Daniel Lehmann74a2dc52010-04-15 16:52:33 -07001052 task.execute((Void[])null);
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001053 }
1054
1055 @Override
Dianne Hackbornc04fc272010-12-20 23:13:10 -08001056 protected void onReset() {
Dianne Hackborn4ef95cc2010-12-16 00:44:33 -08001057 unregisterObserver();
1058 mContact = null;
1059 mDestroyed = true;
1060 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001061}