blob: 4b0bbaad77a7451043c536094d85056d54331f34 [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;
Dave Santoroda5bf1c2011-05-03 10:30:34 -070096 private final String mAltDisplayName;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -070097 private final String mPhoneticName;
98 private final boolean mStarred;
99 private final Integer mPresence;
100 private final ArrayList<Entity> mEntities;
101 private final HashMap<Long, DataStatus> mStatuses;
102 private final String mStatus;
103 private final Long mStatusTimestamp;
104 private final Integer mStatusLabel;
105 private final String mStatusResPackage;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700106
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700107 private String mDirectoryDisplayName;
108 private String mDirectoryType;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700109 private String mDirectoryAccountType;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700110 private String mDirectoryAccountName;
111 private int mDirectoryExportSupport;
112
Dmitri Plotnikove843f912010-09-16 15:21:48 -0700113 private ArrayList<GroupMetaData> mGroups;
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700114
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800115 private boolean mLoadingPhoto;
116 private byte[] mPhotoBinaryData;
117
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700118 /**
119 * Constructor for case "no contact found". This must only be used for the
120 * final {@link Result#NOT_FOUND} singleton
121 */
122 private Result() {
123 mLookupUri = null;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700124 mUri = null;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700125 mDirectoryId = -1;
126 mLookupKey = null;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700127 mId = -1;
128 mEntities = null;
129 mStatuses = null;
130 mNameRawContactId = -1;
131 mDisplayNameSource = DisplayNameSources.UNDEFINED;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700132 mPhotoId = -1;
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700133 mPhotoUri = null;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700134 mDisplayName = null;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700135 mAltDisplayName = null;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700136 mPhoneticName = null;
137 mStarred = false;
138 mPresence = null;
139 mStatus = null;
140 mStatusTimestamp = null;
141 mStatusLabel = null;
142 mStatusResPackage = null;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700143 }
144
145 /**
146 * Constructor to call when contact was found
147 */
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700148 private Result(Uri uri, Uri lookupUri, long directoryId, String lookupKey, long id,
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700149 long nameRawContactId, int displayNameSource, long photoId, String photoUri,
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700150 String displayName, String altDisplayName, String phoneticName, boolean starred,
151 Integer presence, String status, Long statusTimestamp, Integer statusLabel,
152 String statusResPackage) {
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700153 mLookupUri = lookupUri;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700154 mUri = uri;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700155 mDirectoryId = directoryId;
156 mLookupKey = lookupKey;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700157 mId = id;
158 mEntities = new ArrayList<Entity>();
159 mStatuses = new HashMap<Long, DataStatus>();
160 mNameRawContactId = nameRawContactId;
161 mDisplayNameSource = displayNameSource;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700162 mPhotoId = photoId;
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700163 mPhotoUri = photoUri;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700164 mDisplayName = displayName;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700165 mAltDisplayName = altDisplayName;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700166 mPhoneticName = phoneticName;
167 mStarred = starred;
168 mPresence = presence;
169 mStatus = status;
170 mStatusTimestamp = statusTimestamp;
171 mStatusLabel = statusLabel;
172 mStatusResPackage = statusResPackage;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700173 }
174
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800175 private Result(Result from) {
176 mLookupUri = from.mLookupUri;
177 mUri = from.mUri;
178 mDirectoryId = from.mDirectoryId;
179 mLookupKey = from.mLookupKey;
180 mId = from.mId;
181 mNameRawContactId = from.mNameRawContactId;
182 mDisplayNameSource = from.mDisplayNameSource;
183 mPhotoId = from.mPhotoId;
184 mPhotoUri = from.mPhotoUri;
185 mDisplayName = from.mDisplayName;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700186 mAltDisplayName = from.mAltDisplayName;
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800187 mPhoneticName = from.mPhoneticName;
188 mStarred = from.mStarred;
189 mPresence = from.mPresence;
190 mEntities = from.mEntities;
191 mStatuses = from.mStatuses;
192 mStatus = from.mStatus;
193 mStatusTimestamp = from.mStatusTimestamp;
194 mStatusLabel = from.mStatusLabel;
195 mStatusResPackage = from.mStatusResPackage;
196
197 mDirectoryDisplayName = from.mDirectoryDisplayName;
198 mDirectoryType = from.mDirectoryType;
199 mDirectoryAccountType = from.mDirectoryAccountType;
200 mDirectoryAccountName = from.mDirectoryAccountName;
201 mDirectoryExportSupport = from.mDirectoryExportSupport;
202
203 mGroups = from.mGroups;
204
205 mLoadingPhoto = from.mLoadingPhoto;
206 mPhotoBinaryData = from.mPhotoBinaryData;
207 }
208
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700209 /**
210 * @param exportSupport See {@link Directory#EXPORT_SUPPORT}.
211 */
Daniel Lehmann1ad4d1b2010-10-18 19:20:41 -0700212 private void setDirectoryMetaData(String displayName, String directoryType,
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700213 String accountType, String accountName, int exportSupport) {
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700214 mDirectoryDisplayName = displayName;
215 mDirectoryType = directoryType;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700216 mDirectoryAccountType = accountType;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700217 mDirectoryAccountName = accountName;
218 mDirectoryExportSupport = exportSupport;
219 }
220
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800221 private void setLoadingPhoto(boolean flag) {
222 mLoadingPhoto = flag;
223 }
224
225 private void setPhotoBinaryData(byte[] photoBinaryData) {
226 mPhotoBinaryData = photoBinaryData;
227 }
228
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700229 public Uri getLookupUri() {
230 return mLookupUri;
231 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800232
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700233 public String getLookupKey() {
234 return mLookupKey;
235 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800236
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700237 public Uri getUri() {
238 return mUri;
239 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800240
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700241 public long getId() {
242 return mId;
243 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800244
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700245 public long getNameRawContactId() {
246 return mNameRawContactId;
247 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800248
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700249 public int getDisplayNameSource() {
250 return mDisplayNameSource;
251 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800252
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700253 public long getPhotoId() {
254 return mPhotoId;
255 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800256
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700257 public String getPhotoUri() {
258 return mPhotoUri;
259 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800260
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700261 public String getDisplayName() {
262 return mDisplayName;
263 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800264
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700265 public String getAltDisplayName() {
266 return mAltDisplayName;
267 }
268
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700269 public String getPhoneticName() {
270 return mPhoneticName;
271 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800272
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700273 public boolean getStarred() {
274 return mStarred;
275 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800276
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700277 public Integer getPresence() {
278 return mPresence;
279 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800280
Daniel Lehmann2d4f7592010-10-12 23:31:31 -0700281 public String getSocialSnippet() {
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700282 return mStatus;
283 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800284
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700285 public Long getStatusTimestamp() {
286 return mStatusTimestamp;
287 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800288
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700289 public Integer getStatusLabel() {
290 return mStatusLabel;
291 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800292
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700293 public String getStatusResPackage() {
294 return mStatusResPackage;
295 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800296
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700297 public ArrayList<Entity> getEntities() {
298 return mEntities;
299 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800300
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700301 public HashMap<Long, DataStatus> getStatuses() {
302 return mStatuses;
303 }
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700304
305 public long getDirectoryId() {
306 return mDirectoryId;
307 }
308
309 public boolean isDirectoryEntry() {
Dmitri Plotnikov5f72c1f2010-09-01 21:21:04 -0700310 return mDirectoryId != -1 && mDirectoryId != Directory.DEFAULT
311 && mDirectoryId != Directory.LOCAL_INVISIBLE;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700312 }
313
314 public int getDirectoryExportSupport() {
315 return mDirectoryExportSupport;
316 }
317
318 public String getDirectoryDisplayName() {
319 return mDirectoryDisplayName;
320 }
321
322 public String getDirectoryType() {
323 return mDirectoryType;
324 }
325
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700326 public String getDirectoryAccountType() {
327 return mDirectoryAccountType;
328 }
329
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700330 public String getDirectoryAccountName() {
331 return mDirectoryAccountName;
332 }
333
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800334 public boolean isLoadingPhoto() {
335 return mLoadingPhoto;
336 }
337
338 public byte[] getPhotoBinaryData() {
339 return mPhotoBinaryData;
340 }
341
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700342 public ArrayList<ContentValues> getContentValues() {
343 if (mEntities.size() != 1) {
344 throw new IllegalStateException(
345 "Cannot extract content values from an aggregated contact");
346 }
347
348 Entity entity = mEntities.get(0);
349 ArrayList<ContentValues> result = new ArrayList<ContentValues>();
350 ArrayList<NamedContentValues> subValues = entity.getSubValues();
351 if (subValues != null) {
352 int size = subValues.size();
353 for (int i = 0; i < size; i++) {
354 NamedContentValues pair = subValues.get(i);
355 if (Data.CONTENT_URI.equals(pair.uri)) {
356 result.add(pair.values);
357 }
358 }
359 }
Dmitri Plotnikov40ec3a82010-11-10 11:25:33 -0800360
361 // If the photo was loaded using the URI, create an entry for the photo
362 // binary data.
363 if (mPhotoId == 0 && mPhotoBinaryData != null) {
364 ContentValues photo = new ContentValues();
365 photo.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
366 photo.put(Photo.PHOTO, mPhotoBinaryData);
367 result.add(photo);
368 }
369
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700370 return result;
371 }
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700372
Daniel Lehmann1ad4d1b2010-10-18 19:20:41 -0700373 private void addGroupMetaData(GroupMetaData group) {
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700374 if (mGroups == null) {
Dmitri Plotnikove843f912010-09-16 15:21:48 -0700375 mGroups = new ArrayList<GroupMetaData>();
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700376 }
377 mGroups.add(group);
378 }
379
Dmitri Plotnikove843f912010-09-16 15:21:48 -0700380 public List<GroupMetaData> getGroupMetaData() {
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700381 return mGroups;
382 }
383 }
384
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700385 private static class ContactQuery {
386 // Projection used for the query that loads all data for the entire contact.
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700387 final static String[] COLUMNS = new String[] {
388 Contacts.NAME_RAW_CONTACT_ID,
389 Contacts.DISPLAY_NAME_SOURCE,
390 Contacts.LOOKUP_KEY,
391 Contacts.DISPLAY_NAME,
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700392 Contacts.DISPLAY_NAME_ALTERNATIVE,
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700393 Contacts.PHONETIC_NAME,
394 Contacts.PHOTO_ID,
395 Contacts.STARRED,
396 Contacts.CONTACT_PRESENCE,
397 Contacts.CONTACT_STATUS,
398 Contacts.CONTACT_STATUS_TIMESTAMP,
399 Contacts.CONTACT_STATUS_RES_PACKAGE,
400 Contacts.CONTACT_STATUS_LABEL,
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700401 Contacts.Entity.CONTACT_ID,
402 Contacts.Entity.RAW_CONTACT_ID,
403
404 RawContacts.ACCOUNT_NAME,
405 RawContacts.ACCOUNT_TYPE,
406 RawContacts.DIRTY,
407 RawContacts.VERSION,
408 RawContacts.SOURCE_ID,
409 RawContacts.SYNC1,
410 RawContacts.SYNC2,
411 RawContacts.SYNC3,
412 RawContacts.SYNC4,
413 RawContacts.DELETED,
414 RawContacts.IS_RESTRICTED,
415 RawContacts.NAME_VERIFIED,
416
417 Contacts.Entity.DATA_ID,
418 Data.DATA1,
419 Data.DATA2,
420 Data.DATA3,
421 Data.DATA4,
422 Data.DATA5,
423 Data.DATA6,
424 Data.DATA7,
425 Data.DATA8,
426 Data.DATA9,
427 Data.DATA10,
428 Data.DATA11,
429 Data.DATA12,
430 Data.DATA13,
431 Data.DATA14,
432 Data.DATA15,
433 Data.SYNC1,
434 Data.SYNC2,
435 Data.SYNC3,
436 Data.SYNC4,
437 Data.DATA_VERSION,
438 Data.IS_PRIMARY,
439 Data.IS_SUPER_PRIMARY,
440 Data.MIMETYPE,
441 Data.RES_PACKAGE,
442
443 GroupMembership.GROUP_SOURCE_ID,
444
445 Data.PRESENCE,
Daniel Lehmann8fd7bb62010-08-13 20:50:31 -0700446 Data.CHAT_CAPABILITY,
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700447 Data.STATUS,
448 Data.STATUS_RES_PACKAGE,
449 Data.STATUS_ICON,
450 Data.STATUS_LABEL,
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700451 Data.STATUS_TIMESTAMP,
452
453 Contacts.PHOTO_URI,
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700454 };
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700455
456 public final static int NAME_RAW_CONTACT_ID = 0;
457 public final static int DISPLAY_NAME_SOURCE = 1;
458 public final static int LOOKUP_KEY = 2;
459 public final static int DISPLAY_NAME = 3;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700460 public final static int ALT_DISPLAY_NAME = 4;
461 public final static int PHONETIC_NAME = 5;
462 public final static int PHOTO_ID = 6;
463 public final static int STARRED = 7;
464 public final static int CONTACT_PRESENCE = 8;
465 public final static int CONTACT_STATUS = 9;
466 public final static int CONTACT_STATUS_TIMESTAMP = 10;
467 public final static int CONTACT_STATUS_RES_PACKAGE = 11;
468 public final static int CONTACT_STATUS_LABEL = 12;
469 public final static int CONTACT_ID = 13;
470 public final static int RAW_CONTACT_ID = 14;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700471
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700472 public final static int ACCOUNT_NAME = 15;
473 public final static int ACCOUNT_TYPE = 16;
474 public final static int DIRTY = 17;
475 public final static int VERSION = 18;
476 public final static int SOURCE_ID = 19;
477 public final static int SYNC1 = 20;
478 public final static int SYNC2 = 21;
479 public final static int SYNC3 = 22;
480 public final static int SYNC4 = 23;
481 public final static int DELETED = 24;
482 public final static int IS_RESTRICTED = 25;
483 public final static int NAME_VERIFIED = 26;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700484
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700485 public final static int DATA_ID = 27;
486 public final static int DATA1 = 28;
487 public final static int DATA2 = 29;
488 public final static int DATA3 = 30;
489 public final static int DATA4 = 31;
490 public final static int DATA5 = 32;
491 public final static int DATA6 = 33;
492 public final static int DATA7 = 34;
493 public final static int DATA8 = 35;
494 public final static int DATA9 = 36;
495 public final static int DATA10 = 37;
496 public final static int DATA11 = 38;
497 public final static int DATA12 = 39;
498 public final static int DATA13 = 40;
499 public final static int DATA14 = 41;
500 public final static int DATA15 = 42;
501 public final static int DATA_SYNC1 = 43;
502 public final static int DATA_SYNC2 = 44;
503 public final static int DATA_SYNC3 = 45;
504 public final static int DATA_SYNC4 = 46;
505 public final static int DATA_VERSION = 47;
506 public final static int IS_PRIMARY = 48;
507 public final static int IS_SUPERPRIMARY = 49;
508 public final static int MIMETYPE = 50;
509 public final static int RES_PACKAGE = 51;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700510
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700511 public final static int GROUP_SOURCE_ID = 52;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700512
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700513 public final static int PRESENCE = 53;
514 public final static int CHAT_CAPABILITY = 54;
515 public final static int STATUS = 55;
516 public final static int STATUS_RES_PACKAGE = 56;
517 public final static int STATUS_ICON = 57;
518 public final static int STATUS_LABEL = 58;
519 public final static int STATUS_TIMESTAMP = 59;
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700520
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700521 public final static int PHOTO_URI = 60;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700522 }
Daniel Lehmann1316b132010-04-13 15:08:53 -0700523
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700524 private static class DirectoryQuery {
525 // Projection used for the query that loads all data for the entire contact.
526 final static String[] COLUMNS = new String[] {
527 Directory.DISPLAY_NAME,
528 Directory.PACKAGE_NAME,
529 Directory.TYPE_RESOURCE_ID,
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700530 Directory.ACCOUNT_TYPE,
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700531 Directory.ACCOUNT_NAME,
532 Directory.EXPORT_SUPPORT,
533 };
534
535 public final static int DISPLAY_NAME = 0;
536 public final static int PACKAGE_NAME = 1;
537 public final static int TYPE_RESOURCE_ID = 2;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700538 public final static int ACCOUNT_TYPE = 3;
539 public final static int ACCOUNT_NAME = 4;
540 public final static int EXPORT_SUPPORT = 5;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700541 }
542
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700543 private static class GroupQuery {
544 final static String[] COLUMNS = new String[] {
545 Groups.ACCOUNT_NAME,
546 Groups.ACCOUNT_TYPE,
547 Groups._ID,
548 Groups.TITLE,
549 Groups.AUTO_ADD,
550 Groups.FAVORITES,
551 };
552
553 public final static int ACCOUNT_NAME = 0;
554 public final static int ACCOUNT_TYPE = 1;
555 public final static int ID = 2;
556 public final static int TITLE = 3;
557 public final static int AUTO_ADD = 4;
558 public final static int FAVORITES = 5;
559 }
560
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700561 private final class LoadContactTask extends AsyncTask<Void, Void, Result> {
Daniel Lehmann1316b132010-04-13 15:08:53 -0700562
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700563 @Override
564 protected Result doInBackground(Void... args) {
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700565 try {
566 final ContentResolver resolver = getContext().getContentResolver();
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700567 final Uri uriCurrentFormat = ensureIsContactUri(resolver, mLookupUri);
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700568 Result result = loadContactEntity(resolver, uriCurrentFormat);
Dmitri Plotnikov217245c2010-09-18 13:04:50 -0700569 if (result != Result.NOT_FOUND) {
570 if (result.isDirectoryEntry()) {
571 loadDirectoryMetaData(result);
572 } else if (mLoadGroupMetaData) {
573 loadGroupMetaData(result);
574 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800575 loadPhotoBinaryData(result);
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700576 }
577 return result;
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700578 } catch (Exception e) {
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700579 Log.e(TAG, "Error loading the contact: " + mLookupUri, e);
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700580 return Result.ERROR;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700581 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700582 }
583
584 /**
Daniel Lehmann1316b132010-04-13 15:08:53 -0700585 * Transforms the given Uri and returns a Lookup-Uri that represents the contact.
586 * For legacy contacts, a raw-contact lookup is performed.
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700587 * @param resolver
Daniel Lehmann1316b132010-04-13 15:08:53 -0700588 */
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700589 private Uri ensureIsContactUri(final ContentResolver resolver, final Uri uri) {
Daniel Lehmann1316b132010-04-13 15:08:53 -0700590 if (uri == null) throw new IllegalArgumentException("uri must not be null");
591
592 final String authority = uri.getAuthority();
593
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700594 // Current Style Uri?
Daniel Lehmann1316b132010-04-13 15:08:53 -0700595 if (ContactsContract.AUTHORITY.equals(authority)) {
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700596 final String type = resolver.getType(uri);
597 // Contact-Uri? Good, return it
598 if (Contacts.CONTENT_ITEM_TYPE.equals(type)) {
599 return uri;
600 }
601
602 // RawContact-Uri? Transform it to ContactUri
603 if (RawContacts.CONTENT_ITEM_TYPE.equals(type)) {
604 final long rawContactId = ContentUris.parseId(uri);
605 return RawContacts.getContactLookupUri(getContext().getContentResolver(),
606 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId));
607 }
608
609 // Anything else? We don't know what this is
610 throw new IllegalArgumentException("uri format is unknown");
Daniel Lehmann1316b132010-04-13 15:08:53 -0700611 }
612
613 // Legacy Style? Convert to RawContact
614 final String OBSOLETE_AUTHORITY = "contacts";
615 if (OBSOLETE_AUTHORITY.equals(authority)) {
616 // Legacy Format. Convert to RawContact-Uri and then lookup the contact
617 final long rawContactId = ContentUris.parseId(uri);
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700618 return RawContacts.getContactLookupUri(resolver,
Daniel Lehmann1316b132010-04-13 15:08:53 -0700619 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId));
620 }
621
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700622 throw new IllegalArgumentException("uri authority is unknown");
Daniel Lehmann1316b132010-04-13 15:08:53 -0700623 }
624
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700625 private Result loadContactEntity(ContentResolver resolver, Uri contactUri) {
626 Uri entityUri = Uri.withAppendedPath(contactUri, Contacts.Entity.CONTENT_DIRECTORY);
627 Cursor cursor = resolver.query(entityUri, ContactQuery.COLUMNS, null, null,
628 Contacts.Entity.RAW_CONTACT_ID);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700629 if (cursor == null) {
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700630 Log.e(TAG, "No cursor returned in loadContactEntity");
631 return Result.NOT_FOUND;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700632 }
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700633
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700634 try {
635 if (!cursor.moveToFirst()) {
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700636 cursor.close();
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700637 return Result.NOT_FOUND;
638 }
639
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700640 long currentRawContactId = -1;
641 Entity entity = null;
642 Result result = loadContactHeaderData(cursor, contactUri);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700643 ArrayList<Entity> entities = result.getEntities();
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700644 HashMap<Long, DataStatus> statuses = result.getStatuses();
645 for (; !cursor.isAfterLast(); cursor.moveToNext()) {
646 long rawContactId = cursor.getLong(ContactQuery.RAW_CONTACT_ID);
647 if (rawContactId != currentRawContactId) {
648 currentRawContactId = rawContactId;
649 entity = new android.content.Entity(loadRawContact(cursor));
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700650 entities.add(entity);
651 }
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700652 if (!cursor.isNull(ContactQuery.DATA_ID)) {
653 ContentValues data = loadData(cursor);
654 entity.addSubValue(ContactsContract.Data.CONTENT_URI, data);
655
656 if (!cursor.isNull(ContactQuery.PRESENCE)
657 || !cursor.isNull(ContactQuery.STATUS)) {
658 final DataStatus status = new DataStatus(cursor);
659 final long dataId = cursor.getLong(ContactQuery.DATA_ID);
660 statuses.put(dataId, status);
661 }
662 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700663 }
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700664
665 return result;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700666 } finally {
667 cursor.close();
668 }
669 }
670
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700671 /**
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800672 * Looks for the photo data item in entities. If found, creates a new Bitmap instance. If
673 * not found, returns null
674 */
675 private void loadPhotoBinaryData(Result contactData) {
676 final long photoId = contactData.getPhotoId();
677 if (photoId <= 0) {
678 // No photo ID
679 return;
680 }
681
682 for (Entity entity : contactData.getEntities()) {
683 for (NamedContentValues subValue : entity.getSubValues()) {
684 final ContentValues entryValues = subValue.values;
685 final long dataId = entryValues.getAsLong(Data._ID);
686 if (dataId == photoId) {
687 final String mimeType = entryValues.getAsString(Data.MIMETYPE);
688 // Correct Data Id but incorrect MimeType? Don't load
689 if (!Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
690 return;
691 }
692 contactData.setPhotoBinaryData(entryValues.getAsByteArray(Photo.PHOTO));
693 break;
694 }
695 }
696 }
697 }
698
699 /**
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700700 * Extracts Contact level columns from the cursor.
701 */
702 private Result loadContactHeaderData(final Cursor cursor, Uri contactUri) {
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700703 final String directoryParameter =
704 contactUri.getQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY);
705 final long directoryId = directoryParameter == null
706 ? Directory.DEFAULT
707 : Long.parseLong(directoryParameter);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700708 final long contactId = cursor.getLong(ContactQuery.CONTACT_ID);
709 final String lookupKey = cursor.getString(ContactQuery.LOOKUP_KEY);
710 final long nameRawContactId = cursor.getLong(ContactQuery.NAME_RAW_CONTACT_ID);
711 final int displayNameSource = cursor.getInt(ContactQuery.DISPLAY_NAME_SOURCE);
712 final String displayName = cursor.getString(ContactQuery.DISPLAY_NAME);
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700713 final String altDisplayName = cursor.getString(ContactQuery.ALT_DISPLAY_NAME);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700714 final String phoneticName = cursor.getString(ContactQuery.PHONETIC_NAME);
715 final long photoId = cursor.getLong(ContactQuery.PHOTO_ID);
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700716 final String photoUri = cursor.getString(ContactQuery.PHOTO_URI);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700717 final boolean starred = cursor.getInt(ContactQuery.STARRED) != 0;
718 final Integer presence = cursor.isNull(ContactQuery.CONTACT_PRESENCE)
719 ? null
720 : cursor.getInt(ContactQuery.CONTACT_PRESENCE);
721 final String status = cursor.getString(ContactQuery.CONTACT_STATUS);
722 final Long statusTimestamp = cursor.isNull(ContactQuery.CONTACT_STATUS_TIMESTAMP)
723 ? null
724 : cursor.getLong(ContactQuery.CONTACT_STATUS_TIMESTAMP);
725 final Integer statusLabel = cursor.isNull(ContactQuery.CONTACT_STATUS_LABEL)
726 ? null
727 : cursor.getInt(ContactQuery.CONTACT_STATUS_LABEL);
728 final String statusResPackage = cursor.getString(
729 ContactQuery.CONTACT_STATUS_RES_PACKAGE);
730
Dmitri Plotnikov1536ea12010-10-29 11:51:05 -0700731 Uri lookupUri;
732 if (directoryId == Directory.DEFAULT || directoryId == Directory.LOCAL_INVISIBLE) {
733 lookupUri = ContentUris.withAppendedId(
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700734 Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey), contactId);
Dmitri Plotnikov1536ea12010-10-29 11:51:05 -0700735 } else {
736 lookupUri = contactUri;
737 }
738
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700739 return new Result(contactUri, lookupUri, directoryId, lookupKey, contactId,
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700740 nameRawContactId, displayNameSource, photoId, photoUri, displayName,
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700741 altDisplayName, phoneticName, starred, presence, status, statusTimestamp,
742 statusLabel, statusResPackage);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700743 }
744
745 /**
746 * Extracts RawContact level columns from the cursor.
747 */
748 private ContentValues loadRawContact(Cursor cursor) {
749 ContentValues cv = new ContentValues();
750
751 cv.put(RawContacts._ID, cursor.getLong(ContactQuery.RAW_CONTACT_ID));
752
753 cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_NAME);
754 cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_TYPE);
755 cursorColumnToContentValues(cursor, cv, ContactQuery.DIRTY);
756 cursorColumnToContentValues(cursor, cv, ContactQuery.VERSION);
757 cursorColumnToContentValues(cursor, cv, ContactQuery.SOURCE_ID);
758 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC1);
759 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC2);
760 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC3);
761 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC4);
762 cursorColumnToContentValues(cursor, cv, ContactQuery.DELETED);
763 cursorColumnToContentValues(cursor, cv, ContactQuery.CONTACT_ID);
764 cursorColumnToContentValues(cursor, cv, ContactQuery.STARRED);
765 cursorColumnToContentValues(cursor, cv, ContactQuery.IS_RESTRICTED);
766 cursorColumnToContentValues(cursor, cv, ContactQuery.NAME_VERIFIED);
767
768 return cv;
769 }
770
771 /**
772 * Extracts Data level columns from the cursor.
773 */
774 private ContentValues loadData(Cursor cursor) {
775 ContentValues cv = new ContentValues();
776
777 cv.put(Data._ID, cursor.getLong(ContactQuery.DATA_ID));
778
779 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA1);
780 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA2);
781 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA3);
782 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA4);
783 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA5);
784 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA6);
785 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA7);
786 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA8);
787 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA9);
788 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA10);
789 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA11);
790 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA12);
791 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA13);
792 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA14);
793 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA15);
794 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC1);
795 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC2);
796 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC3);
797 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC4);
798 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_VERSION);
799 cursorColumnToContentValues(cursor, cv, ContactQuery.IS_PRIMARY);
800 cursorColumnToContentValues(cursor, cv, ContactQuery.IS_SUPERPRIMARY);
801 cursorColumnToContentValues(cursor, cv, ContactQuery.MIMETYPE);
802 cursorColumnToContentValues(cursor, cv, ContactQuery.RES_PACKAGE);
803 cursorColumnToContentValues(cursor, cv, ContactQuery.GROUP_SOURCE_ID);
Daniel Lehmann8fd7bb62010-08-13 20:50:31 -0700804 cursorColumnToContentValues(cursor, cv, ContactQuery.CHAT_CAPABILITY);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700805
806 return cv;
807 }
808
809 private void cursorColumnToContentValues(
810 Cursor cursor, ContentValues values, int index) {
811 switch (cursor.getType(index)) {
812 case Cursor.FIELD_TYPE_NULL:
813 // don't put anything in the content values
814 break;
815 case Cursor.FIELD_TYPE_INTEGER:
816 values.put(ContactQuery.COLUMNS[index], cursor.getLong(index));
817 break;
818 case Cursor.FIELD_TYPE_STRING:
819 values.put(ContactQuery.COLUMNS[index], cursor.getString(index));
820 break;
821 case Cursor.FIELD_TYPE_BLOB:
822 values.put(ContactQuery.COLUMNS[index], cursor.getBlob(index));
823 break;
824 default:
825 throw new IllegalStateException("Invalid or unhandled data type");
826 }
827 }
828
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700829 private void loadDirectoryMetaData(Result result) {
830 long directoryId = result.getDirectoryId();
831
832 Cursor cursor = getContext().getContentResolver().query(
833 ContentUris.withAppendedId(Directory.CONTENT_URI, directoryId),
834 DirectoryQuery.COLUMNS, null, null, null);
835 if (cursor == null) {
836 return;
837 }
838 try {
839 if (cursor.moveToFirst()) {
840 final String displayName = cursor.getString(DirectoryQuery.DISPLAY_NAME);
841 final String packageName = cursor.getString(DirectoryQuery.PACKAGE_NAME);
842 final int typeResourceId = cursor.getInt(DirectoryQuery.TYPE_RESOURCE_ID);
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700843 final String accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE);
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700844 final String accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME);
845 final int exportSupport = cursor.getInt(DirectoryQuery.EXPORT_SUPPORT);
846 String directoryType = null;
847 if (!TextUtils.isEmpty(packageName)) {
848 PackageManager pm = getContext().getPackageManager();
849 try {
850 Resources resources = pm.getResourcesForApplication(packageName);
851 directoryType = resources.getString(typeResourceId);
852 } catch (NameNotFoundException e) {
853 Log.w(TAG, "Contact directory resource not found: "
854 + packageName + "." + typeResourceId);
855 }
856 }
857
858 result.setDirectoryMetaData(
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700859 displayName, directoryType, accountType, accountName, exportSupport);
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700860 }
861 } finally {
862 cursor.close();
863 }
864 }
865
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700866 /**
867 * Loads groups meta-data for all groups associated with all constituent raw contacts'
868 * accounts.
869 */
870 private void loadGroupMetaData(Result result) {
871 StringBuilder selection = new StringBuilder();
872 ArrayList<String> selectionArgs = new ArrayList<String>();
873 for (Entity entity : result.mEntities) {
874 ContentValues values = entity.getEntityValues();
875 String accountName = values.getAsString(RawContacts.ACCOUNT_NAME);
876 String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
877 if (accountName != null && accountType != null) {
878 if (selection.length() != 0) {
879 selection.append(" OR ");
880 }
881 selection.append(
882 "(" + Groups.ACCOUNT_NAME + "=? AND " + Groups.ACCOUNT_TYPE + "=?)");
883 selectionArgs.add(accountName);
884 selectionArgs.add(accountType);
885 }
886 }
887 Cursor cursor = getContext().getContentResolver().query(Groups.CONTENT_URI,
888 GroupQuery.COLUMNS, selection.toString(), selectionArgs.toArray(new String[0]),
889 null);
890 try {
891 while (cursor.moveToNext()) {
892 final String accountName = cursor.getString(GroupQuery.ACCOUNT_NAME);
893 final String accountType = cursor.getString(GroupQuery.ACCOUNT_TYPE);
894 final long groupId = cursor.getLong(GroupQuery.ID);
895 final String title = cursor.getString(GroupQuery.TITLE);
896 final boolean defaultGroup = cursor.isNull(GroupQuery.AUTO_ADD)
897 ? false
898 : cursor.getInt(GroupQuery.AUTO_ADD) != 0;
899 final boolean favorites = cursor.isNull(GroupQuery.FAVORITES)
900 ? false
901 : cursor.getInt(GroupQuery.FAVORITES) != 0;
902
Dmitri Plotnikove843f912010-09-16 15:21:48 -0700903 result.addGroupMetaData(new GroupMetaData(
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700904 accountName, accountType, groupId, title, defaultGroup, favorites));
905 }
906 } finally {
907 cursor.close();
908 }
909 }
910
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700911 @Override
912 protected void onPostExecute(Result result) {
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -0800913 unregisterObserver();
914
Daniel Lehmann1316b132010-04-13 15:08:53 -0700915 // The creator isn't interested in any further updates
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -0800916 if (mDestroyed || result == null) {
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700917 return;
918 }
919
920 mContact = result;
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700921
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -0800922 if (result != Result.ERROR && result != Result.NOT_FOUND) {
923 mLookupUri = result.getLookupUri();
924
925 if (!result.isDirectoryEntry()) {
926 Log.i(TAG, "Registering content observer for " + mLookupUri);
927 if (mObserver == null) {
928 mObserver = new ForceLoadContentObserver();
929 }
930 getContext().getContentResolver().registerContentObserver(
931 mLookupUri, true, mObserver);
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700932 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800933
934 if (mContact.getPhotoBinaryData() == null && mContact.getPhotoUri() != null) {
935 mContact.setLoadingPhoto(true);
936 new AsyncPhotoLoader().execute(mContact.getPhotoUri());
937 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700938 }
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -0800939
940 deliverResult(mContact);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700941 }
942 }
943
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800944 private class AsyncPhotoLoader extends AsyncTask<String, Void, byte[]> {
945
946 private static final int BUFFER_SIZE = 1024*16;
947
948 @Override
949 protected byte[] doInBackground(String... params) {
950 Uri uri = Uri.parse(params[0]);
951 byte[] data = null;
952 try {
953 InputStream is = getContext().getContentResolver().openInputStream(uri);
954 if (is != null) {
955 ByteArrayOutputStream baos = new ByteArrayOutputStream();
956 try {
957 byte[] mBuffer = new byte[BUFFER_SIZE];
958
959 int size;
960 while ((size = is.read(mBuffer)) != -1) {
961 baos.write(mBuffer, 0, size);
962 }
963 data = baos.toByteArray();
964 } finally {
965 is.close();
966 }
967 } else {
968 Log.v(TAG, "Cannot load photo " + uri);
969 }
970 } catch (IOException e) {
971 Log.e(TAG, "Cannot load photo " + uri, e);
972 }
973
974 return data;
975 }
976
977 @Override
978 protected void onPostExecute(byte[] data) {
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -0800979 if (mContact != null) {
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800980 mContact = new Result(mContact);
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -0800981 mContact.setPhotoBinaryData(data);
982 mContact.setLoadingPhoto(false);
983 deliverResult(mContact);
984 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800985 }
986 }
987
Daniel Lehmann3a120772010-06-21 16:21:35 -0700988 private void unregisterObserver() {
989 if (mObserver != null) {
990 getContext().getContentResolver().unregisterContentObserver(mObserver);
991 mObserver = null;
992 }
993 }
994
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700995 public ContactLoader(Context context, Uri lookupUri) {
Dmitri Plotnikove843f912010-09-16 15:21:48 -0700996 this(context, lookupUri, false);
997 }
998
999 public ContactLoader(Context context, Uri lookupUri, boolean loadGroupMetaData) {
Daniel Lehmann74a2dc52010-04-15 16:52:33 -07001000 super(context);
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001001 mLookupUri = lookupUri;
Dmitri Plotnikove843f912010-09-16 15:21:48 -07001002 mLoadGroupMetaData = loadGroupMetaData;
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001003 }
1004
Dmitri Plotnikov5a30d9a2010-11-23 14:59:50 -08001005 public Uri getLookupUri() {
1006 return mLookupUri;
1007 }
1008
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001009 @Override
Dianne Hackbornc04fc272010-12-20 23:13:10 -08001010 protected void onStartLoading() {
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001011 if (mContact != null) {
Daniel Lehmanncbcc4492010-04-12 18:03:54 -07001012 deliverResult(mContact);
Dmitri Plotnikov97e90c62011-01-03 11:58:13 -08001013 }
1014
1015 if (takeContentChanged() || mContact == null) {
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001016 forceLoad();
1017 }
1018 }
1019
1020 @Override
Dianne Hackbornc04fc272010-12-20 23:13:10 -08001021 protected void onForceLoad() {
Daniel Lehmann18f104f2010-05-07 15:41:11 -07001022 final LoadContactTask task = new LoadContactTask();
Daniel Lehmann74a2dc52010-04-15 16:52:33 -07001023 task.execute((Void[])null);
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001024 }
1025
1026 @Override
Dianne Hackbornc04fc272010-12-20 23:13:10 -08001027 protected void onReset() {
Dianne Hackborn4ef95cc2010-12-16 00:44:33 -08001028 unregisterObserver();
1029 mContact = null;
1030 mDestroyed = true;
1031 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001032}