blob: bb76aead6b531b2bda6bd5b0a50230fb07bc23f0 [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;
Daniel Lehmann18958a22012-02-28 17:45:25 -080059import android.util.LongSparseArray;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070060
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -080061import java.io.ByteArrayOutputStream;
Dave Santoro0a2a5db2011-06-29 00:37:06 -070062import java.io.FileInputStream;
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -080063import java.io.IOException;
64import java.io.InputStream;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070065import java.util.ArrayList;
Dave Santoro39156002011-07-19 01:18:14 -070066import java.util.Collections;
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 Lehmann18958a22012-02-28 17:45:25 -0800121 private final LongSparseArray<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 Lehmann18958a22012-02-28 17:45:25 -0800200 mStatuses = new LongSparseArray<DataStatus>();
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700201 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 Lehmann18958a22012-02-28 17:45:25 -0800393 public LongSparseArray<DataStatus> getStatuses() {
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700394 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();
Daniel Lehmann18958a22012-02-28 17:45:25 -0800733 LongSparseArray<DataStatus> statuses = result.getStatuses();
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700734 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
Daniel Lehmann18958a22012-02-28 17:45:25 -0800825 Map<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);
Daniel Lehmann18958a22012-02-28 17:45:25 -08001067 LongSparseArray<StreamItemEntry> streamItemsById =
1068 new LongSparseArray<StreamItemEntry>();
Dave Santoro39156002011-07-19 01:18:14 -07001069 ArrayList<StreamItemEntry> streamItems = new ArrayList<StreamItemEntry>();
1070 try {
1071 while (cursor.moveToNext()) {
1072 StreamItemEntry streamItem = new StreamItemEntry(cursor);
1073 streamItemsById.put(streamItem.getId(), streamItem);
1074 streamItems.add(streamItem);
1075 }
1076 } finally {
1077 cursor.close();
1078 }
1079
1080 // Now retrieve any photo records associated with the stream items.
Dave Santoro39156002011-07-19 01:18:14 -07001081 if (!streamItems.isEmpty()) {
Dave Santoroa4400d52011-09-02 16:14:53 -07001082 if (result.isUserProfile()) {
1083 // If the stream items we're loading are for the profile, we can't bulk-load the
1084 // stream items with a custom selection.
1085 for (StreamItemEntry entry : streamItems) {
1086 Cursor siCursor = getContext().getContentResolver().query(
1087 Uri.withAppendedPath(
1088 ContentUris.withAppendedId(
1089 StreamItems.CONTENT_URI, entry.getId()),
1090 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY),
1091 null, null, null, null);
1092 try {
1093 while (siCursor.moveToNext()) {
1094 entry.addPhoto(new StreamItemPhotoEntry(siCursor));
1095 }
1096 } finally {
1097 siCursor.close();
1098 }
Dave Santoro39156002011-07-19 01:18:14 -07001099 }
Dave Santoroa4400d52011-09-02 16:14:53 -07001100 } else {
1101 String[] streamItemIdArr = new String[streamItems.size()];
1102 StringBuilder streamItemPhotoSelection = new StringBuilder();
1103 streamItemPhotoSelection.append(StreamItemPhotos.STREAM_ITEM_ID + " IN (");
1104 for (int i = 0; i < streamItems.size(); i++) {
1105 if (i > 0) {
1106 streamItemPhotoSelection.append(",");
1107 }
1108 streamItemPhotoSelection.append("?");
1109 streamItemIdArr[i] = String.valueOf(streamItems.get(i).getId());
Dave Santoro39156002011-07-19 01:18:14 -07001110 }
Dave Santoroa4400d52011-09-02 16:14:53 -07001111 streamItemPhotoSelection.append(")");
1112 Cursor sipCursor = getContext().getContentResolver().query(
1113 StreamItems.CONTENT_PHOTO_URI,
1114 null, streamItemPhotoSelection.toString(), streamItemIdArr,
1115 StreamItemPhotos.STREAM_ITEM_ID);
1116 try {
1117 while (sipCursor.moveToNext()) {
1118 long streamItemId = sipCursor.getLong(
1119 sipCursor.getColumnIndex(StreamItemPhotos.STREAM_ITEM_ID));
1120 StreamItemEntry streamItem = streamItemsById.get(streamItemId);
1121 streamItem.addPhoto(new StreamItemPhotoEntry(sipCursor));
1122 }
1123 } finally {
1124 sipCursor.close();
1125 }
Dave Santoro39156002011-07-19 01:18:14 -07001126 }
1127 }
1128
1129 // Set the sorted stream items on the result.
1130 Collections.sort(streamItems);
1131 result.mStreamItems.addAll(streamItems);
1132 }
1133
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001134 @Override
1135 protected void onPostExecute(Result result) {
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001136 unregisterObserver();
1137
Daniel Lehmann1316b132010-04-13 15:08:53 -07001138 // The creator isn't interested in any further updates
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001139 if (mDestroyed || result == null) {
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001140 return;
1141 }
1142
1143 mContact = result;
Daniel Lehmann18f104f2010-05-07 15:41:11 -07001144
Makoto Onuki2621c5b2011-10-03 12:56:16 -07001145 if (result.isLoaded()) {
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001146 mLookupUri = result.getLookupUri();
1147
1148 if (!result.isDirectoryEntry()) {
1149 Log.i(TAG, "Registering content observer for " + mLookupUri);
1150 if (mObserver == null) {
1151 mObserver = new ForceLoadContentObserver();
1152 }
1153 getContext().getContentResolver().registerContentObserver(
1154 mLookupUri, true, mObserver);
Daniel Lehmann18f104f2010-05-07 15:41:11 -07001155 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -08001156
1157 if (mContact.getPhotoBinaryData() == null && mContact.getPhotoUri() != null) {
1158 mContact.setLoadingPhoto(true);
1159 new AsyncPhotoLoader().execute(mContact.getPhotoUri());
1160 }
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001161
1162 // inform the source of the data that this contact is being looked at
1163 postViewNotificationToSyncAdapter();
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001164 }
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001165
1166 deliverResult(mContact);
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001167 }
1168 }
1169
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001170 /**
1171 * Posts a message to the contributing sync adapters that have opted-in, notifying them
1172 * that the contact has just been loaded
1173 */
1174 private void postViewNotificationToSyncAdapter() {
1175 Context context = getContext();
1176 for (Entity entity : mContact.getEntities()) {
1177 final ContentValues entityValues = entity.getEntityValues();
Makoto Onukiaba2b832011-08-12 15:44:53 -07001178 final long rawContactId = entityValues.getAsLong(RawContacts.Entity._ID);
1179 if (mNotifiedRawContactIds.contains(rawContactId)) {
1180 continue; // Already notified for this raw contact.
1181 }
1182 mNotifiedRawContactIds.add(rawContactId);
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001183 final String type = entityValues.getAsString(RawContacts.ACCOUNT_TYPE);
1184 final String dataSet = entityValues.getAsString(RawContacts.DATA_SET);
Flavio Lerda59a887e2011-08-14 18:13:17 +01001185 final AccountType accountType = AccountTypeManager.getInstance(context).getAccountType(
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001186 type, dataSet);
1187 final String serviceName = accountType.getViewContactNotifyServiceClassName();
1188 final String resPackageName = accountType.resPackageName;
1189 if (!TextUtils.isEmpty(serviceName) && !TextUtils.isEmpty(resPackageName)) {
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001190 final Uri uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
1191 final Intent intent = new Intent();
1192 intent.setClassName(resPackageName, serviceName);
1193 intent.setAction(Intent.ACTION_VIEW);
1194 intent.setDataAndType(uri, RawContacts.CONTENT_ITEM_TYPE);
1195 try {
1196 context.startService(intent);
1197 } catch (Exception e) {
1198 Log.e(TAG, "Error sending message to source-app", e);
1199 }
1200 }
1201 }
1202 }
1203
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -08001204 private class AsyncPhotoLoader extends AsyncTask<String, Void, byte[]> {
1205
1206 private static final int BUFFER_SIZE = 1024*16;
1207
1208 @Override
1209 protected byte[] doInBackground(String... params) {
1210 Uri uri = Uri.parse(params[0]);
1211 byte[] data = null;
1212 try {
1213 InputStream is = getContext().getContentResolver().openInputStream(uri);
1214 if (is != null) {
1215 ByteArrayOutputStream baos = new ByteArrayOutputStream();
1216 try {
1217 byte[] mBuffer = new byte[BUFFER_SIZE];
1218
1219 int size;
1220 while ((size = is.read(mBuffer)) != -1) {
1221 baos.write(mBuffer, 0, size);
1222 }
1223 data = baos.toByteArray();
1224 } finally {
1225 is.close();
1226 }
1227 } else {
1228 Log.v(TAG, "Cannot load photo " + uri);
1229 }
1230 } catch (IOException e) {
1231 Log.e(TAG, "Cannot load photo " + uri, e);
1232 }
1233
1234 return data;
1235 }
1236
1237 @Override
1238 protected void onPostExecute(byte[] data) {
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001239 if (mContact != null) {
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -08001240 mContact = new Result(mContact);
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001241 mContact.setPhotoBinaryData(data);
1242 mContact.setLoadingPhoto(false);
1243 deliverResult(mContact);
1244 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -08001245 }
1246 }
1247
Daniel Lehmann3a120772010-06-21 16:21:35 -07001248 private void unregisterObserver() {
1249 if (mObserver != null) {
1250 getContext().getContentResolver().unregisterContentObserver(mObserver);
1251 mObserver = null;
1252 }
1253 }
1254
Daniel Lehmanncdef2b62010-06-06 18:25:49 -07001255 public ContactLoader(Context context, Uri lookupUri) {
Makoto Onuki69b4a882011-07-22 10:05:10 -07001256 this(context, lookupUri, false, false, false);
Dmitri Plotnikove843f912010-09-16 15:21:48 -07001257 }
1258
Dave Santoro39156002011-07-19 01:18:14 -07001259 public ContactLoader(Context context, Uri lookupUri, boolean loadGroupMetaData,
Makoto Onuki69b4a882011-07-22 10:05:10 -07001260 boolean loadStreamItems, boolean loadInvitableAccountTypes) {
Daniel Lehmann74a2dc52010-04-15 16:52:33 -07001261 super(context);
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001262 mLookupUri = lookupUri;
Daniel Lehmann685157e2011-08-29 21:07:01 -07001263 mRequestedUri = lookupUri;
Dmitri Plotnikove843f912010-09-16 15:21:48 -07001264 mLoadGroupMetaData = loadGroupMetaData;
Dave Santoro39156002011-07-19 01:18:14 -07001265 mLoadStreamItems = loadStreamItems;
Makoto Onuki69b4a882011-07-22 10:05:10 -07001266 mLoadInvitableAccountTypes = loadInvitableAccountTypes;
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001267 }
1268
Daniel Lehmann2a45e352012-02-13 15:03:58 -08001269 /**
1270 * Sets whether to load stream items. Will trigger a reload if the value has changed.
1271 * At the moment, this is only used for debugging purposes
1272 */
1273 public void setLoadStreamItems(boolean value) {
1274 if (mLoadStreamItems != value) {
1275 mLoadStreamItems = value;
1276 onContentChanged();
1277 }
1278 }
1279
1280 public boolean getLoadStreamItems() {
1281 return mLoadStreamItems;
1282 }
1283
Dmitri Plotnikov5a30d9a2010-11-23 14:59:50 -08001284 public Uri getLookupUri() {
1285 return mLookupUri;
1286 }
1287
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001288 @Override
Dianne Hackbornc04fc272010-12-20 23:13:10 -08001289 protected void onStartLoading() {
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001290 if (mContact != null) {
Daniel Lehmanncbcc4492010-04-12 18:03:54 -07001291 deliverResult(mContact);
Dmitri Plotnikov97e90c62011-01-03 11:58:13 -08001292 }
1293
1294 if (takeContentChanged() || mContact == null) {
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001295 forceLoad();
1296 }
1297 }
1298
1299 @Override
Dianne Hackbornc04fc272010-12-20 23:13:10 -08001300 protected void onForceLoad() {
Daniel Lehmann18f104f2010-05-07 15:41:11 -07001301 final LoadContactTask task = new LoadContactTask();
Makoto Onuki73c341f2011-08-29 09:31:44 -07001302 task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[])null);
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001303 }
1304
1305 @Override
Dianne Hackbornc04fc272010-12-20 23:13:10 -08001306 protected void onReset() {
Dianne Hackborn4ef95cc2010-12-16 00:44:33 -08001307 unregisterObserver();
1308 mContact = null;
1309 mDestroyed = true;
1310 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001311}