blob: 57defcfef1f17e3d15569a9e0ddf49d99199cbd8 [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;
Daniel Lehmann685157e2011-08-29 21:07:01 -070077 private final Uri mRequestedUri;
Dmitri Plotnikove843f912010-09-16 15:21:48 -070078 private boolean mLoadGroupMetaData;
Dave Santoro39156002011-07-19 01:18:14 -070079 private boolean mLoadStreamItems;
Makoto Onuki69b4a882011-07-22 10:05:10 -070080 private final boolean mLoadInvitableAccountTypes;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070081 private Result mContact;
82 private ForceLoadContentObserver mObserver;
83 private boolean mDestroyed;
Makoto Onukiaba2b832011-08-12 15:44:53 -070084 private final Set<Long> mNotifiedRawContactIds = Sets.newHashSet();
Dmitri Plotnikove843f912010-09-16 15:21:48 -070085
Daniel Lehmanncdef2b62010-06-06 18:25:49 -070086 public interface Listener {
Daniel Lehmann4cd94412010-04-08 16:44:36 -070087 public void onContactLoaded(Result contact);
88 }
89
90 /**
91 * The result of a load operation. Contains all data necessary to display the contact.
92 */
93 public static final class Result {
94 /**
95 * Singleton instance that represents "No Contact Found"
96 */
Makoto Onuki9e7b5da2011-08-22 15:51:28 -070097 public static final Result NOT_FOUND = new Result((Exception) null);
Daniel Lehmann18f104f2010-05-07 15:41:11 -070098
Daniel Lehmann685157e2011-08-29 21:07:01 -070099 private final Uri mRequestedUri;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700100 private final Uri mLookupUri;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700101 private final Uri mUri;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700102 private final long mDirectoryId;
103 private final String mLookupKey;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700104 private final long mId;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700105 private final long mNameRawContactId;
106 private final int mDisplayNameSource;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700107 private final long mPhotoId;
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700108 private final String mPhotoUri;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700109 private final String mDisplayName;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700110 private final String mAltDisplayName;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700111 private final String mPhoneticName;
112 private final boolean mStarred;
113 private final Integer mPresence;
114 private final ArrayList<Entity> mEntities;
Makoto Onuki870a87e2011-08-12 13:40:31 -0700115 private final ArrayList<StreamItemEntry> mStreamItems;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700116 private final HashMap<Long, DataStatus> mStatuses;
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700117 private final ArrayList<AccountType> mInvitableAccountTypes;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700118
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700119 private String mDirectoryDisplayName;
120 private String mDirectoryType;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700121 private String mDirectoryAccountType;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700122 private String mDirectoryAccountName;
123 private int mDirectoryExportSupport;
124
Dmitri Plotnikove843f912010-09-16 15:21:48 -0700125 private ArrayList<GroupMetaData> mGroups;
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700126
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800127 private boolean mLoadingPhoto;
128 private byte[] mPhotoBinaryData;
Makoto Onuki870a87e2011-08-12 13:40:31 -0700129 private final boolean mSendToVoicemail;
130 private final String mCustomRingtone;
131 private final boolean mIsUserProfile;
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800132
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700133 private final Exception mException;
134
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700135 /**
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700136 * Constructor for special results, namely "no contact found" and "error".
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700137 */
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700138 private Result(Exception exception) {
Daniel Lehmann685157e2011-08-29 21:07:01 -0700139 mRequestedUri = null;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700140 mLookupUri = null;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700141 mUri = null;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700142 mDirectoryId = -1;
143 mLookupKey = null;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700144 mId = -1;
145 mEntities = null;
Dave Santoro39156002011-07-19 01:18:14 -0700146 mStreamItems = new ArrayList<StreamItemEntry>();
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700147 mStatuses = null;
148 mNameRawContactId = -1;
149 mDisplayNameSource = DisplayNameSources.UNDEFINED;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700150 mPhotoId = -1;
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700151 mPhotoUri = null;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700152 mDisplayName = null;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700153 mAltDisplayName = null;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700154 mPhoneticName = null;
155 mStarred = false;
156 mPresence = null;
Makoto Onuki69b4a882011-07-22 10:05:10 -0700157 mInvitableAccountTypes = null;
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700158 mSendToVoicemail = false;
159 mCustomRingtone = null;
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700160 mIsUserProfile = false;
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700161 mException = exception;
162 }
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700163
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700164 private static Result forError(Exception exception) {
165 return new Result(exception);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700166 }
167
168 /**
169 * Constructor to call when contact was found
170 */
Daniel Lehmann685157e2011-08-29 21:07:01 -0700171 private Result(Uri requestedUri, Uri uri, Uri lookupUri, long directoryId, String lookupKey,
172 long id, long nameRawContactId, int displayNameSource, long photoId,
173 String photoUri, String displayName, String altDisplayName, String phoneticName,
174 boolean starred, Integer presence, boolean sendToVoicemail, String customRingtone,
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700175 boolean isUserProfile) {
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700176 mException = null;
Daniel Lehmann685157e2011-08-29 21:07:01 -0700177 mRequestedUri = requestedUri;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700178 mLookupUri = lookupUri;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700179 mUri = uri;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700180 mDirectoryId = directoryId;
181 mLookupKey = lookupKey;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700182 mId = id;
183 mEntities = new ArrayList<Entity>();
Dave Santoro39156002011-07-19 01:18:14 -0700184 mStreamItems = new ArrayList<StreamItemEntry>();
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700185 mStatuses = new HashMap<Long, DataStatus>();
186 mNameRawContactId = nameRawContactId;
187 mDisplayNameSource = displayNameSource;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700188 mPhotoId = photoId;
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700189 mPhotoUri = photoUri;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700190 mDisplayName = displayName;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700191 mAltDisplayName = altDisplayName;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700192 mPhoneticName = phoneticName;
193 mStarred = starred;
194 mPresence = presence;
Makoto Onuki69b4a882011-07-22 10:05:10 -0700195 mInvitableAccountTypes = Lists.newArrayList();
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700196 mSendToVoicemail = sendToVoicemail;
197 mCustomRingtone = customRingtone;
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700198 mIsUserProfile = isUserProfile;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700199 }
200
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800201 private Result(Result from) {
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700202 mException = from.mException;
Daniel Lehmann685157e2011-08-29 21:07:01 -0700203 mRequestedUri = from.mRequestedUri;
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800204 mLookupUri = from.mLookupUri;
205 mUri = from.mUri;
206 mDirectoryId = from.mDirectoryId;
207 mLookupKey = from.mLookupKey;
208 mId = from.mId;
209 mNameRawContactId = from.mNameRawContactId;
210 mDisplayNameSource = from.mDisplayNameSource;
211 mPhotoId = from.mPhotoId;
212 mPhotoUri = from.mPhotoUri;
213 mDisplayName = from.mDisplayName;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700214 mAltDisplayName = from.mAltDisplayName;
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800215 mPhoneticName = from.mPhoneticName;
216 mStarred = from.mStarred;
217 mPresence = from.mPresence;
218 mEntities = from.mEntities;
Dave Santoro39156002011-07-19 01:18:14 -0700219 mStreamItems = from.mStreamItems;
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800220 mStatuses = from.mStatuses;
Makoto Onuki69b4a882011-07-22 10:05:10 -0700221 mInvitableAccountTypes = from.mInvitableAccountTypes;
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800222
223 mDirectoryDisplayName = from.mDirectoryDisplayName;
224 mDirectoryType = from.mDirectoryType;
225 mDirectoryAccountType = from.mDirectoryAccountType;
226 mDirectoryAccountName = from.mDirectoryAccountName;
227 mDirectoryExportSupport = from.mDirectoryExportSupport;
228
229 mGroups = from.mGroups;
230
231 mLoadingPhoto = from.mLoadingPhoto;
232 mPhotoBinaryData = from.mPhotoBinaryData;
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700233 mSendToVoicemail = from.mSendToVoicemail;
234 mCustomRingtone = from.mCustomRingtone;
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700235 mIsUserProfile = from.mIsUserProfile;
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800236 }
237
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700238 /**
239 * @param exportSupport See {@link Directory#EXPORT_SUPPORT}.
240 */
Daniel Lehmann1ad4d1b2010-10-18 19:20:41 -0700241 private void setDirectoryMetaData(String displayName, String directoryType,
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700242 String accountType, String accountName, int exportSupport) {
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700243 mDirectoryDisplayName = displayName;
244 mDirectoryType = directoryType;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700245 mDirectoryAccountType = accountType;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700246 mDirectoryAccountName = accountName;
247 mDirectoryExportSupport = exportSupport;
248 }
249
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800250 private void setLoadingPhoto(boolean flag) {
251 mLoadingPhoto = flag;
252 }
253
254 private void setPhotoBinaryData(byte[] photoBinaryData) {
255 mPhotoBinaryData = photoBinaryData;
256 }
257
Daniel Lehmann685157e2011-08-29 21:07:01 -0700258 /**
259 * Returns the URI for the contact that contains both the lookup key and the ID. This is
260 * the best URI to reference a contact.
261 * For directory contacts, this is the same a the URI as returned by {@link #getUri()}
262 */
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700263 public Uri getLookupUri() {
264 return mLookupUri;
265 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800266
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700267 public String getLookupKey() {
268 return mLookupKey;
269 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800270
Daniel Lehmann685157e2011-08-29 21:07:01 -0700271 /**
272 * Returns the contact Uri that was passed to the provider to make the query. This is
273 * the same as the requested Uri, unless the requested Uri doesn't specify a Contact:
274 * If it either references a Raw-Contact or a Person (a pre-Eclair style Uri), this Uri will
275 * always reference the full aggregate contact.
276 */
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700277 public Uri getUri() {
278 return mUri;
279 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800280
Daniel Lehmann685157e2011-08-29 21:07:01 -0700281 /**
282 * Returns the URI for which this {@link ContactLoader) was initially requested.
283 */
284 public Uri getRequestedUri() {
285 return mRequestedUri;
286 }
287
Flavio Lerda37a26842011-06-27 11:36:52 +0100288 @VisibleForTesting
289 /*package*/ long getId() {
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700290 return mId;
291 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800292
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700293 /**
294 * @return true when an exception happened during loading, in which case
295 * {@link #getException} returns the actual exception object.
296 */
297 public boolean isError() {
298 return mException != null;
299 }
300
301 public Exception getException() {
302 return mException;
303 }
304
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700305 public long getNameRawContactId() {
306 return mNameRawContactId;
307 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800308
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700309 public int getDisplayNameSource() {
310 return mDisplayNameSource;
311 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800312
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700313 public long getPhotoId() {
314 return mPhotoId;
315 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800316
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700317 public String getPhotoUri() {
318 return mPhotoUri;
319 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800320
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700321 public String getDisplayName() {
322 return mDisplayName;
323 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800324
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700325 public String getAltDisplayName() {
326 return mAltDisplayName;
327 }
328
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700329 public String getPhoneticName() {
330 return mPhoneticName;
331 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800332
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700333 public boolean getStarred() {
334 return mStarred;
335 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800336
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700337 public Integer getPresence() {
338 return mPresence;
339 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800340
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700341 public ArrayList<AccountType> getInvitableAccountTypes() {
Makoto Onuki69b4a882011-07-22 10:05:10 -0700342 return mInvitableAccountTypes;
343 }
344
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700345 public ArrayList<Entity> getEntities() {
346 return mEntities;
347 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800348
Dave Santoro39156002011-07-19 01:18:14 -0700349 public ArrayList<StreamItemEntry> getStreamItems() {
350 return mStreamItems;
351 }
352
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700353 public HashMap<Long, DataStatus> getStatuses() {
354 return mStatuses;
355 }
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700356
357 public long getDirectoryId() {
358 return mDirectoryId;
359 }
360
361 public boolean isDirectoryEntry() {
Dmitri Plotnikov5f72c1f2010-09-01 21:21:04 -0700362 return mDirectoryId != -1 && mDirectoryId != Directory.DEFAULT
363 && mDirectoryId != Directory.LOCAL_INVISIBLE;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700364 }
365
366 public int getDirectoryExportSupport() {
367 return mDirectoryExportSupport;
368 }
369
370 public String getDirectoryDisplayName() {
371 return mDirectoryDisplayName;
372 }
373
374 public String getDirectoryType() {
375 return mDirectoryType;
376 }
377
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700378 public String getDirectoryAccountType() {
379 return mDirectoryAccountType;
380 }
381
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700382 public String getDirectoryAccountName() {
383 return mDirectoryAccountName;
384 }
385
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800386 public boolean isLoadingPhoto() {
387 return mLoadingPhoto;
388 }
389
390 public byte[] getPhotoBinaryData() {
391 return mPhotoBinaryData;
392 }
393
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700394 public ArrayList<ContentValues> getContentValues() {
395 if (mEntities.size() != 1) {
396 throw new IllegalStateException(
397 "Cannot extract content values from an aggregated contact");
398 }
399
400 Entity entity = mEntities.get(0);
401 ArrayList<ContentValues> result = new ArrayList<ContentValues>();
402 ArrayList<NamedContentValues> subValues = entity.getSubValues();
403 if (subValues != null) {
404 int size = subValues.size();
405 for (int i = 0; i < size; i++) {
406 NamedContentValues pair = subValues.get(i);
407 if (Data.CONTENT_URI.equals(pair.uri)) {
408 result.add(pair.values);
409 }
410 }
411 }
Dmitri Plotnikov40ec3a82010-11-10 11:25:33 -0800412
413 // If the photo was loaded using the URI, create an entry for the photo
414 // binary data.
415 if (mPhotoId == 0 && mPhotoBinaryData != null) {
416 ContentValues photo = new ContentValues();
417 photo.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
418 photo.put(Photo.PHOTO, mPhotoBinaryData);
419 result.add(photo);
420 }
421
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700422 return result;
423 }
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700424
Daniel Lehmann1ad4d1b2010-10-18 19:20:41 -0700425 private void addGroupMetaData(GroupMetaData group) {
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700426 if (mGroups == null) {
Dmitri Plotnikove843f912010-09-16 15:21:48 -0700427 mGroups = new ArrayList<GroupMetaData>();
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700428 }
429 mGroups.add(group);
430 }
431
Dmitri Plotnikove843f912010-09-16 15:21:48 -0700432 public List<GroupMetaData> getGroupMetaData() {
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700433 return mGroups;
434 }
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700435
436 public boolean isSendToVoicemail() {
437 return mSendToVoicemail;
438 }
439
440 public String getCustomRingtone() {
441 return mCustomRingtone;
442 }
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700443
444 public boolean isUserProfile() {
445 return mIsUserProfile;
446 }
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700447 }
448
Dave Santoro39156002011-07-19 01:18:14 -0700449 /**
450 * Projection used for the query that loads all data for the entire contact (except for
451 * social stream items).
452 */
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700453 private static class ContactQuery {
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700454 final static String[] COLUMNS = new String[] {
455 Contacts.NAME_RAW_CONTACT_ID,
456 Contacts.DISPLAY_NAME_SOURCE,
457 Contacts.LOOKUP_KEY,
458 Contacts.DISPLAY_NAME,
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700459 Contacts.DISPLAY_NAME_ALTERNATIVE,
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700460 Contacts.PHONETIC_NAME,
461 Contacts.PHOTO_ID,
462 Contacts.STARRED,
463 Contacts.CONTACT_PRESENCE,
464 Contacts.CONTACT_STATUS,
465 Contacts.CONTACT_STATUS_TIMESTAMP,
466 Contacts.CONTACT_STATUS_RES_PACKAGE,
467 Contacts.CONTACT_STATUS_LABEL,
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700468 Contacts.Entity.CONTACT_ID,
469 Contacts.Entity.RAW_CONTACT_ID,
470
471 RawContacts.ACCOUNT_NAME,
472 RawContacts.ACCOUNT_TYPE,
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700473 RawContacts.DATA_SET,
474 RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700475 RawContacts.DIRTY,
476 RawContacts.VERSION,
477 RawContacts.SOURCE_ID,
478 RawContacts.SYNC1,
479 RawContacts.SYNC2,
480 RawContacts.SYNC3,
481 RawContacts.SYNC4,
482 RawContacts.DELETED,
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700483 RawContacts.NAME_VERIFIED,
484
485 Contacts.Entity.DATA_ID,
486 Data.DATA1,
487 Data.DATA2,
488 Data.DATA3,
489 Data.DATA4,
490 Data.DATA5,
491 Data.DATA6,
492 Data.DATA7,
493 Data.DATA8,
494 Data.DATA9,
495 Data.DATA10,
496 Data.DATA11,
497 Data.DATA12,
498 Data.DATA13,
499 Data.DATA14,
500 Data.DATA15,
501 Data.SYNC1,
502 Data.SYNC2,
503 Data.SYNC3,
504 Data.SYNC4,
505 Data.DATA_VERSION,
506 Data.IS_PRIMARY,
507 Data.IS_SUPER_PRIMARY,
508 Data.MIMETYPE,
509 Data.RES_PACKAGE,
510
511 GroupMembership.GROUP_SOURCE_ID,
512
513 Data.PRESENCE,
Daniel Lehmann8fd7bb62010-08-13 20:50:31 -0700514 Data.CHAT_CAPABILITY,
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700515 Data.STATUS,
516 Data.STATUS_RES_PACKAGE,
517 Data.STATUS_ICON,
518 Data.STATUS_LABEL,
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700519 Data.STATUS_TIMESTAMP,
520
521 Contacts.PHOTO_URI,
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700522 Contacts.SEND_TO_VOICEMAIL,
523 Contacts.CUSTOM_RINGTONE,
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700524 Contacts.IS_USER_PROFILE,
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700525 };
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700526
527 public final static int NAME_RAW_CONTACT_ID = 0;
528 public final static int DISPLAY_NAME_SOURCE = 1;
529 public final static int LOOKUP_KEY = 2;
530 public final static int DISPLAY_NAME = 3;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700531 public final static int ALT_DISPLAY_NAME = 4;
532 public final static int PHONETIC_NAME = 5;
533 public final static int PHOTO_ID = 6;
534 public final static int STARRED = 7;
535 public final static int CONTACT_PRESENCE = 8;
536 public final static int CONTACT_STATUS = 9;
537 public final static int CONTACT_STATUS_TIMESTAMP = 10;
538 public final static int CONTACT_STATUS_RES_PACKAGE = 11;
539 public final static int CONTACT_STATUS_LABEL = 12;
540 public final static int CONTACT_ID = 13;
541 public final static int RAW_CONTACT_ID = 14;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700542
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700543 public final static int ACCOUNT_NAME = 15;
544 public final static int ACCOUNT_TYPE = 16;
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700545 public final static int DATA_SET = 17;
546 public final static int ACCOUNT_TYPE_AND_DATA_SET = 18;
547 public final static int DIRTY = 19;
548 public final static int VERSION = 20;
549 public final static int SOURCE_ID = 21;
550 public final static int SYNC1 = 22;
551 public final static int SYNC2 = 23;
552 public final static int SYNC3 = 24;
553 public final static int SYNC4 = 25;
554 public final static int DELETED = 26;
555 public final static int NAME_VERIFIED = 27;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700556
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700557 public final static int DATA_ID = 28;
558 public final static int DATA1 = 29;
559 public final static int DATA2 = 30;
560 public final static int DATA3 = 31;
561 public final static int DATA4 = 32;
562 public final static int DATA5 = 33;
563 public final static int DATA6 = 34;
564 public final static int DATA7 = 35;
565 public final static int DATA8 = 36;
566 public final static int DATA9 = 37;
567 public final static int DATA10 = 38;
568 public final static int DATA11 = 39;
569 public final static int DATA12 = 40;
570 public final static int DATA13 = 41;
571 public final static int DATA14 = 42;
572 public final static int DATA15 = 43;
573 public final static int DATA_SYNC1 = 44;
574 public final static int DATA_SYNC2 = 45;
575 public final static int DATA_SYNC3 = 46;
576 public final static int DATA_SYNC4 = 47;
577 public final static int DATA_VERSION = 48;
578 public final static int IS_PRIMARY = 49;
579 public final static int IS_SUPERPRIMARY = 50;
580 public final static int MIMETYPE = 51;
581 public final static int RES_PACKAGE = 52;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700582
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700583 public final static int GROUP_SOURCE_ID = 53;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700584
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700585 public final static int PRESENCE = 54;
586 public final static int CHAT_CAPABILITY = 55;
587 public final static int STATUS = 56;
588 public final static int STATUS_RES_PACKAGE = 57;
589 public final static int STATUS_ICON = 58;
590 public final static int STATUS_LABEL = 59;
591 public final static int STATUS_TIMESTAMP = 60;
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700592
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700593 public final static int PHOTO_URI = 61;
594 public final static int SEND_TO_VOICEMAIL = 62;
595 public final static int CUSTOM_RINGTONE = 63;
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700596 public final static int IS_USER_PROFILE = 64;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700597 }
Daniel Lehmann1316b132010-04-13 15:08:53 -0700598
Dave Santoro39156002011-07-19 01:18:14 -0700599 /**
600 * Projection used for the query that loads all data for the entire contact.
601 */
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700602 private static class DirectoryQuery {
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700603 final static String[] COLUMNS = new String[] {
604 Directory.DISPLAY_NAME,
605 Directory.PACKAGE_NAME,
606 Directory.TYPE_RESOURCE_ID,
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700607 Directory.ACCOUNT_TYPE,
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700608 Directory.ACCOUNT_NAME,
609 Directory.EXPORT_SUPPORT,
610 };
611
612 public final static int DISPLAY_NAME = 0;
613 public final static int PACKAGE_NAME = 1;
614 public final static int TYPE_RESOURCE_ID = 2;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700615 public final static int ACCOUNT_TYPE = 3;
616 public final static int ACCOUNT_NAME = 4;
617 public final static int EXPORT_SUPPORT = 5;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700618 }
619
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700620 private static class GroupQuery {
621 final static String[] COLUMNS = new String[] {
622 Groups.ACCOUNT_NAME,
623 Groups.ACCOUNT_TYPE,
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700624 Groups.DATA_SET,
625 Groups.ACCOUNT_TYPE_AND_DATA_SET,
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700626 Groups._ID,
627 Groups.TITLE,
628 Groups.AUTO_ADD,
629 Groups.FAVORITES,
630 };
631
632 public final static int ACCOUNT_NAME = 0;
633 public final static int ACCOUNT_TYPE = 1;
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700634 public final static int DATA_SET = 2;
635 public final static int ACCOUNT_TYPE_AND_DATA_SET = 3;
636 public final static int ID = 4;
637 public final static int TITLE = 5;
638 public final static int AUTO_ADD = 6;
639 public final static int FAVORITES = 7;
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700640 }
641
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700642 private final class LoadContactTask extends AsyncTask<Void, Void, Result> {
Daniel Lehmann1316b132010-04-13 15:08:53 -0700643
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700644 @Override
645 protected Result doInBackground(Void... args) {
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700646 try {
647 final ContentResolver resolver = getContext().getContentResolver();
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700648 final Uri uriCurrentFormat = ensureIsContactUri(resolver, mLookupUri);
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700649 Result result = loadContactEntity(resolver, uriCurrentFormat);
Dmitri Plotnikov217245c2010-09-18 13:04:50 -0700650 if (result != Result.NOT_FOUND) {
651 if (result.isDirectoryEntry()) {
652 loadDirectoryMetaData(result);
653 } else if (mLoadGroupMetaData) {
654 loadGroupMetaData(result);
655 }
Dave Santoro39156002011-07-19 01:18:14 -0700656 if (mLoadStreamItems) {
657 loadStreamItems(result);
658 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800659 loadPhotoBinaryData(result);
Makoto Onuki870a87e2011-08-12 13:40:31 -0700660
661 // Note ME profile should never have "Add connection"
662 if (mLoadInvitableAccountTypes && !result.isUserProfile()) {
Makoto Onuki69b4a882011-07-22 10:05:10 -0700663 loadInvitableAccountTypes(result);
664 }
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700665 }
666 return result;
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700667 } catch (Exception e) {
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700668 Log.e(TAG, "Error loading the contact: " + mLookupUri, e);
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700669 return Result.forError(e);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700670 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700671 }
672
673 /**
Daniel Lehmann1316b132010-04-13 15:08:53 -0700674 * Transforms the given Uri and returns a Lookup-Uri that represents the contact.
675 * For legacy contacts, a raw-contact lookup is performed.
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700676 * @param resolver
Daniel Lehmann1316b132010-04-13 15:08:53 -0700677 */
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700678 private Uri ensureIsContactUri(final ContentResolver resolver, final Uri uri) {
Daniel Lehmann1316b132010-04-13 15:08:53 -0700679 if (uri == null) throw new IllegalArgumentException("uri must not be null");
680
681 final String authority = uri.getAuthority();
682
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700683 // Current Style Uri?
Daniel Lehmann1316b132010-04-13 15:08:53 -0700684 if (ContactsContract.AUTHORITY.equals(authority)) {
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700685 final String type = resolver.getType(uri);
686 // Contact-Uri? Good, return it
687 if (Contacts.CONTENT_ITEM_TYPE.equals(type)) {
688 return uri;
689 }
690
691 // RawContact-Uri? Transform it to ContactUri
692 if (RawContacts.CONTENT_ITEM_TYPE.equals(type)) {
693 final long rawContactId = ContentUris.parseId(uri);
694 return RawContacts.getContactLookupUri(getContext().getContentResolver(),
695 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId));
696 }
697
698 // Anything else? We don't know what this is
699 throw new IllegalArgumentException("uri format is unknown");
Daniel Lehmann1316b132010-04-13 15:08:53 -0700700 }
701
702 // Legacy Style? Convert to RawContact
703 final String OBSOLETE_AUTHORITY = "contacts";
704 if (OBSOLETE_AUTHORITY.equals(authority)) {
705 // Legacy Format. Convert to RawContact-Uri and then lookup the contact
706 final long rawContactId = ContentUris.parseId(uri);
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700707 return RawContacts.getContactLookupUri(resolver,
Daniel Lehmann1316b132010-04-13 15:08:53 -0700708 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId));
709 }
710
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700711 throw new IllegalArgumentException("uri authority is unknown");
Daniel Lehmann1316b132010-04-13 15:08:53 -0700712 }
713
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700714 private Result loadContactEntity(ContentResolver resolver, Uri contactUri) {
715 Uri entityUri = Uri.withAppendedPath(contactUri, Contacts.Entity.CONTENT_DIRECTORY);
716 Cursor cursor = resolver.query(entityUri, ContactQuery.COLUMNS, null, null,
717 Contacts.Entity.RAW_CONTACT_ID);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700718 if (cursor == null) {
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700719 Log.e(TAG, "No cursor returned in loadContactEntity");
720 return Result.NOT_FOUND;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700721 }
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700722
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700723 try {
724 if (!cursor.moveToFirst()) {
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700725 cursor.close();
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700726 return Result.NOT_FOUND;
727 }
728
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700729 long currentRawContactId = -1;
730 Entity entity = null;
731 Result result = loadContactHeaderData(cursor, contactUri);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700732 ArrayList<Entity> entities = result.getEntities();
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700733 HashMap<Long, DataStatus> statuses = result.getStatuses();
734 for (; !cursor.isAfterLast(); cursor.moveToNext()) {
735 long rawContactId = cursor.getLong(ContactQuery.RAW_CONTACT_ID);
736 if (rawContactId != currentRawContactId) {
737 currentRawContactId = rawContactId;
738 entity = new android.content.Entity(loadRawContact(cursor));
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700739 entities.add(entity);
740 }
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700741 if (!cursor.isNull(ContactQuery.DATA_ID)) {
742 ContentValues data = loadData(cursor);
743 entity.addSubValue(ContactsContract.Data.CONTENT_URI, data);
744
745 if (!cursor.isNull(ContactQuery.PRESENCE)
746 || !cursor.isNull(ContactQuery.STATUS)) {
747 final DataStatus status = new DataStatus(cursor);
748 final long dataId = cursor.getLong(ContactQuery.DATA_ID);
749 statuses.put(dataId, status);
750 }
751 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700752 }
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700753
754 return result;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700755 } finally {
756 cursor.close();
757 }
758 }
759
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700760 /**
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800761 * Looks for the photo data item in entities. If found, creates a new Bitmap instance. If
762 * not found, returns null
763 */
764 private void loadPhotoBinaryData(Result contactData) {
Dave Santoro0a2a5db2011-06-29 00:37:06 -0700765
766 // If we have a photo URI, try loading that first.
767 String photoUri = contactData.getPhotoUri();
768 if (photoUri != null) {
769 try {
770 AssetFileDescriptor fd = getContext().getContentResolver()
771 .openAssetFileDescriptor(Uri.parse(photoUri), "r");
772 byte[] buffer = new byte[16 * 1024];
773 FileInputStream fis = fd.createInputStream();
774 ByteArrayOutputStream baos = new ByteArrayOutputStream();
775 try {
776 int size;
777 while ((size = fis.read(buffer)) != -1) {
778 baos.write(buffer, 0, size);
779 }
780 contactData.setPhotoBinaryData(baos.toByteArray());
781 } finally {
782 fis.close();
783 fd.close();
784 }
785 return;
786 } catch (IOException ioe) {
787 // Just fall back to the case below.
788 }
789 }
790
791 // If we couldn't load from a file, fall back to the data blob.
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800792 final long photoId = contactData.getPhotoId();
793 if (photoId <= 0) {
794 // No photo ID
795 return;
796 }
797
798 for (Entity entity : contactData.getEntities()) {
799 for (NamedContentValues subValue : entity.getSubValues()) {
800 final ContentValues entryValues = subValue.values;
801 final long dataId = entryValues.getAsLong(Data._ID);
802 if (dataId == photoId) {
803 final String mimeType = entryValues.getAsString(Data.MIMETYPE);
804 // Correct Data Id but incorrect MimeType? Don't load
805 if (!Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
806 return;
807 }
808 contactData.setPhotoBinaryData(entryValues.getAsByteArray(Photo.PHOTO));
809 break;
810 }
811 }
812 }
813 }
814
Makoto Onuki3e6991e2011-07-24 14:51:20 -0700815 /**
816 * Sets the "invitable" account types to {@link Result#mInvitableAccountTypes}.
817 *
818 * TODO Exclude the ones with no raw contacts in the database.
819 */
Makoto Onuki69b4a882011-07-22 10:05:10 -0700820 private void loadInvitableAccountTypes(Result contactData) {
Makoto Onuki6ad227f2011-08-15 13:46:59 -0700821 Map<AccountTypeWithDataSet, AccountType> allInvitables =
Makoto Onuki69b4a882011-07-22 10:05:10 -0700822 AccountTypeManager.getInstance(getContext()).getInvitableAccountTypes();
823 if (allInvitables.isEmpty()) {
824 return;
825 }
826
Makoto Onuki6ad227f2011-08-15 13:46:59 -0700827 HashMap<AccountTypeWithDataSet, AccountType> result = Maps.newHashMap(allInvitables);
Makoto Onuki69b4a882011-07-22 10:05:10 -0700828
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -0700829 // Remove the ones that already have a raw contact in the current contact
Makoto Onuki69b4a882011-07-22 10:05:10 -0700830 for (Entity entity : contactData.getEntities()) {
Makoto Onuki6ad227f2011-08-15 13:46:59 -0700831 final ContentValues values = entity.getEntityValues();
832 final AccountTypeWithDataSet type = AccountTypeWithDataSet.get(
833 values.getAsString(RawContacts.ACCOUNT_TYPE),
834 values.getAsString(RawContacts.DATA_SET));
835 result.remove(type);
Makoto Onuki69b4a882011-07-22 10:05:10 -0700836 }
837
838 // Set to mInvitableAccountTypes
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700839 contactData.mInvitableAccountTypes.addAll(result.values());
Makoto Onuki69b4a882011-07-22 10:05:10 -0700840 }
841
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800842 /**
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700843 * Extracts Contact level columns from the cursor.
844 */
845 private Result loadContactHeaderData(final Cursor cursor, Uri contactUri) {
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700846 final String directoryParameter =
847 contactUri.getQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY);
848 final long directoryId = directoryParameter == null
849 ? Directory.DEFAULT
850 : Long.parseLong(directoryParameter);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700851 final long contactId = cursor.getLong(ContactQuery.CONTACT_ID);
852 final String lookupKey = cursor.getString(ContactQuery.LOOKUP_KEY);
853 final long nameRawContactId = cursor.getLong(ContactQuery.NAME_RAW_CONTACT_ID);
854 final int displayNameSource = cursor.getInt(ContactQuery.DISPLAY_NAME_SOURCE);
855 final String displayName = cursor.getString(ContactQuery.DISPLAY_NAME);
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700856 final String altDisplayName = cursor.getString(ContactQuery.ALT_DISPLAY_NAME);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700857 final String phoneticName = cursor.getString(ContactQuery.PHONETIC_NAME);
858 final long photoId = cursor.getLong(ContactQuery.PHOTO_ID);
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700859 final String photoUri = cursor.getString(ContactQuery.PHOTO_URI);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700860 final boolean starred = cursor.getInt(ContactQuery.STARRED) != 0;
861 final Integer presence = cursor.isNull(ContactQuery.CONTACT_PRESENCE)
862 ? null
863 : cursor.getInt(ContactQuery.CONTACT_PRESENCE);
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700864 final boolean sendToVoicemail = cursor.getInt(ContactQuery.SEND_TO_VOICEMAIL) == 1;
865 final String customRingtone = cursor.getString(ContactQuery.CUSTOM_RINGTONE);
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700866 final boolean isUserProfile = cursor.getInt(ContactQuery.IS_USER_PROFILE) == 1;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700867
Dmitri Plotnikov1536ea12010-10-29 11:51:05 -0700868 Uri lookupUri;
869 if (directoryId == Directory.DEFAULT || directoryId == Directory.LOCAL_INVISIBLE) {
870 lookupUri = ContentUris.withAppendedId(
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700871 Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey), contactId);
Dmitri Plotnikov1536ea12010-10-29 11:51:05 -0700872 } else {
873 lookupUri = contactUri;
874 }
875
Daniel Lehmann685157e2011-08-29 21:07:01 -0700876 return new Result(mRequestedUri, contactUri, lookupUri, directoryId, lookupKey,
877 contactId, nameRawContactId, displayNameSource, photoId, photoUri, displayName,
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700878 altDisplayName, phoneticName, starred, presence, sendToVoicemail,
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700879 customRingtone, isUserProfile);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700880 }
881
882 /**
883 * Extracts RawContact level columns from the cursor.
884 */
885 private ContentValues loadRawContact(Cursor cursor) {
886 ContentValues cv = new ContentValues();
887
888 cv.put(RawContacts._ID, cursor.getLong(ContactQuery.RAW_CONTACT_ID));
889
890 cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_NAME);
891 cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_TYPE);
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700892 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SET);
893 cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_TYPE_AND_DATA_SET);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700894 cursorColumnToContentValues(cursor, cv, ContactQuery.DIRTY);
895 cursorColumnToContentValues(cursor, cv, ContactQuery.VERSION);
896 cursorColumnToContentValues(cursor, cv, ContactQuery.SOURCE_ID);
897 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC1);
898 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC2);
899 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC3);
900 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC4);
901 cursorColumnToContentValues(cursor, cv, ContactQuery.DELETED);
902 cursorColumnToContentValues(cursor, cv, ContactQuery.CONTACT_ID);
903 cursorColumnToContentValues(cursor, cv, ContactQuery.STARRED);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700904 cursorColumnToContentValues(cursor, cv, ContactQuery.NAME_VERIFIED);
905
906 return cv;
907 }
908
909 /**
910 * Extracts Data level columns from the cursor.
911 */
912 private ContentValues loadData(Cursor cursor) {
913 ContentValues cv = new ContentValues();
914
915 cv.put(Data._ID, cursor.getLong(ContactQuery.DATA_ID));
916
917 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA1);
918 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA2);
919 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA3);
920 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA4);
921 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA5);
922 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA6);
923 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA7);
924 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA8);
925 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA9);
926 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA10);
927 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA11);
928 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA12);
929 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA13);
930 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA14);
931 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA15);
932 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC1);
933 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC2);
934 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC3);
935 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC4);
936 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_VERSION);
937 cursorColumnToContentValues(cursor, cv, ContactQuery.IS_PRIMARY);
938 cursorColumnToContentValues(cursor, cv, ContactQuery.IS_SUPERPRIMARY);
939 cursorColumnToContentValues(cursor, cv, ContactQuery.MIMETYPE);
940 cursorColumnToContentValues(cursor, cv, ContactQuery.RES_PACKAGE);
941 cursorColumnToContentValues(cursor, cv, ContactQuery.GROUP_SOURCE_ID);
Daniel Lehmann8fd7bb62010-08-13 20:50:31 -0700942 cursorColumnToContentValues(cursor, cv, ContactQuery.CHAT_CAPABILITY);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700943
944 return cv;
945 }
946
947 private void cursorColumnToContentValues(
948 Cursor cursor, ContentValues values, int index) {
949 switch (cursor.getType(index)) {
950 case Cursor.FIELD_TYPE_NULL:
951 // don't put anything in the content values
952 break;
953 case Cursor.FIELD_TYPE_INTEGER:
954 values.put(ContactQuery.COLUMNS[index], cursor.getLong(index));
955 break;
956 case Cursor.FIELD_TYPE_STRING:
957 values.put(ContactQuery.COLUMNS[index], cursor.getString(index));
958 break;
959 case Cursor.FIELD_TYPE_BLOB:
960 values.put(ContactQuery.COLUMNS[index], cursor.getBlob(index));
961 break;
962 default:
963 throw new IllegalStateException("Invalid or unhandled data type");
964 }
965 }
966
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700967 private void loadDirectoryMetaData(Result result) {
968 long directoryId = result.getDirectoryId();
969
970 Cursor cursor = getContext().getContentResolver().query(
971 ContentUris.withAppendedId(Directory.CONTENT_URI, directoryId),
972 DirectoryQuery.COLUMNS, null, null, null);
973 if (cursor == null) {
974 return;
975 }
976 try {
977 if (cursor.moveToFirst()) {
978 final String displayName = cursor.getString(DirectoryQuery.DISPLAY_NAME);
979 final String packageName = cursor.getString(DirectoryQuery.PACKAGE_NAME);
980 final int typeResourceId = cursor.getInt(DirectoryQuery.TYPE_RESOURCE_ID);
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700981 final String accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE);
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700982 final String accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME);
983 final int exportSupport = cursor.getInt(DirectoryQuery.EXPORT_SUPPORT);
984 String directoryType = null;
985 if (!TextUtils.isEmpty(packageName)) {
986 PackageManager pm = getContext().getPackageManager();
987 try {
988 Resources resources = pm.getResourcesForApplication(packageName);
989 directoryType = resources.getString(typeResourceId);
990 } catch (NameNotFoundException e) {
991 Log.w(TAG, "Contact directory resource not found: "
992 + packageName + "." + typeResourceId);
993 }
994 }
995
996 result.setDirectoryMetaData(
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700997 displayName, directoryType, accountType, accountName, exportSupport);
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700998 }
999 } finally {
1000 cursor.close();
1001 }
1002 }
1003
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001004 /**
1005 * Loads groups meta-data for all groups associated with all constituent raw contacts'
1006 * accounts.
1007 */
1008 private void loadGroupMetaData(Result result) {
1009 StringBuilder selection = new StringBuilder();
1010 ArrayList<String> selectionArgs = new ArrayList<String>();
1011 for (Entity entity : result.mEntities) {
1012 ContentValues values = entity.getEntityValues();
1013 String accountName = values.getAsString(RawContacts.ACCOUNT_NAME);
1014 String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
Dave Santoro2b3f3c52011-07-26 17:35:42 -07001015 String dataSet = values.getAsString(RawContacts.DATA_SET);
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001016 if (accountName != null && accountType != null) {
1017 if (selection.length() != 0) {
1018 selection.append(" OR ");
1019 }
1020 selection.append(
Dave Santoro2b3f3c52011-07-26 17:35:42 -07001021 "(" + Groups.ACCOUNT_NAME + "=? AND " + Groups.ACCOUNT_TYPE + "=?");
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001022 selectionArgs.add(accountName);
1023 selectionArgs.add(accountType);
Dave Santoro2b3f3c52011-07-26 17:35:42 -07001024
1025 if (dataSet != null) {
1026 selection.append(" AND " + Groups.DATA_SET + "=?");
1027 selectionArgs.add(dataSet);
1028 } else {
1029 selection.append(" AND " + Groups.DATA_SET + " IS NULL");
1030 }
1031 selection.append(")");
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001032 }
1033 }
1034 Cursor cursor = getContext().getContentResolver().query(Groups.CONTENT_URI,
1035 GroupQuery.COLUMNS, selection.toString(), selectionArgs.toArray(new String[0]),
1036 null);
1037 try {
1038 while (cursor.moveToNext()) {
1039 final String accountName = cursor.getString(GroupQuery.ACCOUNT_NAME);
1040 final String accountType = cursor.getString(GroupQuery.ACCOUNT_TYPE);
Dave Santoro2b3f3c52011-07-26 17:35:42 -07001041 final String dataSet = cursor.getString(GroupQuery.DATA_SET);
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001042 final long groupId = cursor.getLong(GroupQuery.ID);
1043 final String title = cursor.getString(GroupQuery.TITLE);
1044 final boolean defaultGroup = cursor.isNull(GroupQuery.AUTO_ADD)
1045 ? false
1046 : cursor.getInt(GroupQuery.AUTO_ADD) != 0;
1047 final boolean favorites = cursor.isNull(GroupQuery.FAVORITES)
1048 ? false
1049 : cursor.getInt(GroupQuery.FAVORITES) != 0;
1050
Dmitri Plotnikove843f912010-09-16 15:21:48 -07001051 result.addGroupMetaData(new GroupMetaData(
Dave Santoro2b3f3c52011-07-26 17:35:42 -07001052 accountName, accountType, dataSet, groupId, title, defaultGroup,
1053 favorites));
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001054 }
1055 } finally {
1056 cursor.close();
1057 }
1058 }
1059
Dave Santoro39156002011-07-19 01:18:14 -07001060 /**
1061 * Loads all stream items and stream item photos belonging to this contact.
1062 */
1063 private void loadStreamItems(Result result) {
1064 Cursor cursor = getContext().getContentResolver().query(
1065 Contacts.CONTENT_LOOKUP_URI.buildUpon()
1066 .appendPath(result.getLookupKey())
1067 .appendPath(Contacts.StreamItems.CONTENT_DIRECTORY).build(),
1068 null, null, null, null);
1069 Map<Long, StreamItemEntry> streamItemsById = new HashMap<Long, StreamItemEntry>();
1070 ArrayList<StreamItemEntry> streamItems = new ArrayList<StreamItemEntry>();
1071 try {
1072 while (cursor.moveToNext()) {
1073 StreamItemEntry streamItem = new StreamItemEntry(cursor);
1074 streamItemsById.put(streamItem.getId(), streamItem);
1075 streamItems.add(streamItem);
1076 }
1077 } finally {
1078 cursor.close();
1079 }
1080
1081 // Now retrieve any photo records associated with the stream items.
Dave Santoro39156002011-07-19 01:18:14 -07001082 if (!streamItems.isEmpty()) {
Dave Santoroa4400d52011-09-02 16:14:53 -07001083 if (result.isUserProfile()) {
1084 // If the stream items we're loading are for the profile, we can't bulk-load the
1085 // stream items with a custom selection.
1086 for (StreamItemEntry entry : streamItems) {
1087 Cursor siCursor = getContext().getContentResolver().query(
1088 Uri.withAppendedPath(
1089 ContentUris.withAppendedId(
1090 StreamItems.CONTENT_URI, entry.getId()),
1091 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY),
1092 null, null, null, null);
1093 try {
1094 while (siCursor.moveToNext()) {
1095 entry.addPhoto(new StreamItemPhotoEntry(siCursor));
1096 }
1097 } finally {
1098 siCursor.close();
1099 }
Dave Santoro39156002011-07-19 01:18:14 -07001100 }
Dave Santoroa4400d52011-09-02 16:14:53 -07001101 } else {
1102 String[] streamItemIdArr = new String[streamItems.size()];
1103 StringBuilder streamItemPhotoSelection = new StringBuilder();
1104 streamItemPhotoSelection.append(StreamItemPhotos.STREAM_ITEM_ID + " IN (");
1105 for (int i = 0; i < streamItems.size(); i++) {
1106 if (i > 0) {
1107 streamItemPhotoSelection.append(",");
1108 }
1109 streamItemPhotoSelection.append("?");
1110 streamItemIdArr[i] = String.valueOf(streamItems.get(i).getId());
Dave Santoro39156002011-07-19 01:18:14 -07001111 }
Dave Santoroa4400d52011-09-02 16:14:53 -07001112 streamItemPhotoSelection.append(")");
1113 Cursor sipCursor = getContext().getContentResolver().query(
1114 StreamItems.CONTENT_PHOTO_URI,
1115 null, streamItemPhotoSelection.toString(), streamItemIdArr,
1116 StreamItemPhotos.STREAM_ITEM_ID);
1117 try {
1118 while (sipCursor.moveToNext()) {
1119 long streamItemId = sipCursor.getLong(
1120 sipCursor.getColumnIndex(StreamItemPhotos.STREAM_ITEM_ID));
1121 StreamItemEntry streamItem = streamItemsById.get(streamItemId);
1122 streamItem.addPhoto(new StreamItemPhotoEntry(sipCursor));
1123 }
1124 } finally {
1125 sipCursor.close();
1126 }
Dave Santoro39156002011-07-19 01:18:14 -07001127 }
1128 }
1129
1130 // Set the sorted stream items on the result.
1131 Collections.sort(streamItems);
1132 result.mStreamItems.addAll(streamItems);
1133 }
1134
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001135 @Override
1136 protected void onPostExecute(Result result) {
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001137 unregisterObserver();
1138
Daniel Lehmann1316b132010-04-13 15:08:53 -07001139 // The creator isn't interested in any further updates
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001140 if (mDestroyed || result == null) {
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001141 return;
1142 }
1143
1144 mContact = result;
Daniel Lehmann18f104f2010-05-07 15:41:11 -07001145
Makoto Onuki9e7b5da2011-08-22 15:51:28 -07001146 if (!result.isError() && result != Result.NOT_FOUND) {
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001147 mLookupUri = result.getLookupUri();
1148
1149 if (!result.isDirectoryEntry()) {
1150 Log.i(TAG, "Registering content observer for " + mLookupUri);
1151 if (mObserver == null) {
1152 mObserver = new ForceLoadContentObserver();
1153 }
1154 getContext().getContentResolver().registerContentObserver(
1155 mLookupUri, true, mObserver);
Daniel Lehmann18f104f2010-05-07 15:41:11 -07001156 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -08001157
1158 if (mContact.getPhotoBinaryData() == null && mContact.getPhotoUri() != null) {
1159 mContact.setLoadingPhoto(true);
1160 new AsyncPhotoLoader().execute(mContact.getPhotoUri());
1161 }
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001162
1163 // inform the source of the data that this contact is being looked at
1164 postViewNotificationToSyncAdapter();
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001165 }
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001166
1167 deliverResult(mContact);
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001168 }
1169 }
1170
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001171 /**
1172 * Posts a message to the contributing sync adapters that have opted-in, notifying them
1173 * that the contact has just been loaded
1174 */
1175 private void postViewNotificationToSyncAdapter() {
1176 Context context = getContext();
1177 for (Entity entity : mContact.getEntities()) {
1178 final ContentValues entityValues = entity.getEntityValues();
Makoto Onukiaba2b832011-08-12 15:44:53 -07001179 final long rawContactId = entityValues.getAsLong(RawContacts.Entity._ID);
1180 if (mNotifiedRawContactIds.contains(rawContactId)) {
1181 continue; // Already notified for this raw contact.
1182 }
1183 mNotifiedRawContactIds.add(rawContactId);
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001184 final String type = entityValues.getAsString(RawContacts.ACCOUNT_TYPE);
1185 final String dataSet = entityValues.getAsString(RawContacts.DATA_SET);
Flavio Lerda59a887e2011-08-14 18:13:17 +01001186 final AccountType accountType = AccountTypeManager.getInstance(context).getAccountType(
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001187 type, dataSet);
1188 final String serviceName = accountType.getViewContactNotifyServiceClassName();
1189 final String resPackageName = accountType.resPackageName;
1190 if (!TextUtils.isEmpty(serviceName) && !TextUtils.isEmpty(resPackageName)) {
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001191 final Uri uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
1192 final Intent intent = new Intent();
1193 intent.setClassName(resPackageName, serviceName);
1194 intent.setAction(Intent.ACTION_VIEW);
1195 intent.setDataAndType(uri, RawContacts.CONTENT_ITEM_TYPE);
1196 try {
1197 context.startService(intent);
1198 } catch (Exception e) {
1199 Log.e(TAG, "Error sending message to source-app", e);
1200 }
1201 }
1202 }
1203 }
1204
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -08001205 private class AsyncPhotoLoader extends AsyncTask<String, Void, byte[]> {
1206
1207 private static final int BUFFER_SIZE = 1024*16;
1208
1209 @Override
1210 protected byte[] doInBackground(String... params) {
1211 Uri uri = Uri.parse(params[0]);
1212 byte[] data = null;
1213 try {
1214 InputStream is = getContext().getContentResolver().openInputStream(uri);
1215 if (is != null) {
1216 ByteArrayOutputStream baos = new ByteArrayOutputStream();
1217 try {
1218 byte[] mBuffer = new byte[BUFFER_SIZE];
1219
1220 int size;
1221 while ((size = is.read(mBuffer)) != -1) {
1222 baos.write(mBuffer, 0, size);
1223 }
1224 data = baos.toByteArray();
1225 } finally {
1226 is.close();
1227 }
1228 } else {
1229 Log.v(TAG, "Cannot load photo " + uri);
1230 }
1231 } catch (IOException e) {
1232 Log.e(TAG, "Cannot load photo " + uri, e);
1233 }
1234
1235 return data;
1236 }
1237
1238 @Override
1239 protected void onPostExecute(byte[] data) {
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001240 if (mContact != null) {
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -08001241 mContact = new Result(mContact);
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001242 mContact.setPhotoBinaryData(data);
1243 mContact.setLoadingPhoto(false);
1244 deliverResult(mContact);
1245 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -08001246 }
1247 }
1248
Daniel Lehmann3a120772010-06-21 16:21:35 -07001249 private void unregisterObserver() {
1250 if (mObserver != null) {
1251 getContext().getContentResolver().unregisterContentObserver(mObserver);
1252 mObserver = null;
1253 }
1254 }
1255
Daniel Lehmanncdef2b62010-06-06 18:25:49 -07001256 public ContactLoader(Context context, Uri lookupUri) {
Makoto Onuki69b4a882011-07-22 10:05:10 -07001257 this(context, lookupUri, false, false, false);
Dmitri Plotnikove843f912010-09-16 15:21:48 -07001258 }
1259
Dave Santoro39156002011-07-19 01:18:14 -07001260 public ContactLoader(Context context, Uri lookupUri, boolean loadGroupMetaData,
Makoto Onuki69b4a882011-07-22 10:05:10 -07001261 boolean loadStreamItems, boolean loadInvitableAccountTypes) {
Daniel Lehmann74a2dc52010-04-15 16:52:33 -07001262 super(context);
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001263 mLookupUri = lookupUri;
Daniel Lehmann685157e2011-08-29 21:07:01 -07001264 mRequestedUri = lookupUri;
Dmitri Plotnikove843f912010-09-16 15:21:48 -07001265 mLoadGroupMetaData = loadGroupMetaData;
Dave Santoro39156002011-07-19 01:18:14 -07001266 mLoadStreamItems = loadStreamItems;
Makoto Onuki69b4a882011-07-22 10:05:10 -07001267 mLoadInvitableAccountTypes = loadInvitableAccountTypes;
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001268 }
1269
Dmitri Plotnikov5a30d9a2010-11-23 14:59:50 -08001270 public Uri getLookupUri() {
1271 return mLookupUri;
1272 }
1273
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001274 @Override
Dianne Hackbornc04fc272010-12-20 23:13:10 -08001275 protected void onStartLoading() {
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001276 if (mContact != null) {
Daniel Lehmanncbcc4492010-04-12 18:03:54 -07001277 deliverResult(mContact);
Dmitri Plotnikov97e90c62011-01-03 11:58:13 -08001278 }
1279
1280 if (takeContentChanged() || mContact == null) {
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001281 forceLoad();
1282 }
1283 }
1284
1285 @Override
Dianne Hackbornc04fc272010-12-20 23:13:10 -08001286 protected void onForceLoad() {
Daniel Lehmann18f104f2010-05-07 15:41:11 -07001287 final LoadContactTask task = new LoadContactTask();
Makoto Onuki73c341f2011-08-29 09:31:44 -07001288 task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[])null);
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001289 }
1290
1291 @Override
Dianne Hackbornc04fc272010-12-20 23:13:10 -08001292 protected void onReset() {
Dianne Hackborn4ef95cc2010-12-16 00:44:33 -08001293 unregisterObserver();
1294 mContact = null;
1295 mDestroyed = true;
1296 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001297}