blob: 11075304380c49645fc9fc1c85a939b3770c31b8 [file] [log] [blame]
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001/*
Daniel Lehmannef3f8f02010-07-26 18:55:25 -07002 * Copyright (C) 2010 The Android Open Source Project
Daniel Lehmann4cd94412010-04-08 16:44:36 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
Dmitri Plotnikov18ffaa22010-12-03 14:28:00 -080017package com.android.contacts;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070018
Makoto Onuki69b4a882011-07-22 10:05:10 -070019import com.android.contacts.model.AccountType;
20import com.android.contacts.model.AccountTypeManager;
Makoto Onuki6ad227f2011-08-15 13:46:59 -070021import com.android.contacts.model.AccountTypeWithDataSet;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070022import com.android.contacts.util.DataStatus;
Dave Santoro39156002011-07-19 01:18:14 -070023import com.android.contacts.util.StreamItemEntry;
24import com.android.contacts.util.StreamItemPhotoEntry;
Makoto Onuki69b4a882011-07-22 10:05:10 -070025import com.google.android.collect.Lists;
Flavio Lerda37a26842011-06-27 11:36:52 +010026import com.google.common.annotations.VisibleForTesting;
Makoto Onuki6ad227f2011-08-15 13:46:59 -070027import com.google.common.collect.Maps;
Makoto Onukiaba2b832011-08-12 15:44:53 -070028import com.google.common.collect.Sets;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070029
30import android.content.ContentResolver;
31import android.content.ContentUris;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -070032import android.content.ContentValues;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070033import android.content.Context;
34import android.content.Entity;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -070035import android.content.Entity.NamedContentValues;
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -070036import android.content.Intent;
Jeff Hamilton3c462912010-05-15 02:20:01 -050037import android.content.Loader;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -070038import android.content.pm.PackageManager;
39import android.content.pm.PackageManager.NameNotFoundException;
Dave Santoro0a2a5db2011-06-29 00:37:06 -070040import android.content.res.AssetFileDescriptor;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -070041import android.content.res.Resources;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070042import android.database.Cursor;
43import android.net.Uri;
44import android.os.AsyncTask;
Daniel Lehmann1316b132010-04-13 15:08:53 -070045import android.provider.ContactsContract;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -070046import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -080047import android.provider.ContactsContract.CommonDataKinds.Photo;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070048import android.provider.ContactsContract.Contacts;
49import android.provider.ContactsContract.Data;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -070050import android.provider.ContactsContract.Directory;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070051import android.provider.ContactsContract.DisplayNameSources;
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -070052import android.provider.ContactsContract.Groups;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070053import android.provider.ContactsContract.RawContacts;
Dave Santoro39156002011-07-19 01:18:14 -070054import android.provider.ContactsContract.StreamItemPhotos;
Makoto Onuki69b4a882011-07-22 10:05:10 -070055import android.provider.ContactsContract.StreamItems;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -070056import android.text.TextUtils;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070057import android.util.Log;
58
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -080059import java.io.ByteArrayOutputStream;
Dave Santoro0a2a5db2011-06-29 00:37:06 -070060import java.io.FileInputStream;
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -080061import java.io.IOException;
62import java.io.InputStream;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070063import java.util.ArrayList;
Dave Santoro39156002011-07-19 01:18:14 -070064import java.util.Collections;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070065import java.util.HashMap;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -070066import java.util.List;
Dave Santoro39156002011-07-19 01:18:14 -070067import java.util.Map;
Makoto Onukiaba2b832011-08-12 15:44:53 -070068import java.util.Set;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070069
70/**
71 * Loads a single Contact and all it constituent RawContacts.
72 */
Daniel Lehmanncdef2b62010-06-06 18:25:49 -070073public class ContactLoader extends Loader<ContactLoader.Result> {
Daniel Lehmann18f104f2010-05-07 15:41:11 -070074 private static final String TAG = "ContactLoader";
75
Daniel Lehmann4cd94412010-04-08 16:44:36 -070076 private Uri mLookupUri;
Dmitri Plotnikove843f912010-09-16 15:21:48 -070077 private boolean mLoadGroupMetaData;
Dave Santoro39156002011-07-19 01:18:14 -070078 private boolean mLoadStreamItems;
Makoto Onuki69b4a882011-07-22 10:05:10 -070079 private final boolean mLoadInvitableAccountTypes;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070080 private Result mContact;
81 private ForceLoadContentObserver mObserver;
82 private boolean mDestroyed;
Makoto Onukiaba2b832011-08-12 15:44:53 -070083 private final Set<Long> mNotifiedRawContactIds = Sets.newHashSet();
Dmitri Plotnikove843f912010-09-16 15:21:48 -070084
Daniel Lehmanncdef2b62010-06-06 18:25:49 -070085 public interface Listener {
Daniel Lehmann4cd94412010-04-08 16:44:36 -070086 public void onContactLoaded(Result contact);
87 }
88
89 /**
90 * The result of a load operation. Contains all data necessary to display the contact.
91 */
92 public static final class Result {
93 /**
94 * Singleton instance that represents "No Contact Found"
95 */
96 public static final Result NOT_FOUND = new Result();
97
Daniel Lehmann18f104f2010-05-07 15:41:11 -070098 /**
99 * Singleton instance that represents an error, e.g. because of an invalid Uri
100 * TODO: We should come up with something nicer here. Maybe use an Either type so
101 * that we can capture the Exception?
102 */
103 public static final Result ERROR = new Result();
104
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700105 private final Uri mLookupUri;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700106 private final Uri mUri;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700107 private final long mDirectoryId;
108 private final String mLookupKey;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700109 private final long mId;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700110 private final long mNameRawContactId;
111 private final int mDisplayNameSource;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700112 private final long mPhotoId;
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700113 private final String mPhotoUri;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700114 private final String mDisplayName;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700115 private final String mAltDisplayName;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700116 private final String mPhoneticName;
117 private final boolean mStarred;
118 private final Integer mPresence;
119 private final ArrayList<Entity> mEntities;
Makoto Onuki870a87e2011-08-12 13:40:31 -0700120 private final ArrayList<StreamItemEntry> mStreamItems;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700121 private final HashMap<Long, DataStatus> mStatuses;
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700122 private final ArrayList<AccountType> mInvitableAccountTypes;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700123
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700124 private String mDirectoryDisplayName;
125 private String mDirectoryType;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700126 private String mDirectoryAccountType;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700127 private String mDirectoryAccountName;
128 private int mDirectoryExportSupport;
129
Dmitri Plotnikove843f912010-09-16 15:21:48 -0700130 private ArrayList<GroupMetaData> mGroups;
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700131
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800132 private boolean mLoadingPhoto;
133 private byte[] mPhotoBinaryData;
Makoto Onuki870a87e2011-08-12 13:40:31 -0700134 private final boolean mSendToVoicemail;
135 private final String mCustomRingtone;
136 private final boolean mIsUserProfile;
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800137
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700138 /**
139 * Constructor for case "no contact found". This must only be used for the
140 * final {@link Result#NOT_FOUND} singleton
141 */
142 private Result() {
143 mLookupUri = null;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700144 mUri = null;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700145 mDirectoryId = -1;
146 mLookupKey = null;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700147 mId = -1;
148 mEntities = null;
Dave Santoro39156002011-07-19 01:18:14 -0700149 mStreamItems = new ArrayList<StreamItemEntry>();
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700150 mStatuses = null;
151 mNameRawContactId = -1;
152 mDisplayNameSource = DisplayNameSources.UNDEFINED;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700153 mPhotoId = -1;
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700154 mPhotoUri = null;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700155 mDisplayName = null;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700156 mAltDisplayName = null;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700157 mPhoneticName = null;
158 mStarred = false;
159 mPresence = null;
Makoto Onuki69b4a882011-07-22 10:05:10 -0700160 mInvitableAccountTypes = null;
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700161 mSendToVoicemail = false;
162 mCustomRingtone = null;
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700163 mIsUserProfile = false;
164
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700165 }
166
167 /**
168 * Constructor to call when contact was found
169 */
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700170 private Result(Uri uri, Uri lookupUri, long directoryId, String lookupKey, long id,
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700171 long nameRawContactId, int displayNameSource, long photoId, String photoUri,
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700172 String displayName, String altDisplayName, String phoneticName, boolean starred,
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700173 Integer presence, boolean sendToVoicemail, String customRingtone,
174 boolean isUserProfile) {
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700175 mLookupUri = lookupUri;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700176 mUri = uri;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700177 mDirectoryId = directoryId;
178 mLookupKey = lookupKey;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700179 mId = id;
180 mEntities = new ArrayList<Entity>();
Dave Santoro39156002011-07-19 01:18:14 -0700181 mStreamItems = new ArrayList<StreamItemEntry>();
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700182 mStatuses = new HashMap<Long, DataStatus>();
183 mNameRawContactId = nameRawContactId;
184 mDisplayNameSource = displayNameSource;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700185 mPhotoId = photoId;
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700186 mPhotoUri = photoUri;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700187 mDisplayName = displayName;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700188 mAltDisplayName = altDisplayName;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700189 mPhoneticName = phoneticName;
190 mStarred = starred;
191 mPresence = presence;
Makoto Onuki69b4a882011-07-22 10:05:10 -0700192 mInvitableAccountTypes = Lists.newArrayList();
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700193 mSendToVoicemail = sendToVoicemail;
194 mCustomRingtone = customRingtone;
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700195 mIsUserProfile = isUserProfile;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700196 }
197
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800198 private Result(Result from) {
199 mLookupUri = from.mLookupUri;
200 mUri = from.mUri;
201 mDirectoryId = from.mDirectoryId;
202 mLookupKey = from.mLookupKey;
203 mId = from.mId;
204 mNameRawContactId = from.mNameRawContactId;
205 mDisplayNameSource = from.mDisplayNameSource;
206 mPhotoId = from.mPhotoId;
207 mPhotoUri = from.mPhotoUri;
208 mDisplayName = from.mDisplayName;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700209 mAltDisplayName = from.mAltDisplayName;
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800210 mPhoneticName = from.mPhoneticName;
211 mStarred = from.mStarred;
212 mPresence = from.mPresence;
213 mEntities = from.mEntities;
Dave Santoro39156002011-07-19 01:18:14 -0700214 mStreamItems = from.mStreamItems;
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800215 mStatuses = from.mStatuses;
Makoto Onuki69b4a882011-07-22 10:05:10 -0700216 mInvitableAccountTypes = from.mInvitableAccountTypes;
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800217
218 mDirectoryDisplayName = from.mDirectoryDisplayName;
219 mDirectoryType = from.mDirectoryType;
220 mDirectoryAccountType = from.mDirectoryAccountType;
221 mDirectoryAccountName = from.mDirectoryAccountName;
222 mDirectoryExportSupport = from.mDirectoryExportSupport;
223
224 mGroups = from.mGroups;
225
226 mLoadingPhoto = from.mLoadingPhoto;
227 mPhotoBinaryData = from.mPhotoBinaryData;
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700228 mSendToVoicemail = from.mSendToVoicemail;
229 mCustomRingtone = from.mCustomRingtone;
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700230 mIsUserProfile = from.mIsUserProfile;
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800231 }
232
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700233 /**
234 * @param exportSupport See {@link Directory#EXPORT_SUPPORT}.
235 */
Daniel Lehmann1ad4d1b2010-10-18 19:20:41 -0700236 private void setDirectoryMetaData(String displayName, String directoryType,
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700237 String accountType, String accountName, int exportSupport) {
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700238 mDirectoryDisplayName = displayName;
239 mDirectoryType = directoryType;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700240 mDirectoryAccountType = accountType;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700241 mDirectoryAccountName = accountName;
242 mDirectoryExportSupport = exportSupport;
243 }
244
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800245 private void setLoadingPhoto(boolean flag) {
246 mLoadingPhoto = flag;
247 }
248
249 private void setPhotoBinaryData(byte[] photoBinaryData) {
250 mPhotoBinaryData = photoBinaryData;
251 }
252
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700253 public Uri getLookupUri() {
254 return mLookupUri;
255 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800256
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700257 public String getLookupKey() {
258 return mLookupKey;
259 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800260
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700261 public Uri getUri() {
262 return mUri;
263 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800264
Flavio Lerda37a26842011-06-27 11:36:52 +0100265 @VisibleForTesting
266 /*package*/ long getId() {
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700267 return mId;
268 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800269
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700270 public long getNameRawContactId() {
271 return mNameRawContactId;
272 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800273
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700274 public int getDisplayNameSource() {
275 return mDisplayNameSource;
276 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800277
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700278 public long getPhotoId() {
279 return mPhotoId;
280 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800281
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700282 public String getPhotoUri() {
283 return mPhotoUri;
284 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800285
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700286 public String getDisplayName() {
287 return mDisplayName;
288 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800289
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700290 public String getAltDisplayName() {
291 return mAltDisplayName;
292 }
293
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700294 public String getPhoneticName() {
295 return mPhoneticName;
296 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800297
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700298 public boolean getStarred() {
299 return mStarred;
300 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800301
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700302 public Integer getPresence() {
303 return mPresence;
304 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800305
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700306 public ArrayList<AccountType> getInvitableAccountTypes() {
Makoto Onuki69b4a882011-07-22 10:05:10 -0700307 return mInvitableAccountTypes;
308 }
309
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700310 public ArrayList<Entity> getEntities() {
311 return mEntities;
312 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800313
Dave Santoro39156002011-07-19 01:18:14 -0700314 public ArrayList<StreamItemEntry> getStreamItems() {
315 return mStreamItems;
316 }
317
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700318 public HashMap<Long, DataStatus> getStatuses() {
319 return mStatuses;
320 }
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700321
322 public long getDirectoryId() {
323 return mDirectoryId;
324 }
325
326 public boolean isDirectoryEntry() {
Dmitri Plotnikov5f72c1f2010-09-01 21:21:04 -0700327 return mDirectoryId != -1 && mDirectoryId != Directory.DEFAULT
328 && mDirectoryId != Directory.LOCAL_INVISIBLE;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700329 }
330
331 public int getDirectoryExportSupport() {
332 return mDirectoryExportSupport;
333 }
334
335 public String getDirectoryDisplayName() {
336 return mDirectoryDisplayName;
337 }
338
339 public String getDirectoryType() {
340 return mDirectoryType;
341 }
342
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700343 public String getDirectoryAccountType() {
344 return mDirectoryAccountType;
345 }
346
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700347 public String getDirectoryAccountName() {
348 return mDirectoryAccountName;
349 }
350
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800351 public boolean isLoadingPhoto() {
352 return mLoadingPhoto;
353 }
354
355 public byte[] getPhotoBinaryData() {
356 return mPhotoBinaryData;
357 }
358
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700359 public ArrayList<ContentValues> getContentValues() {
360 if (mEntities.size() != 1) {
361 throw new IllegalStateException(
362 "Cannot extract content values from an aggregated contact");
363 }
364
365 Entity entity = mEntities.get(0);
366 ArrayList<ContentValues> result = new ArrayList<ContentValues>();
367 ArrayList<NamedContentValues> subValues = entity.getSubValues();
368 if (subValues != null) {
369 int size = subValues.size();
370 for (int i = 0; i < size; i++) {
371 NamedContentValues pair = subValues.get(i);
372 if (Data.CONTENT_URI.equals(pair.uri)) {
373 result.add(pair.values);
374 }
375 }
376 }
Dmitri Plotnikov40ec3a82010-11-10 11:25:33 -0800377
378 // If the photo was loaded using the URI, create an entry for the photo
379 // binary data.
380 if (mPhotoId == 0 && mPhotoBinaryData != null) {
381 ContentValues photo = new ContentValues();
382 photo.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
383 photo.put(Photo.PHOTO, mPhotoBinaryData);
384 result.add(photo);
385 }
386
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700387 return result;
388 }
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700389
Daniel Lehmann1ad4d1b2010-10-18 19:20:41 -0700390 private void addGroupMetaData(GroupMetaData group) {
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700391 if (mGroups == null) {
Dmitri Plotnikove843f912010-09-16 15:21:48 -0700392 mGroups = new ArrayList<GroupMetaData>();
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700393 }
394 mGroups.add(group);
395 }
396
Dmitri Plotnikove843f912010-09-16 15:21:48 -0700397 public List<GroupMetaData> getGroupMetaData() {
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700398 return mGroups;
399 }
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700400
401 public boolean isSendToVoicemail() {
402 return mSendToVoicemail;
403 }
404
405 public String getCustomRingtone() {
406 return mCustomRingtone;
407 }
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700408
409 public boolean isUserProfile() {
410 return mIsUserProfile;
411 }
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700412 }
413
Dave Santoro39156002011-07-19 01:18:14 -0700414 /**
415 * Projection used for the query that loads all data for the entire contact (except for
416 * social stream items).
417 */
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700418 private static class ContactQuery {
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700419 final static String[] COLUMNS = new String[] {
420 Contacts.NAME_RAW_CONTACT_ID,
421 Contacts.DISPLAY_NAME_SOURCE,
422 Contacts.LOOKUP_KEY,
423 Contacts.DISPLAY_NAME,
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700424 Contacts.DISPLAY_NAME_ALTERNATIVE,
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700425 Contacts.PHONETIC_NAME,
426 Contacts.PHOTO_ID,
427 Contacts.STARRED,
428 Contacts.CONTACT_PRESENCE,
429 Contacts.CONTACT_STATUS,
430 Contacts.CONTACT_STATUS_TIMESTAMP,
431 Contacts.CONTACT_STATUS_RES_PACKAGE,
432 Contacts.CONTACT_STATUS_LABEL,
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700433 Contacts.Entity.CONTACT_ID,
434 Contacts.Entity.RAW_CONTACT_ID,
435
436 RawContacts.ACCOUNT_NAME,
437 RawContacts.ACCOUNT_TYPE,
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700438 RawContacts.DATA_SET,
439 RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700440 RawContacts.DIRTY,
441 RawContacts.VERSION,
442 RawContacts.SOURCE_ID,
443 RawContacts.SYNC1,
444 RawContacts.SYNC2,
445 RawContacts.SYNC3,
446 RawContacts.SYNC4,
447 RawContacts.DELETED,
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700448 RawContacts.NAME_VERIFIED,
449
450 Contacts.Entity.DATA_ID,
451 Data.DATA1,
452 Data.DATA2,
453 Data.DATA3,
454 Data.DATA4,
455 Data.DATA5,
456 Data.DATA6,
457 Data.DATA7,
458 Data.DATA8,
459 Data.DATA9,
460 Data.DATA10,
461 Data.DATA11,
462 Data.DATA12,
463 Data.DATA13,
464 Data.DATA14,
465 Data.DATA15,
466 Data.SYNC1,
467 Data.SYNC2,
468 Data.SYNC3,
469 Data.SYNC4,
470 Data.DATA_VERSION,
471 Data.IS_PRIMARY,
472 Data.IS_SUPER_PRIMARY,
473 Data.MIMETYPE,
474 Data.RES_PACKAGE,
475
476 GroupMembership.GROUP_SOURCE_ID,
477
478 Data.PRESENCE,
Daniel Lehmann8fd7bb62010-08-13 20:50:31 -0700479 Data.CHAT_CAPABILITY,
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700480 Data.STATUS,
481 Data.STATUS_RES_PACKAGE,
482 Data.STATUS_ICON,
483 Data.STATUS_LABEL,
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700484 Data.STATUS_TIMESTAMP,
485
486 Contacts.PHOTO_URI,
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700487 Contacts.SEND_TO_VOICEMAIL,
488 Contacts.CUSTOM_RINGTONE,
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700489 Contacts.IS_USER_PROFILE,
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700490 };
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700491
492 public final static int NAME_RAW_CONTACT_ID = 0;
493 public final static int DISPLAY_NAME_SOURCE = 1;
494 public final static int LOOKUP_KEY = 2;
495 public final static int DISPLAY_NAME = 3;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700496 public final static int ALT_DISPLAY_NAME = 4;
497 public final static int PHONETIC_NAME = 5;
498 public final static int PHOTO_ID = 6;
499 public final static int STARRED = 7;
500 public final static int CONTACT_PRESENCE = 8;
501 public final static int CONTACT_STATUS = 9;
502 public final static int CONTACT_STATUS_TIMESTAMP = 10;
503 public final static int CONTACT_STATUS_RES_PACKAGE = 11;
504 public final static int CONTACT_STATUS_LABEL = 12;
505 public final static int CONTACT_ID = 13;
506 public final static int RAW_CONTACT_ID = 14;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700507
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700508 public final static int ACCOUNT_NAME = 15;
509 public final static int ACCOUNT_TYPE = 16;
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700510 public final static int DATA_SET = 17;
511 public final static int ACCOUNT_TYPE_AND_DATA_SET = 18;
512 public final static int DIRTY = 19;
513 public final static int VERSION = 20;
514 public final static int SOURCE_ID = 21;
515 public final static int SYNC1 = 22;
516 public final static int SYNC2 = 23;
517 public final static int SYNC3 = 24;
518 public final static int SYNC4 = 25;
519 public final static int DELETED = 26;
520 public final static int NAME_VERIFIED = 27;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700521
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700522 public final static int DATA_ID = 28;
523 public final static int DATA1 = 29;
524 public final static int DATA2 = 30;
525 public final static int DATA3 = 31;
526 public final static int DATA4 = 32;
527 public final static int DATA5 = 33;
528 public final static int DATA6 = 34;
529 public final static int DATA7 = 35;
530 public final static int DATA8 = 36;
531 public final static int DATA9 = 37;
532 public final static int DATA10 = 38;
533 public final static int DATA11 = 39;
534 public final static int DATA12 = 40;
535 public final static int DATA13 = 41;
536 public final static int DATA14 = 42;
537 public final static int DATA15 = 43;
538 public final static int DATA_SYNC1 = 44;
539 public final static int DATA_SYNC2 = 45;
540 public final static int DATA_SYNC3 = 46;
541 public final static int DATA_SYNC4 = 47;
542 public final static int DATA_VERSION = 48;
543 public final static int IS_PRIMARY = 49;
544 public final static int IS_SUPERPRIMARY = 50;
545 public final static int MIMETYPE = 51;
546 public final static int RES_PACKAGE = 52;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700547
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700548 public final static int GROUP_SOURCE_ID = 53;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700549
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700550 public final static int PRESENCE = 54;
551 public final static int CHAT_CAPABILITY = 55;
552 public final static int STATUS = 56;
553 public final static int STATUS_RES_PACKAGE = 57;
554 public final static int STATUS_ICON = 58;
555 public final static int STATUS_LABEL = 59;
556 public final static int STATUS_TIMESTAMP = 60;
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700557
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700558 public final static int PHOTO_URI = 61;
559 public final static int SEND_TO_VOICEMAIL = 62;
560 public final static int CUSTOM_RINGTONE = 63;
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700561 public final static int IS_USER_PROFILE = 64;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700562 }
Daniel Lehmann1316b132010-04-13 15:08:53 -0700563
Dave Santoro39156002011-07-19 01:18:14 -0700564 /**
565 * Projection used for the query that loads all data for the entire contact.
566 */
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700567 private static class DirectoryQuery {
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700568 final static String[] COLUMNS = new String[] {
569 Directory.DISPLAY_NAME,
570 Directory.PACKAGE_NAME,
571 Directory.TYPE_RESOURCE_ID,
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700572 Directory.ACCOUNT_TYPE,
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700573 Directory.ACCOUNT_NAME,
574 Directory.EXPORT_SUPPORT,
575 };
576
577 public final static int DISPLAY_NAME = 0;
578 public final static int PACKAGE_NAME = 1;
579 public final static int TYPE_RESOURCE_ID = 2;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700580 public final static int ACCOUNT_TYPE = 3;
581 public final static int ACCOUNT_NAME = 4;
582 public final static int EXPORT_SUPPORT = 5;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700583 }
584
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700585 private static class GroupQuery {
586 final static String[] COLUMNS = new String[] {
587 Groups.ACCOUNT_NAME,
588 Groups.ACCOUNT_TYPE,
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700589 Groups.DATA_SET,
590 Groups.ACCOUNT_TYPE_AND_DATA_SET,
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700591 Groups._ID,
592 Groups.TITLE,
593 Groups.AUTO_ADD,
594 Groups.FAVORITES,
595 };
596
597 public final static int ACCOUNT_NAME = 0;
598 public final static int ACCOUNT_TYPE = 1;
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700599 public final static int DATA_SET = 2;
600 public final static int ACCOUNT_TYPE_AND_DATA_SET = 3;
601 public final static int ID = 4;
602 public final static int TITLE = 5;
603 public final static int AUTO_ADD = 6;
604 public final static int FAVORITES = 7;
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700605 }
606
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700607 private final class LoadContactTask extends AsyncTask<Void, Void, Result> {
Daniel Lehmann1316b132010-04-13 15:08:53 -0700608
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700609 @Override
610 protected Result doInBackground(Void... args) {
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700611 try {
612 final ContentResolver resolver = getContext().getContentResolver();
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700613 final Uri uriCurrentFormat = ensureIsContactUri(resolver, mLookupUri);
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700614 Result result = loadContactEntity(resolver, uriCurrentFormat);
Dmitri Plotnikov217245c2010-09-18 13:04:50 -0700615 if (result != Result.NOT_FOUND) {
616 if (result.isDirectoryEntry()) {
617 loadDirectoryMetaData(result);
618 } else if (mLoadGroupMetaData) {
619 loadGroupMetaData(result);
620 }
Dave Santoro39156002011-07-19 01:18:14 -0700621 if (mLoadStreamItems) {
622 loadStreamItems(result);
623 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800624 loadPhotoBinaryData(result);
Makoto Onuki870a87e2011-08-12 13:40:31 -0700625
626 // Note ME profile should never have "Add connection"
627 if (mLoadInvitableAccountTypes && !result.isUserProfile()) {
Makoto Onuki69b4a882011-07-22 10:05:10 -0700628 loadInvitableAccountTypes(result);
629 }
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700630 }
631 return result;
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700632 } catch (Exception e) {
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700633 Log.e(TAG, "Error loading the contact: " + mLookupUri, e);
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700634 return Result.ERROR;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700635 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700636 }
637
638 /**
Daniel Lehmann1316b132010-04-13 15:08:53 -0700639 * Transforms the given Uri and returns a Lookup-Uri that represents the contact.
640 * For legacy contacts, a raw-contact lookup is performed.
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700641 * @param resolver
Daniel Lehmann1316b132010-04-13 15:08:53 -0700642 */
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700643 private Uri ensureIsContactUri(final ContentResolver resolver, final Uri uri) {
Daniel Lehmann1316b132010-04-13 15:08:53 -0700644 if (uri == null) throw new IllegalArgumentException("uri must not be null");
645
646 final String authority = uri.getAuthority();
647
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700648 // Current Style Uri?
Daniel Lehmann1316b132010-04-13 15:08:53 -0700649 if (ContactsContract.AUTHORITY.equals(authority)) {
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700650 final String type = resolver.getType(uri);
651 // Contact-Uri? Good, return it
652 if (Contacts.CONTENT_ITEM_TYPE.equals(type)) {
653 return uri;
654 }
655
656 // RawContact-Uri? Transform it to ContactUri
657 if (RawContacts.CONTENT_ITEM_TYPE.equals(type)) {
658 final long rawContactId = ContentUris.parseId(uri);
659 return RawContacts.getContactLookupUri(getContext().getContentResolver(),
660 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId));
661 }
662
663 // Anything else? We don't know what this is
664 throw new IllegalArgumentException("uri format is unknown");
Daniel Lehmann1316b132010-04-13 15:08:53 -0700665 }
666
667 // Legacy Style? Convert to RawContact
668 final String OBSOLETE_AUTHORITY = "contacts";
669 if (OBSOLETE_AUTHORITY.equals(authority)) {
670 // Legacy Format. Convert to RawContact-Uri and then lookup the contact
671 final long rawContactId = ContentUris.parseId(uri);
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700672 return RawContacts.getContactLookupUri(resolver,
Daniel Lehmann1316b132010-04-13 15:08:53 -0700673 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId));
674 }
675
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700676 throw new IllegalArgumentException("uri authority is unknown");
Daniel Lehmann1316b132010-04-13 15:08:53 -0700677 }
678
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700679 private Result loadContactEntity(ContentResolver resolver, Uri contactUri) {
680 Uri entityUri = Uri.withAppendedPath(contactUri, Contacts.Entity.CONTENT_DIRECTORY);
681 Cursor cursor = resolver.query(entityUri, ContactQuery.COLUMNS, null, null,
682 Contacts.Entity.RAW_CONTACT_ID);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700683 if (cursor == null) {
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700684 Log.e(TAG, "No cursor returned in loadContactEntity");
685 return Result.NOT_FOUND;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700686 }
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700687
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700688 try {
689 if (!cursor.moveToFirst()) {
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700690 cursor.close();
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700691 return Result.NOT_FOUND;
692 }
693
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700694 long currentRawContactId = -1;
695 Entity entity = null;
696 Result result = loadContactHeaderData(cursor, contactUri);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700697 ArrayList<Entity> entities = result.getEntities();
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700698 HashMap<Long, DataStatus> statuses = result.getStatuses();
699 for (; !cursor.isAfterLast(); cursor.moveToNext()) {
700 long rawContactId = cursor.getLong(ContactQuery.RAW_CONTACT_ID);
701 if (rawContactId != currentRawContactId) {
702 currentRawContactId = rawContactId;
703 entity = new android.content.Entity(loadRawContact(cursor));
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700704 entities.add(entity);
705 }
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700706 if (!cursor.isNull(ContactQuery.DATA_ID)) {
707 ContentValues data = loadData(cursor);
708 entity.addSubValue(ContactsContract.Data.CONTENT_URI, data);
709
710 if (!cursor.isNull(ContactQuery.PRESENCE)
711 || !cursor.isNull(ContactQuery.STATUS)) {
712 final DataStatus status = new DataStatus(cursor);
713 final long dataId = cursor.getLong(ContactQuery.DATA_ID);
714 statuses.put(dataId, status);
715 }
716 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700717 }
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700718
719 return result;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700720 } finally {
721 cursor.close();
722 }
723 }
724
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700725 /**
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800726 * Looks for the photo data item in entities. If found, creates a new Bitmap instance. If
727 * not found, returns null
728 */
729 private void loadPhotoBinaryData(Result contactData) {
Dave Santoro0a2a5db2011-06-29 00:37:06 -0700730
731 // If we have a photo URI, try loading that first.
732 String photoUri = contactData.getPhotoUri();
733 if (photoUri != null) {
734 try {
735 AssetFileDescriptor fd = getContext().getContentResolver()
736 .openAssetFileDescriptor(Uri.parse(photoUri), "r");
737 byte[] buffer = new byte[16 * 1024];
738 FileInputStream fis = fd.createInputStream();
739 ByteArrayOutputStream baos = new ByteArrayOutputStream();
740 try {
741 int size;
742 while ((size = fis.read(buffer)) != -1) {
743 baos.write(buffer, 0, size);
744 }
745 contactData.setPhotoBinaryData(baos.toByteArray());
746 } finally {
747 fis.close();
748 fd.close();
749 }
750 return;
751 } catch (IOException ioe) {
752 // Just fall back to the case below.
753 }
754 }
755
756 // If we couldn't load from a file, fall back to the data blob.
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800757 final long photoId = contactData.getPhotoId();
758 if (photoId <= 0) {
759 // No photo ID
760 return;
761 }
762
763 for (Entity entity : contactData.getEntities()) {
764 for (NamedContentValues subValue : entity.getSubValues()) {
765 final ContentValues entryValues = subValue.values;
766 final long dataId = entryValues.getAsLong(Data._ID);
767 if (dataId == photoId) {
768 final String mimeType = entryValues.getAsString(Data.MIMETYPE);
769 // Correct Data Id but incorrect MimeType? Don't load
770 if (!Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
771 return;
772 }
773 contactData.setPhotoBinaryData(entryValues.getAsByteArray(Photo.PHOTO));
774 break;
775 }
776 }
777 }
778 }
779
Makoto Onuki3e6991e2011-07-24 14:51:20 -0700780 /**
781 * Sets the "invitable" account types to {@link Result#mInvitableAccountTypes}.
782 *
783 * TODO Exclude the ones with no raw contacts in the database.
784 */
Makoto Onuki69b4a882011-07-22 10:05:10 -0700785 private void loadInvitableAccountTypes(Result contactData) {
Makoto Onuki6ad227f2011-08-15 13:46:59 -0700786 Map<AccountTypeWithDataSet, AccountType> allInvitables =
Makoto Onuki69b4a882011-07-22 10:05:10 -0700787 AccountTypeManager.getInstance(getContext()).getInvitableAccountTypes();
788 if (allInvitables.isEmpty()) {
789 return;
790 }
791
Makoto Onuki6ad227f2011-08-15 13:46:59 -0700792 HashMap<AccountTypeWithDataSet, AccountType> result = Maps.newHashMap(allInvitables);
Makoto Onuki69b4a882011-07-22 10:05:10 -0700793
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -0700794 // Remove the ones that already have a raw contact in the current contact
Makoto Onuki69b4a882011-07-22 10:05:10 -0700795 for (Entity entity : contactData.getEntities()) {
Makoto Onuki6ad227f2011-08-15 13:46:59 -0700796 final ContentValues values = entity.getEntityValues();
797 final AccountTypeWithDataSet type = AccountTypeWithDataSet.get(
798 values.getAsString(RawContacts.ACCOUNT_TYPE),
799 values.getAsString(RawContacts.DATA_SET));
800 result.remove(type);
Makoto Onuki69b4a882011-07-22 10:05:10 -0700801 }
802
803 // Set to mInvitableAccountTypes
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700804 contactData.mInvitableAccountTypes.addAll(result.values());
Makoto Onuki69b4a882011-07-22 10:05:10 -0700805 }
806
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800807 /**
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700808 * Extracts Contact level columns from the cursor.
809 */
810 private Result loadContactHeaderData(final Cursor cursor, Uri contactUri) {
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700811 final String directoryParameter =
812 contactUri.getQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY);
813 final long directoryId = directoryParameter == null
814 ? Directory.DEFAULT
815 : Long.parseLong(directoryParameter);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700816 final long contactId = cursor.getLong(ContactQuery.CONTACT_ID);
817 final String lookupKey = cursor.getString(ContactQuery.LOOKUP_KEY);
818 final long nameRawContactId = cursor.getLong(ContactQuery.NAME_RAW_CONTACT_ID);
819 final int displayNameSource = cursor.getInt(ContactQuery.DISPLAY_NAME_SOURCE);
820 final String displayName = cursor.getString(ContactQuery.DISPLAY_NAME);
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700821 final String altDisplayName = cursor.getString(ContactQuery.ALT_DISPLAY_NAME);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700822 final String phoneticName = cursor.getString(ContactQuery.PHONETIC_NAME);
823 final long photoId = cursor.getLong(ContactQuery.PHOTO_ID);
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700824 final String photoUri = cursor.getString(ContactQuery.PHOTO_URI);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700825 final boolean starred = cursor.getInt(ContactQuery.STARRED) != 0;
826 final Integer presence = cursor.isNull(ContactQuery.CONTACT_PRESENCE)
827 ? null
828 : cursor.getInt(ContactQuery.CONTACT_PRESENCE);
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700829 final boolean sendToVoicemail = cursor.getInt(ContactQuery.SEND_TO_VOICEMAIL) == 1;
830 final String customRingtone = cursor.getString(ContactQuery.CUSTOM_RINGTONE);
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700831 final boolean isUserProfile = cursor.getInt(ContactQuery.IS_USER_PROFILE) == 1;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700832
Dmitri Plotnikov1536ea12010-10-29 11:51:05 -0700833 Uri lookupUri;
834 if (directoryId == Directory.DEFAULT || directoryId == Directory.LOCAL_INVISIBLE) {
835 lookupUri = ContentUris.withAppendedId(
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700836 Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey), contactId);
Dmitri Plotnikov1536ea12010-10-29 11:51:05 -0700837 } else {
838 lookupUri = contactUri;
839 }
840
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700841 return new Result(contactUri, lookupUri, directoryId, lookupKey, contactId,
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700842 nameRawContactId, displayNameSource, photoId, photoUri, displayName,
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700843 altDisplayName, phoneticName, starred, presence, sendToVoicemail,
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700844 customRingtone, isUserProfile);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700845 }
846
847 /**
848 * Extracts RawContact level columns from the cursor.
849 */
850 private ContentValues loadRawContact(Cursor cursor) {
851 ContentValues cv = new ContentValues();
852
853 cv.put(RawContacts._ID, cursor.getLong(ContactQuery.RAW_CONTACT_ID));
854
855 cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_NAME);
856 cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_TYPE);
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700857 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SET);
858 cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_TYPE_AND_DATA_SET);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700859 cursorColumnToContentValues(cursor, cv, ContactQuery.DIRTY);
860 cursorColumnToContentValues(cursor, cv, ContactQuery.VERSION);
861 cursorColumnToContentValues(cursor, cv, ContactQuery.SOURCE_ID);
862 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC1);
863 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC2);
864 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC3);
865 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC4);
866 cursorColumnToContentValues(cursor, cv, ContactQuery.DELETED);
867 cursorColumnToContentValues(cursor, cv, ContactQuery.CONTACT_ID);
868 cursorColumnToContentValues(cursor, cv, ContactQuery.STARRED);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700869 cursorColumnToContentValues(cursor, cv, ContactQuery.NAME_VERIFIED);
870
871 return cv;
872 }
873
874 /**
875 * Extracts Data level columns from the cursor.
876 */
877 private ContentValues loadData(Cursor cursor) {
878 ContentValues cv = new ContentValues();
879
880 cv.put(Data._ID, cursor.getLong(ContactQuery.DATA_ID));
881
882 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA1);
883 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA2);
884 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA3);
885 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA4);
886 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA5);
887 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA6);
888 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA7);
889 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA8);
890 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA9);
891 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA10);
892 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA11);
893 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA12);
894 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA13);
895 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA14);
896 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA15);
897 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC1);
898 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC2);
899 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC3);
900 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC4);
901 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_VERSION);
902 cursorColumnToContentValues(cursor, cv, ContactQuery.IS_PRIMARY);
903 cursorColumnToContentValues(cursor, cv, ContactQuery.IS_SUPERPRIMARY);
904 cursorColumnToContentValues(cursor, cv, ContactQuery.MIMETYPE);
905 cursorColumnToContentValues(cursor, cv, ContactQuery.RES_PACKAGE);
906 cursorColumnToContentValues(cursor, cv, ContactQuery.GROUP_SOURCE_ID);
Daniel Lehmann8fd7bb62010-08-13 20:50:31 -0700907 cursorColumnToContentValues(cursor, cv, ContactQuery.CHAT_CAPABILITY);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700908
909 return cv;
910 }
911
912 private void cursorColumnToContentValues(
913 Cursor cursor, ContentValues values, int index) {
914 switch (cursor.getType(index)) {
915 case Cursor.FIELD_TYPE_NULL:
916 // don't put anything in the content values
917 break;
918 case Cursor.FIELD_TYPE_INTEGER:
919 values.put(ContactQuery.COLUMNS[index], cursor.getLong(index));
920 break;
921 case Cursor.FIELD_TYPE_STRING:
922 values.put(ContactQuery.COLUMNS[index], cursor.getString(index));
923 break;
924 case Cursor.FIELD_TYPE_BLOB:
925 values.put(ContactQuery.COLUMNS[index], cursor.getBlob(index));
926 break;
927 default:
928 throw new IllegalStateException("Invalid or unhandled data type");
929 }
930 }
931
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700932 private void loadDirectoryMetaData(Result result) {
933 long directoryId = result.getDirectoryId();
934
935 Cursor cursor = getContext().getContentResolver().query(
936 ContentUris.withAppendedId(Directory.CONTENT_URI, directoryId),
937 DirectoryQuery.COLUMNS, null, null, null);
938 if (cursor == null) {
939 return;
940 }
941 try {
942 if (cursor.moveToFirst()) {
943 final String displayName = cursor.getString(DirectoryQuery.DISPLAY_NAME);
944 final String packageName = cursor.getString(DirectoryQuery.PACKAGE_NAME);
945 final int typeResourceId = cursor.getInt(DirectoryQuery.TYPE_RESOURCE_ID);
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700946 final String accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE);
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700947 final String accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME);
948 final int exportSupport = cursor.getInt(DirectoryQuery.EXPORT_SUPPORT);
949 String directoryType = null;
950 if (!TextUtils.isEmpty(packageName)) {
951 PackageManager pm = getContext().getPackageManager();
952 try {
953 Resources resources = pm.getResourcesForApplication(packageName);
954 directoryType = resources.getString(typeResourceId);
955 } catch (NameNotFoundException e) {
956 Log.w(TAG, "Contact directory resource not found: "
957 + packageName + "." + typeResourceId);
958 }
959 }
960
961 result.setDirectoryMetaData(
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700962 displayName, directoryType, accountType, accountName, exportSupport);
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700963 }
964 } finally {
965 cursor.close();
966 }
967 }
968
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700969 /**
970 * Loads groups meta-data for all groups associated with all constituent raw contacts'
971 * accounts.
972 */
973 private void loadGroupMetaData(Result result) {
974 StringBuilder selection = new StringBuilder();
975 ArrayList<String> selectionArgs = new ArrayList<String>();
976 for (Entity entity : result.mEntities) {
977 ContentValues values = entity.getEntityValues();
978 String accountName = values.getAsString(RawContacts.ACCOUNT_NAME);
979 String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700980 String dataSet = values.getAsString(RawContacts.DATA_SET);
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700981 if (accountName != null && accountType != null) {
982 if (selection.length() != 0) {
983 selection.append(" OR ");
984 }
985 selection.append(
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700986 "(" + Groups.ACCOUNT_NAME + "=? AND " + Groups.ACCOUNT_TYPE + "=?");
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700987 selectionArgs.add(accountName);
988 selectionArgs.add(accountType);
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700989
990 if (dataSet != null) {
991 selection.append(" AND " + Groups.DATA_SET + "=?");
992 selectionArgs.add(dataSet);
993 } else {
994 selection.append(" AND " + Groups.DATA_SET + " IS NULL");
995 }
996 selection.append(")");
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700997 }
998 }
999 Cursor cursor = getContext().getContentResolver().query(Groups.CONTENT_URI,
1000 GroupQuery.COLUMNS, selection.toString(), selectionArgs.toArray(new String[0]),
1001 null);
1002 try {
1003 while (cursor.moveToNext()) {
1004 final String accountName = cursor.getString(GroupQuery.ACCOUNT_NAME);
1005 final String accountType = cursor.getString(GroupQuery.ACCOUNT_TYPE);
Dave Santoro2b3f3c52011-07-26 17:35:42 -07001006 final String dataSet = cursor.getString(GroupQuery.DATA_SET);
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001007 final long groupId = cursor.getLong(GroupQuery.ID);
1008 final String title = cursor.getString(GroupQuery.TITLE);
1009 final boolean defaultGroup = cursor.isNull(GroupQuery.AUTO_ADD)
1010 ? false
1011 : cursor.getInt(GroupQuery.AUTO_ADD) != 0;
1012 final boolean favorites = cursor.isNull(GroupQuery.FAVORITES)
1013 ? false
1014 : cursor.getInt(GroupQuery.FAVORITES) != 0;
1015
Dmitri Plotnikove843f912010-09-16 15:21:48 -07001016 result.addGroupMetaData(new GroupMetaData(
Dave Santoro2b3f3c52011-07-26 17:35:42 -07001017 accountName, accountType, dataSet, groupId, title, defaultGroup,
1018 favorites));
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001019 }
1020 } finally {
1021 cursor.close();
1022 }
1023 }
1024
Dave Santoro39156002011-07-19 01:18:14 -07001025 /**
1026 * Loads all stream items and stream item photos belonging to this contact.
1027 */
1028 private void loadStreamItems(Result result) {
1029 Cursor cursor = getContext().getContentResolver().query(
1030 Contacts.CONTENT_LOOKUP_URI.buildUpon()
1031 .appendPath(result.getLookupKey())
1032 .appendPath(Contacts.StreamItems.CONTENT_DIRECTORY).build(),
1033 null, null, null, null);
1034 Map<Long, StreamItemEntry> streamItemsById = new HashMap<Long, StreamItemEntry>();
1035 ArrayList<StreamItemEntry> streamItems = new ArrayList<StreamItemEntry>();
1036 try {
1037 while (cursor.moveToNext()) {
1038 StreamItemEntry streamItem = new StreamItemEntry(cursor);
1039 streamItemsById.put(streamItem.getId(), streamItem);
1040 streamItems.add(streamItem);
1041 }
1042 } finally {
1043 cursor.close();
1044 }
1045
1046 // Now retrieve any photo records associated with the stream items.
1047 String[] streamItemIdArr = new String[streamItems.size()];
1048 StringBuilder streamItemPhotoSelection = new StringBuilder();
1049 if (!streamItems.isEmpty()) {
1050 streamItemPhotoSelection.append(StreamItemPhotos.STREAM_ITEM_ID + " IN (");
1051 for (int i = 0; i < streamItems.size(); i++) {
1052 if (i > 0) {
1053 streamItemPhotoSelection.append(",");
1054 }
1055 streamItemPhotoSelection.append("?");
1056 streamItemIdArr[i] = String.valueOf(streamItems.get(i).getId());
1057 }
1058 streamItemPhotoSelection.append(")");
1059 cursor = getContext().getContentResolver().query(StreamItems.CONTENT_PHOTO_URI,
1060 null, streamItemPhotoSelection.toString(), streamItemIdArr,
1061 StreamItemPhotos.STREAM_ITEM_ID);
1062 try {
1063 while (cursor.moveToNext()) {
1064 long streamItemId = cursor.getLong(
1065 cursor.getColumnIndex(StreamItemPhotos.STREAM_ITEM_ID));
1066 StreamItemEntry streamItem = streamItemsById.get(streamItemId);
1067 streamItem.addPhoto(new StreamItemPhotoEntry(cursor));
1068 }
1069 } finally {
1070 cursor.close();
1071 }
1072 }
1073
1074 // Set the sorted stream items on the result.
1075 Collections.sort(streamItems);
1076 result.mStreamItems.addAll(streamItems);
1077 }
1078
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001079 @Override
1080 protected void onPostExecute(Result result) {
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001081 unregisterObserver();
1082
Daniel Lehmann1316b132010-04-13 15:08:53 -07001083 // The creator isn't interested in any further updates
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001084 if (mDestroyed || result == null) {
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001085 return;
1086 }
1087
1088 mContact = result;
Daniel Lehmann18f104f2010-05-07 15:41:11 -07001089
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001090 if (result != Result.ERROR && result != Result.NOT_FOUND) {
1091 mLookupUri = result.getLookupUri();
1092
1093 if (!result.isDirectoryEntry()) {
1094 Log.i(TAG, "Registering content observer for " + mLookupUri);
1095 if (mObserver == null) {
1096 mObserver = new ForceLoadContentObserver();
1097 }
1098 getContext().getContentResolver().registerContentObserver(
1099 mLookupUri, true, mObserver);
Daniel Lehmann18f104f2010-05-07 15:41:11 -07001100 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -08001101
1102 if (mContact.getPhotoBinaryData() == null && mContact.getPhotoUri() != null) {
1103 mContact.setLoadingPhoto(true);
1104 new AsyncPhotoLoader().execute(mContact.getPhotoUri());
1105 }
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001106
1107 // inform the source of the data that this contact is being looked at
1108 postViewNotificationToSyncAdapter();
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001109 }
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001110
1111 deliverResult(mContact);
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001112 }
1113 }
1114
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001115 /**
1116 * Posts a message to the contributing sync adapters that have opted-in, notifying them
1117 * that the contact has just been loaded
1118 */
1119 private void postViewNotificationToSyncAdapter() {
1120 Context context = getContext();
1121 for (Entity entity : mContact.getEntities()) {
1122 final ContentValues entityValues = entity.getEntityValues();
Makoto Onukiaba2b832011-08-12 15:44:53 -07001123 final long rawContactId = entityValues.getAsLong(RawContacts.Entity._ID);
1124 if (mNotifiedRawContactIds.contains(rawContactId)) {
1125 continue; // Already notified for this raw contact.
1126 }
1127 mNotifiedRawContactIds.add(rawContactId);
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001128 final String type = entityValues.getAsString(RawContacts.ACCOUNT_TYPE);
1129 final String dataSet = entityValues.getAsString(RawContacts.DATA_SET);
Flavio Lerda59a887e2011-08-14 18:13:17 +01001130 final AccountType accountType = AccountTypeManager.getInstance(context).getAccountType(
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001131 type, dataSet);
1132 final String serviceName = accountType.getViewContactNotifyServiceClassName();
1133 final String resPackageName = accountType.resPackageName;
1134 if (!TextUtils.isEmpty(serviceName) && !TextUtils.isEmpty(resPackageName)) {
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001135 final Uri uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
1136 final Intent intent = new Intent();
1137 intent.setClassName(resPackageName, serviceName);
1138 intent.setAction(Intent.ACTION_VIEW);
1139 intent.setDataAndType(uri, RawContacts.CONTENT_ITEM_TYPE);
1140 try {
1141 context.startService(intent);
1142 } catch (Exception e) {
1143 Log.e(TAG, "Error sending message to source-app", e);
1144 }
1145 }
1146 }
1147 }
1148
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -08001149 private class AsyncPhotoLoader extends AsyncTask<String, Void, byte[]> {
1150
1151 private static final int BUFFER_SIZE = 1024*16;
1152
1153 @Override
1154 protected byte[] doInBackground(String... params) {
1155 Uri uri = Uri.parse(params[0]);
1156 byte[] data = null;
1157 try {
1158 InputStream is = getContext().getContentResolver().openInputStream(uri);
1159 if (is != null) {
1160 ByteArrayOutputStream baos = new ByteArrayOutputStream();
1161 try {
1162 byte[] mBuffer = new byte[BUFFER_SIZE];
1163
1164 int size;
1165 while ((size = is.read(mBuffer)) != -1) {
1166 baos.write(mBuffer, 0, size);
1167 }
1168 data = baos.toByteArray();
1169 } finally {
1170 is.close();
1171 }
1172 } else {
1173 Log.v(TAG, "Cannot load photo " + uri);
1174 }
1175 } catch (IOException e) {
1176 Log.e(TAG, "Cannot load photo " + uri, e);
1177 }
1178
1179 return data;
1180 }
1181
1182 @Override
1183 protected void onPostExecute(byte[] data) {
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001184 if (mContact != null) {
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -08001185 mContact = new Result(mContact);
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001186 mContact.setPhotoBinaryData(data);
1187 mContact.setLoadingPhoto(false);
1188 deliverResult(mContact);
1189 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -08001190 }
1191 }
1192
Daniel Lehmann3a120772010-06-21 16:21:35 -07001193 private void unregisterObserver() {
1194 if (mObserver != null) {
1195 getContext().getContentResolver().unregisterContentObserver(mObserver);
1196 mObserver = null;
1197 }
1198 }
1199
Daniel Lehmanncdef2b62010-06-06 18:25:49 -07001200 public ContactLoader(Context context, Uri lookupUri) {
Makoto Onuki69b4a882011-07-22 10:05:10 -07001201 this(context, lookupUri, false, false, false);
Dmitri Plotnikove843f912010-09-16 15:21:48 -07001202 }
1203
Dave Santoro39156002011-07-19 01:18:14 -07001204 public ContactLoader(Context context, Uri lookupUri, boolean loadGroupMetaData,
Makoto Onuki69b4a882011-07-22 10:05:10 -07001205 boolean loadStreamItems, boolean loadInvitableAccountTypes) {
Daniel Lehmann74a2dc52010-04-15 16:52:33 -07001206 super(context);
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001207 mLookupUri = lookupUri;
Dmitri Plotnikove843f912010-09-16 15:21:48 -07001208 mLoadGroupMetaData = loadGroupMetaData;
Dave Santoro39156002011-07-19 01:18:14 -07001209 mLoadStreamItems = loadStreamItems;
Makoto Onuki69b4a882011-07-22 10:05:10 -07001210 mLoadInvitableAccountTypes = loadInvitableAccountTypes;
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001211 }
1212
Dmitri Plotnikov5a30d9a2010-11-23 14:59:50 -08001213 public Uri getLookupUri() {
1214 return mLookupUri;
1215 }
1216
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001217 @Override
Dianne Hackbornc04fc272010-12-20 23:13:10 -08001218 protected void onStartLoading() {
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001219 if (mContact != null) {
Daniel Lehmanncbcc4492010-04-12 18:03:54 -07001220 deliverResult(mContact);
Dmitri Plotnikov97e90c62011-01-03 11:58:13 -08001221 }
1222
1223 if (takeContentChanged() || mContact == null) {
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001224 forceLoad();
1225 }
1226 }
1227
1228 @Override
Dianne Hackbornc04fc272010-12-20 23:13:10 -08001229 protected void onForceLoad() {
Daniel Lehmann18f104f2010-05-07 15:41:11 -07001230 final LoadContactTask task = new LoadContactTask();
Daniel Lehmann74a2dc52010-04-15 16:52:33 -07001231 task.execute((Void[])null);
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001232 }
1233
1234 @Override
Dianne Hackbornc04fc272010-12-20 23:13:10 -08001235 protected void onReset() {
Dianne Hackborn4ef95cc2010-12-16 00:44:33 -08001236 unregisterObserver();
1237 mContact = null;
1238 mDestroyed = true;
1239 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001240}