blob: b7cc87d4044f919418796f3eb9cd0296e6d8f2bb [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;
Katherine Kuan6cd5b0a2011-09-16 11:46:01 -070022import com.android.contacts.util.ContactLoaderUtils;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070023import com.android.contacts.util.DataStatus;
Dave Santoro39156002011-07-19 01:18:14 -070024import com.android.contacts.util.StreamItemEntry;
25import com.android.contacts.util.StreamItemPhotoEntry;
Makoto Onuki69b4a882011-07-22 10:05:10 -070026import com.google.android.collect.Lists;
Flavio Lerda37a26842011-06-27 11:36:52 +010027import com.google.common.annotations.VisibleForTesting;
Makoto Onuki6ad227f2011-08-15 13:46:59 -070028import com.google.common.collect.Maps;
Makoto Onukiaba2b832011-08-12 15:44:53 -070029import com.google.common.collect.Sets;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070030
31import android.content.ContentResolver;
32import android.content.ContentUris;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -070033import android.content.ContentValues;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070034import android.content.Context;
35import android.content.Entity;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -070036import android.content.Entity.NamedContentValues;
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -070037import android.content.Intent;
Jeff Hamilton3c462912010-05-15 02:20:01 -050038import android.content.Loader;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -070039import android.content.pm.PackageManager;
40import android.content.pm.PackageManager.NameNotFoundException;
Dave Santoro0a2a5db2011-06-29 00:37:06 -070041import android.content.res.AssetFileDescriptor;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -070042import android.content.res.Resources;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070043import android.database.Cursor;
44import android.net.Uri;
45import android.os.AsyncTask;
Daniel Lehmann1316b132010-04-13 15:08:53 -070046import android.provider.ContactsContract;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -070047import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -080048import android.provider.ContactsContract.CommonDataKinds.Photo;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070049import android.provider.ContactsContract.Contacts;
50import android.provider.ContactsContract.Data;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -070051import android.provider.ContactsContract.Directory;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070052import android.provider.ContactsContract.DisplayNameSources;
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -070053import android.provider.ContactsContract.Groups;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070054import android.provider.ContactsContract.RawContacts;
Dave Santoro39156002011-07-19 01:18:14 -070055import android.provider.ContactsContract.StreamItemPhotos;
Makoto Onuki69b4a882011-07-22 10:05:10 -070056import android.provider.ContactsContract.StreamItems;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -070057import android.text.TextUtils;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070058import android.util.Log;
59
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -080060import java.io.ByteArrayOutputStream;
Dave Santoro0a2a5db2011-06-29 00:37:06 -070061import java.io.FileInputStream;
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -080062import java.io.IOException;
63import java.io.InputStream;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070064import java.util.ArrayList;
Dave Santoro39156002011-07-19 01:18:14 -070065import java.util.Collections;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070066import java.util.HashMap;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -070067import java.util.List;
Dave Santoro39156002011-07-19 01:18:14 -070068import java.util.Map;
Makoto Onukiaba2b832011-08-12 15:44:53 -070069import java.util.Set;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070070
71/**
72 * Loads a single Contact and all it constituent RawContacts.
73 */
Daniel Lehmanncdef2b62010-06-06 18:25:49 -070074public class ContactLoader extends Loader<ContactLoader.Result> {
Daniel Lehmann18f104f2010-05-07 15:41:11 -070075 private static final String TAG = "ContactLoader";
76
Daniel Lehmann685157e2011-08-29 21:07:01 -070077 private final Uri mRequestedUri;
Makoto Onuki2621c5b2011-10-03 12:56:16 -070078 private Uri mLookupUri;
Dmitri Plotnikove843f912010-09-16 15:21:48 -070079 private boolean mLoadGroupMetaData;
Dave Santoro39156002011-07-19 01:18:14 -070080 private boolean mLoadStreamItems;
Makoto Onuki69b4a882011-07-22 10:05:10 -070081 private final boolean mLoadInvitableAccountTypes;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070082 private Result mContact;
83 private ForceLoadContentObserver mObserver;
84 private boolean mDestroyed;
Makoto Onukiaba2b832011-08-12 15:44:53 -070085 private final Set<Long> mNotifiedRawContactIds = Sets.newHashSet();
Dmitri Plotnikove843f912010-09-16 15:21:48 -070086
Daniel Lehmanncdef2b62010-06-06 18:25:49 -070087 public interface Listener {
Daniel Lehmann4cd94412010-04-08 16:44:36 -070088 public void onContactLoaded(Result contact);
89 }
90
91 /**
92 * The result of a load operation. Contains all data necessary to display the contact.
93 */
94 public static final class Result {
Makoto Onuki2621c5b2011-10-03 12:56:16 -070095 private enum Status {
96 /** Contact is successfully loaded */
97 LOADED,
98 /** There was an error loading the contact */
99 ERROR,
100 /** Contact is not found */
101 NOT_FOUND,
102 }
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700103
Daniel Lehmann685157e2011-08-29 21:07:01 -0700104 private final Uri mRequestedUri;
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
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700138 private final Status mStatus;
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700139 private final Exception mException;
140
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700141 /**
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700142 * Constructor for special results, namely "no contact found" and "error".
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700143 */
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700144 private Result(Uri requestedUri, Status status, Exception exception) {
145 if (status == Status.ERROR && exception == null) {
146 throw new IllegalArgumentException("ERROR result must have exception");
147 }
148 mStatus = status;
149 mException = exception;
150 mRequestedUri = requestedUri;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700151 mLookupUri = null;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700152 mUri = null;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700153 mDirectoryId = -1;
154 mLookupKey = null;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700155 mId = -1;
156 mEntities = null;
Dave Santoro39156002011-07-19 01:18:14 -0700157 mStreamItems = new ArrayList<StreamItemEntry>();
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700158 mStatuses = null;
159 mNameRawContactId = -1;
160 mDisplayNameSource = DisplayNameSources.UNDEFINED;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700161 mPhotoId = -1;
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700162 mPhotoUri = null;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700163 mDisplayName = null;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700164 mAltDisplayName = null;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700165 mPhoneticName = null;
166 mStarred = false;
167 mPresence = null;
Makoto Onuki69b4a882011-07-22 10:05:10 -0700168 mInvitableAccountTypes = null;
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700169 mSendToVoicemail = false;
170 mCustomRingtone = null;
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700171 mIsUserProfile = false;
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700172 }
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700173
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700174 private static Result forError(Uri requestedUri, Exception exception) {
175 return new Result(requestedUri, Status.ERROR, exception);
176 }
177
178 private static Result forNotFound(Uri requestedUri) {
179 return new Result(requestedUri, Status.NOT_FOUND, null);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700180 }
181
182 /**
183 * Constructor to call when contact was found
184 */
Daniel Lehmann685157e2011-08-29 21:07:01 -0700185 private Result(Uri requestedUri, Uri uri, Uri lookupUri, long directoryId, String lookupKey,
186 long id, long nameRawContactId, int displayNameSource, long photoId,
187 String photoUri, String displayName, String altDisplayName, String phoneticName,
188 boolean starred, Integer presence, boolean sendToVoicemail, String customRingtone,
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700189 boolean isUserProfile) {
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700190 mStatus = Status.LOADED;
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700191 mException = null;
Daniel Lehmann685157e2011-08-29 21:07:01 -0700192 mRequestedUri = requestedUri;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700193 mLookupUri = lookupUri;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700194 mUri = uri;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700195 mDirectoryId = directoryId;
196 mLookupKey = lookupKey;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700197 mId = id;
198 mEntities = new ArrayList<Entity>();
Dave Santoro39156002011-07-19 01:18:14 -0700199 mStreamItems = new ArrayList<StreamItemEntry>();
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700200 mStatuses = new HashMap<Long, DataStatus>();
201 mNameRawContactId = nameRawContactId;
202 mDisplayNameSource = displayNameSource;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700203 mPhotoId = photoId;
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700204 mPhotoUri = photoUri;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700205 mDisplayName = displayName;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700206 mAltDisplayName = altDisplayName;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700207 mPhoneticName = phoneticName;
208 mStarred = starred;
209 mPresence = presence;
Makoto Onuki69b4a882011-07-22 10:05:10 -0700210 mInvitableAccountTypes = Lists.newArrayList();
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700211 mSendToVoicemail = sendToVoicemail;
212 mCustomRingtone = customRingtone;
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700213 mIsUserProfile = isUserProfile;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700214 }
215
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800216 private Result(Result from) {
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700217 mStatus = from.mStatus;
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700218 mException = from.mException;
Daniel Lehmann685157e2011-08-29 21:07:01 -0700219 mRequestedUri = from.mRequestedUri;
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800220 mLookupUri = from.mLookupUri;
221 mUri = from.mUri;
222 mDirectoryId = from.mDirectoryId;
223 mLookupKey = from.mLookupKey;
224 mId = from.mId;
225 mNameRawContactId = from.mNameRawContactId;
226 mDisplayNameSource = from.mDisplayNameSource;
227 mPhotoId = from.mPhotoId;
228 mPhotoUri = from.mPhotoUri;
229 mDisplayName = from.mDisplayName;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700230 mAltDisplayName = from.mAltDisplayName;
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800231 mPhoneticName = from.mPhoneticName;
232 mStarred = from.mStarred;
233 mPresence = from.mPresence;
234 mEntities = from.mEntities;
Dave Santoro39156002011-07-19 01:18:14 -0700235 mStreamItems = from.mStreamItems;
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800236 mStatuses = from.mStatuses;
Makoto Onuki69b4a882011-07-22 10:05:10 -0700237 mInvitableAccountTypes = from.mInvitableAccountTypes;
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800238
239 mDirectoryDisplayName = from.mDirectoryDisplayName;
240 mDirectoryType = from.mDirectoryType;
241 mDirectoryAccountType = from.mDirectoryAccountType;
242 mDirectoryAccountName = from.mDirectoryAccountName;
243 mDirectoryExportSupport = from.mDirectoryExportSupport;
244
245 mGroups = from.mGroups;
246
247 mLoadingPhoto = from.mLoadingPhoto;
248 mPhotoBinaryData = from.mPhotoBinaryData;
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700249 mSendToVoicemail = from.mSendToVoicemail;
250 mCustomRingtone = from.mCustomRingtone;
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700251 mIsUserProfile = from.mIsUserProfile;
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800252 }
253
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700254 /**
255 * @param exportSupport See {@link Directory#EXPORT_SUPPORT}.
256 */
Daniel Lehmann1ad4d1b2010-10-18 19:20:41 -0700257 private void setDirectoryMetaData(String displayName, String directoryType,
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700258 String accountType, String accountName, int exportSupport) {
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700259 mDirectoryDisplayName = displayName;
260 mDirectoryType = directoryType;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700261 mDirectoryAccountType = accountType;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700262 mDirectoryAccountName = accountName;
263 mDirectoryExportSupport = exportSupport;
264 }
265
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800266 private void setLoadingPhoto(boolean flag) {
267 mLoadingPhoto = flag;
268 }
269
270 private void setPhotoBinaryData(byte[] photoBinaryData) {
271 mPhotoBinaryData = photoBinaryData;
272 }
273
Daniel Lehmann685157e2011-08-29 21:07:01 -0700274 /**
275 * Returns the URI for the contact that contains both the lookup key and the ID. This is
276 * the best URI to reference a contact.
277 * For directory contacts, this is the same a the URI as returned by {@link #getUri()}
278 */
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700279 public Uri getLookupUri() {
280 return mLookupUri;
281 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800282
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700283 public String getLookupKey() {
284 return mLookupKey;
285 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800286
Daniel Lehmann685157e2011-08-29 21:07:01 -0700287 /**
288 * Returns the contact Uri that was passed to the provider to make the query. This is
289 * the same as the requested Uri, unless the requested Uri doesn't specify a Contact:
290 * If it either references a Raw-Contact or a Person (a pre-Eclair style Uri), this Uri will
291 * always reference the full aggregate contact.
292 */
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700293 public Uri getUri() {
294 return mUri;
295 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800296
Daniel Lehmann685157e2011-08-29 21:07:01 -0700297 /**
298 * Returns the URI for which this {@link ContactLoader) was initially requested.
299 */
300 public Uri getRequestedUri() {
301 return mRequestedUri;
302 }
303
Dave Santoro6fa73842011-09-28 14:37:06 -0700304 /**
305 * Returns the contact ID.
306 */
Makoto Onuki98306102011-11-28 15:16:58 -0800307 @VisibleForTesting
308 /* package */ long getId() {
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700309 return mId;
310 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800311
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700312 /**
313 * @return true when an exception happened during loading, in which case
314 * {@link #getException} returns the actual exception object.
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700315 * Note {@link #isNotFound()} and {@link #isError()} are mutually exclusive; If
316 * {@link #isError()} is {@code true}, {@link #isNotFound()} is always {@code false},
317 * and vice versa.
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700318 */
319 public boolean isError() {
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700320 return mStatus == Status.ERROR;
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700321 }
322
323 public Exception getException() {
324 return mException;
325 }
326
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700327 /**
328 * @return true when the specified contact is not found.
329 * Note {@link #isNotFound()} and {@link #isError()} are mutually exclusive; If
330 * {@link #isError()} is {@code true}, {@link #isNotFound()} is always {@code false},
331 * and vice versa.
332 */
333 public boolean isNotFound() {
334 return mStatus == Status.NOT_FOUND;
335 }
336
337 /**
338 * @return true if the specified contact is successfully loaded.
339 * i.e. neither {@link #isError()} nor {@link #isNotFound()}.
340 */
341 public boolean isLoaded() {
342 return mStatus == Status.LOADED;
343 }
344
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700345 public long getNameRawContactId() {
346 return mNameRawContactId;
347 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800348
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700349 public int getDisplayNameSource() {
350 return mDisplayNameSource;
351 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800352
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700353 public long getPhotoId() {
354 return mPhotoId;
355 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800356
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700357 public String getPhotoUri() {
358 return mPhotoUri;
359 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800360
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700361 public String getDisplayName() {
362 return mDisplayName;
363 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800364
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700365 public String getAltDisplayName() {
366 return mAltDisplayName;
367 }
368
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700369 public String getPhoneticName() {
370 return mPhoneticName;
371 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800372
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700373 public boolean getStarred() {
374 return mStarred;
375 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800376
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700377 public Integer getPresence() {
378 return mPresence;
379 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800380
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700381 public ArrayList<AccountType> getInvitableAccountTypes() {
Makoto Onuki69b4a882011-07-22 10:05:10 -0700382 return mInvitableAccountTypes;
383 }
384
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700385 public ArrayList<Entity> getEntities() {
386 return mEntities;
387 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800388
Dave Santoro39156002011-07-19 01:18:14 -0700389 public ArrayList<StreamItemEntry> getStreamItems() {
390 return mStreamItems;
391 }
392
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700393 public HashMap<Long, DataStatus> getStatuses() {
394 return mStatuses;
395 }
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700396
397 public long getDirectoryId() {
398 return mDirectoryId;
399 }
400
401 public boolean isDirectoryEntry() {
Dmitri Plotnikov5f72c1f2010-09-01 21:21:04 -0700402 return mDirectoryId != -1 && mDirectoryId != Directory.DEFAULT
403 && mDirectoryId != Directory.LOCAL_INVISIBLE;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700404 }
405
406 public int getDirectoryExportSupport() {
407 return mDirectoryExportSupport;
408 }
409
410 public String getDirectoryDisplayName() {
411 return mDirectoryDisplayName;
412 }
413
414 public String getDirectoryType() {
415 return mDirectoryType;
416 }
417
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700418 public String getDirectoryAccountType() {
419 return mDirectoryAccountType;
420 }
421
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700422 public String getDirectoryAccountName() {
423 return mDirectoryAccountName;
424 }
425
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800426 public boolean isLoadingPhoto() {
427 return mLoadingPhoto;
428 }
429
430 public byte[] getPhotoBinaryData() {
431 return mPhotoBinaryData;
432 }
433
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700434 public ArrayList<ContentValues> getContentValues() {
435 if (mEntities.size() != 1) {
436 throw new IllegalStateException(
437 "Cannot extract content values from an aggregated contact");
438 }
439
440 Entity entity = mEntities.get(0);
441 ArrayList<ContentValues> result = new ArrayList<ContentValues>();
442 ArrayList<NamedContentValues> subValues = entity.getSubValues();
443 if (subValues != null) {
444 int size = subValues.size();
445 for (int i = 0; i < size; i++) {
446 NamedContentValues pair = subValues.get(i);
447 if (Data.CONTENT_URI.equals(pair.uri)) {
448 result.add(pair.values);
449 }
450 }
451 }
Dmitri Plotnikov40ec3a82010-11-10 11:25:33 -0800452
453 // If the photo was loaded using the URI, create an entry for the photo
454 // binary data.
455 if (mPhotoId == 0 && mPhotoBinaryData != null) {
456 ContentValues photo = new ContentValues();
457 photo.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
458 photo.put(Photo.PHOTO, mPhotoBinaryData);
459 result.add(photo);
460 }
461
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700462 return result;
463 }
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700464
Daniel Lehmann1ad4d1b2010-10-18 19:20:41 -0700465 private void addGroupMetaData(GroupMetaData group) {
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700466 if (mGroups == null) {
Dmitri Plotnikove843f912010-09-16 15:21:48 -0700467 mGroups = new ArrayList<GroupMetaData>();
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700468 }
469 mGroups.add(group);
470 }
471
Dmitri Plotnikove843f912010-09-16 15:21:48 -0700472 public List<GroupMetaData> getGroupMetaData() {
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700473 return mGroups;
474 }
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700475
476 public boolean isSendToVoicemail() {
477 return mSendToVoicemail;
478 }
479
480 public String getCustomRingtone() {
481 return mCustomRingtone;
482 }
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700483
484 public boolean isUserProfile() {
485 return mIsUserProfile;
486 }
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700487 }
488
Dave Santoro39156002011-07-19 01:18:14 -0700489 /**
490 * Projection used for the query that loads all data for the entire contact (except for
491 * social stream items).
492 */
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700493 private static class ContactQuery {
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700494 final static String[] COLUMNS = new String[] {
495 Contacts.NAME_RAW_CONTACT_ID,
496 Contacts.DISPLAY_NAME_SOURCE,
497 Contacts.LOOKUP_KEY,
498 Contacts.DISPLAY_NAME,
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700499 Contacts.DISPLAY_NAME_ALTERNATIVE,
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700500 Contacts.PHONETIC_NAME,
501 Contacts.PHOTO_ID,
502 Contacts.STARRED,
503 Contacts.CONTACT_PRESENCE,
504 Contacts.CONTACT_STATUS,
505 Contacts.CONTACT_STATUS_TIMESTAMP,
506 Contacts.CONTACT_STATUS_RES_PACKAGE,
507 Contacts.CONTACT_STATUS_LABEL,
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700508 Contacts.Entity.CONTACT_ID,
509 Contacts.Entity.RAW_CONTACT_ID,
510
511 RawContacts.ACCOUNT_NAME,
512 RawContacts.ACCOUNT_TYPE,
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700513 RawContacts.DATA_SET,
514 RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700515 RawContacts.DIRTY,
516 RawContacts.VERSION,
517 RawContacts.SOURCE_ID,
518 RawContacts.SYNC1,
519 RawContacts.SYNC2,
520 RawContacts.SYNC3,
521 RawContacts.SYNC4,
522 RawContacts.DELETED,
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700523 RawContacts.NAME_VERIFIED,
524
525 Contacts.Entity.DATA_ID,
526 Data.DATA1,
527 Data.DATA2,
528 Data.DATA3,
529 Data.DATA4,
530 Data.DATA5,
531 Data.DATA6,
532 Data.DATA7,
533 Data.DATA8,
534 Data.DATA9,
535 Data.DATA10,
536 Data.DATA11,
537 Data.DATA12,
538 Data.DATA13,
539 Data.DATA14,
540 Data.DATA15,
541 Data.SYNC1,
542 Data.SYNC2,
543 Data.SYNC3,
544 Data.SYNC4,
545 Data.DATA_VERSION,
546 Data.IS_PRIMARY,
547 Data.IS_SUPER_PRIMARY,
548 Data.MIMETYPE,
549 Data.RES_PACKAGE,
550
551 GroupMembership.GROUP_SOURCE_ID,
552
553 Data.PRESENCE,
Daniel Lehmann8fd7bb62010-08-13 20:50:31 -0700554 Data.CHAT_CAPABILITY,
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700555 Data.STATUS,
556 Data.STATUS_RES_PACKAGE,
557 Data.STATUS_ICON,
558 Data.STATUS_LABEL,
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700559 Data.STATUS_TIMESTAMP,
560
561 Contacts.PHOTO_URI,
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700562 Contacts.SEND_TO_VOICEMAIL,
563 Contacts.CUSTOM_RINGTONE,
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700564 Contacts.IS_USER_PROFILE,
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700565 };
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700566
567 public final static int NAME_RAW_CONTACT_ID = 0;
568 public final static int DISPLAY_NAME_SOURCE = 1;
569 public final static int LOOKUP_KEY = 2;
570 public final static int DISPLAY_NAME = 3;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700571 public final static int ALT_DISPLAY_NAME = 4;
572 public final static int PHONETIC_NAME = 5;
573 public final static int PHOTO_ID = 6;
574 public final static int STARRED = 7;
575 public final static int CONTACT_PRESENCE = 8;
576 public final static int CONTACT_STATUS = 9;
577 public final static int CONTACT_STATUS_TIMESTAMP = 10;
578 public final static int CONTACT_STATUS_RES_PACKAGE = 11;
579 public final static int CONTACT_STATUS_LABEL = 12;
580 public final static int CONTACT_ID = 13;
581 public final static int RAW_CONTACT_ID = 14;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700582
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700583 public final static int ACCOUNT_NAME = 15;
584 public final static int ACCOUNT_TYPE = 16;
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700585 public final static int DATA_SET = 17;
586 public final static int ACCOUNT_TYPE_AND_DATA_SET = 18;
587 public final static int DIRTY = 19;
588 public final static int VERSION = 20;
589 public final static int SOURCE_ID = 21;
590 public final static int SYNC1 = 22;
591 public final static int SYNC2 = 23;
592 public final static int SYNC3 = 24;
593 public final static int SYNC4 = 25;
594 public final static int DELETED = 26;
595 public final static int NAME_VERIFIED = 27;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700596
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700597 public final static int DATA_ID = 28;
598 public final static int DATA1 = 29;
599 public final static int DATA2 = 30;
600 public final static int DATA3 = 31;
601 public final static int DATA4 = 32;
602 public final static int DATA5 = 33;
603 public final static int DATA6 = 34;
604 public final static int DATA7 = 35;
605 public final static int DATA8 = 36;
606 public final static int DATA9 = 37;
607 public final static int DATA10 = 38;
608 public final static int DATA11 = 39;
609 public final static int DATA12 = 40;
610 public final static int DATA13 = 41;
611 public final static int DATA14 = 42;
612 public final static int DATA15 = 43;
613 public final static int DATA_SYNC1 = 44;
614 public final static int DATA_SYNC2 = 45;
615 public final static int DATA_SYNC3 = 46;
616 public final static int DATA_SYNC4 = 47;
617 public final static int DATA_VERSION = 48;
618 public final static int IS_PRIMARY = 49;
619 public final static int IS_SUPERPRIMARY = 50;
620 public final static int MIMETYPE = 51;
621 public final static int RES_PACKAGE = 52;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700622
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700623 public final static int GROUP_SOURCE_ID = 53;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700624
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700625 public final static int PRESENCE = 54;
626 public final static int CHAT_CAPABILITY = 55;
627 public final static int STATUS = 56;
628 public final static int STATUS_RES_PACKAGE = 57;
629 public final static int STATUS_ICON = 58;
630 public final static int STATUS_LABEL = 59;
631 public final static int STATUS_TIMESTAMP = 60;
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700632
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700633 public final static int PHOTO_URI = 61;
634 public final static int SEND_TO_VOICEMAIL = 62;
635 public final static int CUSTOM_RINGTONE = 63;
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700636 public final static int IS_USER_PROFILE = 64;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700637 }
Daniel Lehmann1316b132010-04-13 15:08:53 -0700638
Dave Santoro39156002011-07-19 01:18:14 -0700639 /**
640 * Projection used for the query that loads all data for the entire contact.
641 */
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700642 private static class DirectoryQuery {
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700643 final static String[] COLUMNS = new String[] {
644 Directory.DISPLAY_NAME,
645 Directory.PACKAGE_NAME,
646 Directory.TYPE_RESOURCE_ID,
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700647 Directory.ACCOUNT_TYPE,
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700648 Directory.ACCOUNT_NAME,
649 Directory.EXPORT_SUPPORT,
650 };
651
652 public final static int DISPLAY_NAME = 0;
653 public final static int PACKAGE_NAME = 1;
654 public final static int TYPE_RESOURCE_ID = 2;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700655 public final static int ACCOUNT_TYPE = 3;
656 public final static int ACCOUNT_NAME = 4;
657 public final static int EXPORT_SUPPORT = 5;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700658 }
659
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700660 private static class GroupQuery {
661 final static String[] COLUMNS = new String[] {
662 Groups.ACCOUNT_NAME,
663 Groups.ACCOUNT_TYPE,
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700664 Groups.DATA_SET,
665 Groups.ACCOUNT_TYPE_AND_DATA_SET,
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700666 Groups._ID,
667 Groups.TITLE,
668 Groups.AUTO_ADD,
669 Groups.FAVORITES,
670 };
671
672 public final static int ACCOUNT_NAME = 0;
673 public final static int ACCOUNT_TYPE = 1;
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700674 public final static int DATA_SET = 2;
675 public final static int ACCOUNT_TYPE_AND_DATA_SET = 3;
676 public final static int ID = 4;
677 public final static int TITLE = 5;
678 public final static int AUTO_ADD = 6;
679 public final static int FAVORITES = 7;
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700680 }
681
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700682 private final class LoadContactTask extends AsyncTask<Void, Void, Result> {
Daniel Lehmann1316b132010-04-13 15:08:53 -0700683
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700684 @Override
685 protected Result doInBackground(Void... args) {
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700686 try {
687 final ContentResolver resolver = getContext().getContentResolver();
Katherine Kuan6cd5b0a2011-09-16 11:46:01 -0700688 final Uri uriCurrentFormat = ContactLoaderUtils.ensureIsContactUri(
689 resolver, mLookupUri);
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700690 Result result = loadContactEntity(resolver, uriCurrentFormat);
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700691 if (!result.isNotFound()) {
Dmitri Plotnikov217245c2010-09-18 13:04:50 -0700692 if (result.isDirectoryEntry()) {
693 loadDirectoryMetaData(result);
694 } else if (mLoadGroupMetaData) {
695 loadGroupMetaData(result);
696 }
Dave Santoro39156002011-07-19 01:18:14 -0700697 if (mLoadStreamItems) {
698 loadStreamItems(result);
699 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800700 loadPhotoBinaryData(result);
Makoto Onuki870a87e2011-08-12 13:40:31 -0700701
702 // Note ME profile should never have "Add connection"
703 if (mLoadInvitableAccountTypes && !result.isUserProfile()) {
Makoto Onuki69b4a882011-07-22 10:05:10 -0700704 loadInvitableAccountTypes(result);
705 }
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700706 }
707 return result;
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700708 } catch (Exception e) {
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700709 Log.e(TAG, "Error loading the contact: " + mLookupUri, e);
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700710 return Result.forError(mRequestedUri, e);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700711 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -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");
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700720 return Result.forNotFound(mRequestedUri);
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();
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700726 return Result.forNotFound(mRequestedUri);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700727 }
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}.
Makoto Onuki3e6991e2011-07-24 14:51:20 -0700817 */
Makoto Onuki69b4a882011-07-22 10:05:10 -0700818 private void loadInvitableAccountTypes(Result contactData) {
Katherine Kuan08bcf712011-10-09 13:43:53 -0700819 Map<AccountTypeWithDataSet, AccountType> invitables =
820 AccountTypeManager.getInstance(getContext()).getUsableInvitableAccountTypes();
821 if (invitables.isEmpty()) {
Makoto Onuki69b4a882011-07-22 10:05:10 -0700822 return;
823 }
824
Katherine Kuan08bcf712011-10-09 13:43:53 -0700825 HashMap<AccountTypeWithDataSet, AccountType> result = Maps.newHashMap(invitables);
Makoto Onuki69b4a882011-07-22 10:05:10 -0700826
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -0700827 // Remove the ones that already have a raw contact in the current contact
Makoto Onuki69b4a882011-07-22 10:05:10 -0700828 for (Entity entity : contactData.getEntities()) {
Makoto Onuki6ad227f2011-08-15 13:46:59 -0700829 final ContentValues values = entity.getEntityValues();
830 final AccountTypeWithDataSet type = AccountTypeWithDataSet.get(
831 values.getAsString(RawContacts.ACCOUNT_TYPE),
832 values.getAsString(RawContacts.DATA_SET));
833 result.remove(type);
Makoto Onuki69b4a882011-07-22 10:05:10 -0700834 }
835
836 // Set to mInvitableAccountTypes
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700837 contactData.mInvitableAccountTypes.addAll(result.values());
Makoto Onuki69b4a882011-07-22 10:05:10 -0700838 }
839
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800840 /**
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700841 * Extracts Contact level columns from the cursor.
842 */
843 private Result loadContactHeaderData(final Cursor cursor, Uri contactUri) {
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700844 final String directoryParameter =
845 contactUri.getQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY);
846 final long directoryId = directoryParameter == null
847 ? Directory.DEFAULT
848 : Long.parseLong(directoryParameter);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700849 final long contactId = cursor.getLong(ContactQuery.CONTACT_ID);
850 final String lookupKey = cursor.getString(ContactQuery.LOOKUP_KEY);
851 final long nameRawContactId = cursor.getLong(ContactQuery.NAME_RAW_CONTACT_ID);
852 final int displayNameSource = cursor.getInt(ContactQuery.DISPLAY_NAME_SOURCE);
853 final String displayName = cursor.getString(ContactQuery.DISPLAY_NAME);
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700854 final String altDisplayName = cursor.getString(ContactQuery.ALT_DISPLAY_NAME);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700855 final String phoneticName = cursor.getString(ContactQuery.PHONETIC_NAME);
856 final long photoId = cursor.getLong(ContactQuery.PHOTO_ID);
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700857 final String photoUri = cursor.getString(ContactQuery.PHOTO_URI);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700858 final boolean starred = cursor.getInt(ContactQuery.STARRED) != 0;
859 final Integer presence = cursor.isNull(ContactQuery.CONTACT_PRESENCE)
860 ? null
861 : cursor.getInt(ContactQuery.CONTACT_PRESENCE);
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700862 final boolean sendToVoicemail = cursor.getInt(ContactQuery.SEND_TO_VOICEMAIL) == 1;
863 final String customRingtone = cursor.getString(ContactQuery.CUSTOM_RINGTONE);
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700864 final boolean isUserProfile = cursor.getInt(ContactQuery.IS_USER_PROFILE) == 1;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700865
Dmitri Plotnikov1536ea12010-10-29 11:51:05 -0700866 Uri lookupUri;
867 if (directoryId == Directory.DEFAULT || directoryId == Directory.LOCAL_INVISIBLE) {
868 lookupUri = ContentUris.withAppendedId(
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700869 Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey), contactId);
Dmitri Plotnikov1536ea12010-10-29 11:51:05 -0700870 } else {
871 lookupUri = contactUri;
872 }
873
Daniel Lehmann685157e2011-08-29 21:07:01 -0700874 return new Result(mRequestedUri, contactUri, lookupUri, directoryId, lookupKey,
875 contactId, nameRawContactId, displayNameSource, photoId, photoUri, displayName,
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700876 altDisplayName, phoneticName, starred, presence, sendToVoicemail,
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700877 customRingtone, isUserProfile);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700878 }
879
880 /**
881 * Extracts RawContact level columns from the cursor.
882 */
883 private ContentValues loadRawContact(Cursor cursor) {
884 ContentValues cv = new ContentValues();
885
886 cv.put(RawContacts._ID, cursor.getLong(ContactQuery.RAW_CONTACT_ID));
887
888 cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_NAME);
889 cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_TYPE);
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700890 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SET);
891 cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_TYPE_AND_DATA_SET);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700892 cursorColumnToContentValues(cursor, cv, ContactQuery.DIRTY);
893 cursorColumnToContentValues(cursor, cv, ContactQuery.VERSION);
894 cursorColumnToContentValues(cursor, cv, ContactQuery.SOURCE_ID);
895 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC1);
896 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC2);
897 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC3);
898 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC4);
899 cursorColumnToContentValues(cursor, cv, ContactQuery.DELETED);
900 cursorColumnToContentValues(cursor, cv, ContactQuery.CONTACT_ID);
901 cursorColumnToContentValues(cursor, cv, ContactQuery.STARRED);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700902 cursorColumnToContentValues(cursor, cv, ContactQuery.NAME_VERIFIED);
903
904 return cv;
905 }
906
907 /**
908 * Extracts Data level columns from the cursor.
909 */
910 private ContentValues loadData(Cursor cursor) {
911 ContentValues cv = new ContentValues();
912
913 cv.put(Data._ID, cursor.getLong(ContactQuery.DATA_ID));
914
915 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA1);
916 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA2);
917 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA3);
918 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA4);
919 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA5);
920 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA6);
921 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA7);
922 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA8);
923 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA9);
924 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA10);
925 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA11);
926 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA12);
927 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA13);
928 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA14);
929 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA15);
930 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC1);
931 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC2);
932 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC3);
933 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC4);
934 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_VERSION);
935 cursorColumnToContentValues(cursor, cv, ContactQuery.IS_PRIMARY);
936 cursorColumnToContentValues(cursor, cv, ContactQuery.IS_SUPERPRIMARY);
937 cursorColumnToContentValues(cursor, cv, ContactQuery.MIMETYPE);
938 cursorColumnToContentValues(cursor, cv, ContactQuery.RES_PACKAGE);
939 cursorColumnToContentValues(cursor, cv, ContactQuery.GROUP_SOURCE_ID);
Daniel Lehmann8fd7bb62010-08-13 20:50:31 -0700940 cursorColumnToContentValues(cursor, cv, ContactQuery.CHAT_CAPABILITY);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700941
942 return cv;
943 }
944
945 private void cursorColumnToContentValues(
946 Cursor cursor, ContentValues values, int index) {
947 switch (cursor.getType(index)) {
948 case Cursor.FIELD_TYPE_NULL:
949 // don't put anything in the content values
950 break;
951 case Cursor.FIELD_TYPE_INTEGER:
952 values.put(ContactQuery.COLUMNS[index], cursor.getLong(index));
953 break;
954 case Cursor.FIELD_TYPE_STRING:
955 values.put(ContactQuery.COLUMNS[index], cursor.getString(index));
956 break;
957 case Cursor.FIELD_TYPE_BLOB:
958 values.put(ContactQuery.COLUMNS[index], cursor.getBlob(index));
959 break;
960 default:
961 throw new IllegalStateException("Invalid or unhandled data type");
962 }
963 }
964
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700965 private void loadDirectoryMetaData(Result result) {
966 long directoryId = result.getDirectoryId();
967
968 Cursor cursor = getContext().getContentResolver().query(
969 ContentUris.withAppendedId(Directory.CONTENT_URI, directoryId),
970 DirectoryQuery.COLUMNS, null, null, null);
971 if (cursor == null) {
972 return;
973 }
974 try {
975 if (cursor.moveToFirst()) {
976 final String displayName = cursor.getString(DirectoryQuery.DISPLAY_NAME);
977 final String packageName = cursor.getString(DirectoryQuery.PACKAGE_NAME);
978 final int typeResourceId = cursor.getInt(DirectoryQuery.TYPE_RESOURCE_ID);
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700979 final String accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE);
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700980 final String accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME);
981 final int exportSupport = cursor.getInt(DirectoryQuery.EXPORT_SUPPORT);
982 String directoryType = null;
983 if (!TextUtils.isEmpty(packageName)) {
984 PackageManager pm = getContext().getPackageManager();
985 try {
986 Resources resources = pm.getResourcesForApplication(packageName);
987 directoryType = resources.getString(typeResourceId);
988 } catch (NameNotFoundException e) {
989 Log.w(TAG, "Contact directory resource not found: "
990 + packageName + "." + typeResourceId);
991 }
992 }
993
994 result.setDirectoryMetaData(
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700995 displayName, directoryType, accountType, accountName, exportSupport);
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700996 }
997 } finally {
998 cursor.close();
999 }
1000 }
1001
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001002 /**
1003 * Loads groups meta-data for all groups associated with all constituent raw contacts'
1004 * accounts.
1005 */
1006 private void loadGroupMetaData(Result result) {
1007 StringBuilder selection = new StringBuilder();
1008 ArrayList<String> selectionArgs = new ArrayList<String>();
1009 for (Entity entity : result.mEntities) {
1010 ContentValues values = entity.getEntityValues();
1011 String accountName = values.getAsString(RawContacts.ACCOUNT_NAME);
1012 String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
Dave Santoro2b3f3c52011-07-26 17:35:42 -07001013 String dataSet = values.getAsString(RawContacts.DATA_SET);
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001014 if (accountName != null && accountType != null) {
1015 if (selection.length() != 0) {
1016 selection.append(" OR ");
1017 }
1018 selection.append(
Dave Santoro2b3f3c52011-07-26 17:35:42 -07001019 "(" + Groups.ACCOUNT_NAME + "=? AND " + Groups.ACCOUNT_TYPE + "=?");
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001020 selectionArgs.add(accountName);
1021 selectionArgs.add(accountType);
Dave Santoro2b3f3c52011-07-26 17:35:42 -07001022
1023 if (dataSet != null) {
1024 selection.append(" AND " + Groups.DATA_SET + "=?");
1025 selectionArgs.add(dataSet);
1026 } else {
1027 selection.append(" AND " + Groups.DATA_SET + " IS NULL");
1028 }
1029 selection.append(")");
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001030 }
1031 }
1032 Cursor cursor = getContext().getContentResolver().query(Groups.CONTENT_URI,
1033 GroupQuery.COLUMNS, selection.toString(), selectionArgs.toArray(new String[0]),
1034 null);
1035 try {
1036 while (cursor.moveToNext()) {
1037 final String accountName = cursor.getString(GroupQuery.ACCOUNT_NAME);
1038 final String accountType = cursor.getString(GroupQuery.ACCOUNT_TYPE);
Dave Santoro2b3f3c52011-07-26 17:35:42 -07001039 final String dataSet = cursor.getString(GroupQuery.DATA_SET);
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001040 final long groupId = cursor.getLong(GroupQuery.ID);
1041 final String title = cursor.getString(GroupQuery.TITLE);
1042 final boolean defaultGroup = cursor.isNull(GroupQuery.AUTO_ADD)
1043 ? false
1044 : cursor.getInt(GroupQuery.AUTO_ADD) != 0;
1045 final boolean favorites = cursor.isNull(GroupQuery.FAVORITES)
1046 ? false
1047 : cursor.getInt(GroupQuery.FAVORITES) != 0;
1048
Dmitri Plotnikove843f912010-09-16 15:21:48 -07001049 result.addGroupMetaData(new GroupMetaData(
Dave Santoro2b3f3c52011-07-26 17:35:42 -07001050 accountName, accountType, dataSet, groupId, title, defaultGroup,
1051 favorites));
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001052 }
1053 } finally {
1054 cursor.close();
1055 }
1056 }
1057
Dave Santoro39156002011-07-19 01:18:14 -07001058 /**
1059 * Loads all stream items and stream item photos belonging to this contact.
1060 */
1061 private void loadStreamItems(Result result) {
1062 Cursor cursor = getContext().getContentResolver().query(
1063 Contacts.CONTENT_LOOKUP_URI.buildUpon()
1064 .appendPath(result.getLookupKey())
1065 .appendPath(Contacts.StreamItems.CONTENT_DIRECTORY).build(),
1066 null, null, null, null);
1067 Map<Long, StreamItemEntry> streamItemsById = new HashMap<Long, StreamItemEntry>();
1068 ArrayList<StreamItemEntry> streamItems = new ArrayList<StreamItemEntry>();
1069 try {
1070 while (cursor.moveToNext()) {
1071 StreamItemEntry streamItem = new StreamItemEntry(cursor);
1072 streamItemsById.put(streamItem.getId(), streamItem);
1073 streamItems.add(streamItem);
1074 }
1075 } finally {
1076 cursor.close();
1077 }
1078
1079 // Now retrieve any photo records associated with the stream items.
Dave Santoro39156002011-07-19 01:18:14 -07001080 if (!streamItems.isEmpty()) {
Dave Santoroa4400d52011-09-02 16:14:53 -07001081 if (result.isUserProfile()) {
1082 // If the stream items we're loading are for the profile, we can't bulk-load the
1083 // stream items with a custom selection.
1084 for (StreamItemEntry entry : streamItems) {
1085 Cursor siCursor = getContext().getContentResolver().query(
1086 Uri.withAppendedPath(
1087 ContentUris.withAppendedId(
1088 StreamItems.CONTENT_URI, entry.getId()),
1089 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY),
1090 null, null, null, null);
1091 try {
1092 while (siCursor.moveToNext()) {
1093 entry.addPhoto(new StreamItemPhotoEntry(siCursor));
1094 }
1095 } finally {
1096 siCursor.close();
1097 }
Dave Santoro39156002011-07-19 01:18:14 -07001098 }
Dave Santoroa4400d52011-09-02 16:14:53 -07001099 } else {
1100 String[] streamItemIdArr = new String[streamItems.size()];
1101 StringBuilder streamItemPhotoSelection = new StringBuilder();
1102 streamItemPhotoSelection.append(StreamItemPhotos.STREAM_ITEM_ID + " IN (");
1103 for (int i = 0; i < streamItems.size(); i++) {
1104 if (i > 0) {
1105 streamItemPhotoSelection.append(",");
1106 }
1107 streamItemPhotoSelection.append("?");
1108 streamItemIdArr[i] = String.valueOf(streamItems.get(i).getId());
Dave Santoro39156002011-07-19 01:18:14 -07001109 }
Dave Santoroa4400d52011-09-02 16:14:53 -07001110 streamItemPhotoSelection.append(")");
1111 Cursor sipCursor = getContext().getContentResolver().query(
1112 StreamItems.CONTENT_PHOTO_URI,
1113 null, streamItemPhotoSelection.toString(), streamItemIdArr,
1114 StreamItemPhotos.STREAM_ITEM_ID);
1115 try {
1116 while (sipCursor.moveToNext()) {
1117 long streamItemId = sipCursor.getLong(
1118 sipCursor.getColumnIndex(StreamItemPhotos.STREAM_ITEM_ID));
1119 StreamItemEntry streamItem = streamItemsById.get(streamItemId);
1120 streamItem.addPhoto(new StreamItemPhotoEntry(sipCursor));
1121 }
1122 } finally {
1123 sipCursor.close();
1124 }
Dave Santoro39156002011-07-19 01:18:14 -07001125 }
1126 }
1127
1128 // Set the sorted stream items on the result.
1129 Collections.sort(streamItems);
1130 result.mStreamItems.addAll(streamItems);
1131 }
1132
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001133 @Override
1134 protected void onPostExecute(Result result) {
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001135 unregisterObserver();
1136
Daniel Lehmann1316b132010-04-13 15:08:53 -07001137 // The creator isn't interested in any further updates
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001138 if (mDestroyed || result == null) {
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001139 return;
1140 }
1141
1142 mContact = result;
Daniel Lehmann18f104f2010-05-07 15:41:11 -07001143
Makoto Onuki2621c5b2011-10-03 12:56:16 -07001144 if (result.isLoaded()) {
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001145 mLookupUri = result.getLookupUri();
1146
1147 if (!result.isDirectoryEntry()) {
1148 Log.i(TAG, "Registering content observer for " + mLookupUri);
1149 if (mObserver == null) {
1150 mObserver = new ForceLoadContentObserver();
1151 }
1152 getContext().getContentResolver().registerContentObserver(
1153 mLookupUri, true, mObserver);
Daniel Lehmann18f104f2010-05-07 15:41:11 -07001154 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -08001155
1156 if (mContact.getPhotoBinaryData() == null && mContact.getPhotoUri() != null) {
1157 mContact.setLoadingPhoto(true);
1158 new AsyncPhotoLoader().execute(mContact.getPhotoUri());
1159 }
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001160
1161 // inform the source of the data that this contact is being looked at
1162 postViewNotificationToSyncAdapter();
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001163 }
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001164
1165 deliverResult(mContact);
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001166 }
1167 }
1168
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001169 /**
1170 * Posts a message to the contributing sync adapters that have opted-in, notifying them
1171 * that the contact has just been loaded
1172 */
1173 private void postViewNotificationToSyncAdapter() {
1174 Context context = getContext();
1175 for (Entity entity : mContact.getEntities()) {
1176 final ContentValues entityValues = entity.getEntityValues();
Makoto Onukiaba2b832011-08-12 15:44:53 -07001177 final long rawContactId = entityValues.getAsLong(RawContacts.Entity._ID);
1178 if (mNotifiedRawContactIds.contains(rawContactId)) {
1179 continue; // Already notified for this raw contact.
1180 }
1181 mNotifiedRawContactIds.add(rawContactId);
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001182 final String type = entityValues.getAsString(RawContacts.ACCOUNT_TYPE);
1183 final String dataSet = entityValues.getAsString(RawContacts.DATA_SET);
Flavio Lerda59a887e2011-08-14 18:13:17 +01001184 final AccountType accountType = AccountTypeManager.getInstance(context).getAccountType(
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001185 type, dataSet);
1186 final String serviceName = accountType.getViewContactNotifyServiceClassName();
1187 final String resPackageName = accountType.resPackageName;
1188 if (!TextUtils.isEmpty(serviceName) && !TextUtils.isEmpty(resPackageName)) {
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001189 final Uri uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
1190 final Intent intent = new Intent();
1191 intent.setClassName(resPackageName, serviceName);
1192 intent.setAction(Intent.ACTION_VIEW);
1193 intent.setDataAndType(uri, RawContacts.CONTENT_ITEM_TYPE);
1194 try {
1195 context.startService(intent);
1196 } catch (Exception e) {
1197 Log.e(TAG, "Error sending message to source-app", e);
1198 }
1199 }
1200 }
1201 }
1202
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -08001203 private class AsyncPhotoLoader extends AsyncTask<String, Void, byte[]> {
1204
1205 private static final int BUFFER_SIZE = 1024*16;
1206
1207 @Override
1208 protected byte[] doInBackground(String... params) {
1209 Uri uri = Uri.parse(params[0]);
1210 byte[] data = null;
1211 try {
1212 InputStream is = getContext().getContentResolver().openInputStream(uri);
1213 if (is != null) {
1214 ByteArrayOutputStream baos = new ByteArrayOutputStream();
1215 try {
1216 byte[] mBuffer = new byte[BUFFER_SIZE];
1217
1218 int size;
1219 while ((size = is.read(mBuffer)) != -1) {
1220 baos.write(mBuffer, 0, size);
1221 }
1222 data = baos.toByteArray();
1223 } finally {
1224 is.close();
1225 }
1226 } else {
1227 Log.v(TAG, "Cannot load photo " + uri);
1228 }
1229 } catch (IOException e) {
1230 Log.e(TAG, "Cannot load photo " + uri, e);
1231 }
1232
1233 return data;
1234 }
1235
1236 @Override
1237 protected void onPostExecute(byte[] data) {
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001238 if (mContact != null) {
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -08001239 mContact = new Result(mContact);
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001240 mContact.setPhotoBinaryData(data);
1241 mContact.setLoadingPhoto(false);
1242 deliverResult(mContact);
1243 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -08001244 }
1245 }
1246
Daniel Lehmann3a120772010-06-21 16:21:35 -07001247 private void unregisterObserver() {
1248 if (mObserver != null) {
1249 getContext().getContentResolver().unregisterContentObserver(mObserver);
1250 mObserver = null;
1251 }
1252 }
1253
Daniel Lehmanncdef2b62010-06-06 18:25:49 -07001254 public ContactLoader(Context context, Uri lookupUri) {
Makoto Onuki69b4a882011-07-22 10:05:10 -07001255 this(context, lookupUri, false, false, false);
Dmitri Plotnikove843f912010-09-16 15:21:48 -07001256 }
1257
Dave Santoro39156002011-07-19 01:18:14 -07001258 public ContactLoader(Context context, Uri lookupUri, boolean loadGroupMetaData,
Makoto Onuki69b4a882011-07-22 10:05:10 -07001259 boolean loadStreamItems, boolean loadInvitableAccountTypes) {
Daniel Lehmann74a2dc52010-04-15 16:52:33 -07001260 super(context);
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001261 mLookupUri = lookupUri;
Daniel Lehmann685157e2011-08-29 21:07:01 -07001262 mRequestedUri = lookupUri;
Dmitri Plotnikove843f912010-09-16 15:21:48 -07001263 mLoadGroupMetaData = loadGroupMetaData;
Dave Santoro39156002011-07-19 01:18:14 -07001264 mLoadStreamItems = loadStreamItems;
Makoto Onuki69b4a882011-07-22 10:05:10 -07001265 mLoadInvitableAccountTypes = loadInvitableAccountTypes;
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001266 }
1267
Daniel Lehmann2a45e352012-02-13 15:03:58 -08001268 /**
1269 * Sets whether to load stream items. Will trigger a reload if the value has changed.
1270 * At the moment, this is only used for debugging purposes
1271 */
1272 public void setLoadStreamItems(boolean value) {
1273 if (mLoadStreamItems != value) {
1274 mLoadStreamItems = value;
1275 onContentChanged();
1276 }
1277 }
1278
1279 public boolean getLoadStreamItems() {
1280 return mLoadStreamItems;
1281 }
1282
Dmitri Plotnikov5a30d9a2010-11-23 14:59:50 -08001283 public Uri getLookupUri() {
1284 return mLookupUri;
1285 }
1286
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001287 @Override
Dianne Hackbornc04fc272010-12-20 23:13:10 -08001288 protected void onStartLoading() {
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001289 if (mContact != null) {
Daniel Lehmanncbcc4492010-04-12 18:03:54 -07001290 deliverResult(mContact);
Dmitri Plotnikov97e90c62011-01-03 11:58:13 -08001291 }
1292
1293 if (takeContentChanged() || mContact == null) {
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001294 forceLoad();
1295 }
1296 }
1297
1298 @Override
Dianne Hackbornc04fc272010-12-20 23:13:10 -08001299 protected void onForceLoad() {
Daniel Lehmann18f104f2010-05-07 15:41:11 -07001300 final LoadContactTask task = new LoadContactTask();
Makoto Onuki73c341f2011-08-29 09:31:44 -07001301 task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[])null);
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001302 }
1303
1304 @Override
Dianne Hackbornc04fc272010-12-20 23:13:10 -08001305 protected void onReset() {
Dianne Hackborn4ef95cc2010-12-16 00:44:33 -08001306 unregisterObserver();
1307 mContact = null;
1308 mDestroyed = true;
1309 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001310}