blob: 007c1e06492cedb73fd0abbaa91cf285aedacef9 [file] [log] [blame]
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001/*
Daniel Lehmannef3f8f02010-07-26 18:55:25 -07002 * Copyright (C) 2010 The Android Open Source Project
Daniel Lehmann4cd94412010-04-08 16:44:36 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
Dmitri Plotnikov18ffaa22010-12-03 14:28:00 -080017package com.android.contacts;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070018
Makoto Onuki69b4a882011-07-22 10:05:10 -070019import com.android.contacts.model.AccountType;
20import com.android.contacts.model.AccountTypeManager;
Makoto Onuki6ad227f2011-08-15 13:46:59 -070021import com.android.contacts.model.AccountTypeWithDataSet;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070022import com.android.contacts.util.DataStatus;
Dave Santoro39156002011-07-19 01:18:14 -070023import com.android.contacts.util.StreamItemEntry;
24import com.android.contacts.util.StreamItemPhotoEntry;
Makoto Onuki69b4a882011-07-22 10:05:10 -070025import com.google.android.collect.Lists;
Flavio Lerda37a26842011-06-27 11:36:52 +010026import com.google.common.annotations.VisibleForTesting;
Makoto Onuki6ad227f2011-08-15 13:46:59 -070027import com.google.common.collect.Maps;
Makoto Onukiaba2b832011-08-12 15:44:53 -070028import com.google.common.collect.Sets;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070029
30import android.content.ContentResolver;
31import android.content.ContentUris;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -070032import android.content.ContentValues;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070033import android.content.Context;
34import android.content.Entity;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -070035import android.content.Entity.NamedContentValues;
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -070036import android.content.Intent;
Jeff Hamilton3c462912010-05-15 02:20:01 -050037import android.content.Loader;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -070038import android.content.pm.PackageManager;
39import android.content.pm.PackageManager.NameNotFoundException;
Dave Santoro0a2a5db2011-06-29 00:37:06 -070040import android.content.res.AssetFileDescriptor;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -070041import android.content.res.Resources;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070042import android.database.Cursor;
43import android.net.Uri;
44import android.os.AsyncTask;
Daniel Lehmann1316b132010-04-13 15:08:53 -070045import android.provider.ContactsContract;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -070046import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -080047import android.provider.ContactsContract.CommonDataKinds.Photo;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070048import android.provider.ContactsContract.Contacts;
49import android.provider.ContactsContract.Data;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -070050import android.provider.ContactsContract.Directory;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070051import android.provider.ContactsContract.DisplayNameSources;
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -070052import android.provider.ContactsContract.Groups;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070053import android.provider.ContactsContract.RawContacts;
Dave Santoro39156002011-07-19 01:18:14 -070054import android.provider.ContactsContract.StreamItemPhotos;
Makoto Onuki69b4a882011-07-22 10:05:10 -070055import android.provider.ContactsContract.StreamItems;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -070056import android.text.TextUtils;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070057import android.util.Log;
58
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -080059import java.io.ByteArrayOutputStream;
Dave Santoro0a2a5db2011-06-29 00:37:06 -070060import java.io.FileInputStream;
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -080061import java.io.IOException;
62import java.io.InputStream;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070063import java.util.ArrayList;
Dave Santoro39156002011-07-19 01:18:14 -070064import java.util.Collections;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070065import java.util.HashMap;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -070066import java.util.List;
Dave Santoro39156002011-07-19 01:18:14 -070067import java.util.Map;
Makoto Onukiaba2b832011-08-12 15:44:53 -070068import java.util.Set;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070069
70/**
71 * Loads a single Contact and all it constituent RawContacts.
72 */
Daniel Lehmanncdef2b62010-06-06 18:25:49 -070073public class ContactLoader extends Loader<ContactLoader.Result> {
Daniel Lehmann18f104f2010-05-07 15:41:11 -070074 private static final String TAG = "ContactLoader";
75
Daniel Lehmann685157e2011-08-29 21:07:01 -070076 private final Uri mRequestedUri;
Makoto Onuki2621c5b2011-10-03 12:56:16 -070077 private Uri mLookupUri;
Dmitri Plotnikove843f912010-09-16 15:21:48 -070078 private boolean mLoadGroupMetaData;
Dave Santoro39156002011-07-19 01:18:14 -070079 private boolean mLoadStreamItems;
Makoto Onuki69b4a882011-07-22 10:05:10 -070080 private final boolean mLoadInvitableAccountTypes;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070081 private Result mContact;
82 private ForceLoadContentObserver mObserver;
83 private boolean mDestroyed;
Makoto Onukiaba2b832011-08-12 15:44:53 -070084 private final Set<Long> mNotifiedRawContactIds = Sets.newHashSet();
Dmitri Plotnikove843f912010-09-16 15:21:48 -070085
Daniel Lehmanncdef2b62010-06-06 18:25:49 -070086 public interface Listener {
Daniel Lehmann4cd94412010-04-08 16:44:36 -070087 public void onContactLoaded(Result contact);
88 }
89
90 /**
91 * The result of a load operation. Contains all data necessary to display the contact.
92 */
93 public static final class Result {
Makoto Onuki2621c5b2011-10-03 12:56:16 -070094 private enum Status {
95 /** Contact is successfully loaded */
96 LOADED,
97 /** There was an error loading the contact */
98 ERROR,
99 /** Contact is not found */
100 NOT_FOUND,
101 }
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700102
Daniel Lehmann685157e2011-08-29 21:07:01 -0700103 private final Uri mRequestedUri;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700104 private final Uri mLookupUri;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700105 private final Uri mUri;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700106 private final long mDirectoryId;
107 private final String mLookupKey;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700108 private final long mId;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700109 private final long mNameRawContactId;
110 private final int mDisplayNameSource;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700111 private final long mPhotoId;
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700112 private final String mPhotoUri;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700113 private final String mDisplayName;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700114 private final String mAltDisplayName;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700115 private final String mPhoneticName;
116 private final boolean mStarred;
117 private final Integer mPresence;
118 private final ArrayList<Entity> mEntities;
Makoto Onuki870a87e2011-08-12 13:40:31 -0700119 private final ArrayList<StreamItemEntry> mStreamItems;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700120 private final HashMap<Long, DataStatus> mStatuses;
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700121 private final ArrayList<AccountType> mInvitableAccountTypes;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700122
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700123 private String mDirectoryDisplayName;
124 private String mDirectoryType;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700125 private String mDirectoryAccountType;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700126 private String mDirectoryAccountName;
127 private int mDirectoryExportSupport;
128
Dmitri Plotnikove843f912010-09-16 15:21:48 -0700129 private ArrayList<GroupMetaData> mGroups;
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700130
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800131 private boolean mLoadingPhoto;
132 private byte[] mPhotoBinaryData;
Makoto Onuki870a87e2011-08-12 13:40:31 -0700133 private final boolean mSendToVoicemail;
134 private final String mCustomRingtone;
135 private final boolean mIsUserProfile;
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800136
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700137 private final Status mStatus;
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700138 private final Exception mException;
139
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700140 /**
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700141 * Constructor for special results, namely "no contact found" and "error".
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700142 */
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700143 private Result(Uri requestedUri, Status status, Exception exception) {
144 if (status == Status.ERROR && exception == null) {
145 throw new IllegalArgumentException("ERROR result must have exception");
146 }
147 mStatus = status;
148 mException = exception;
149 mRequestedUri = requestedUri;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700150 mLookupUri = null;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700151 mUri = null;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700152 mDirectoryId = -1;
153 mLookupKey = null;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700154 mId = -1;
155 mEntities = null;
Dave Santoro39156002011-07-19 01:18:14 -0700156 mStreamItems = new ArrayList<StreamItemEntry>();
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700157 mStatuses = null;
158 mNameRawContactId = -1;
159 mDisplayNameSource = DisplayNameSources.UNDEFINED;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700160 mPhotoId = -1;
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700161 mPhotoUri = null;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700162 mDisplayName = null;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700163 mAltDisplayName = null;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700164 mPhoneticName = null;
165 mStarred = false;
166 mPresence = null;
Makoto Onuki69b4a882011-07-22 10:05:10 -0700167 mInvitableAccountTypes = null;
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700168 mSendToVoicemail = false;
169 mCustomRingtone = null;
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700170 mIsUserProfile = false;
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700171 }
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700172
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700173 private static Result forError(Uri requestedUri, Exception exception) {
174 return new Result(requestedUri, Status.ERROR, exception);
175 }
176
177 private static Result forNotFound(Uri requestedUri) {
178 return new Result(requestedUri, Status.NOT_FOUND, null);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700179 }
180
181 /**
182 * Constructor to call when contact was found
183 */
Daniel Lehmann685157e2011-08-29 21:07:01 -0700184 private Result(Uri requestedUri, Uri uri, Uri lookupUri, long directoryId, String lookupKey,
185 long id, long nameRawContactId, int displayNameSource, long photoId,
186 String photoUri, String displayName, String altDisplayName, String phoneticName,
187 boolean starred, Integer presence, boolean sendToVoicemail, String customRingtone,
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700188 boolean isUserProfile) {
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700189 mStatus = Status.LOADED;
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700190 mException = null;
Daniel Lehmann685157e2011-08-29 21:07:01 -0700191 mRequestedUri = requestedUri;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700192 mLookupUri = lookupUri;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700193 mUri = uri;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700194 mDirectoryId = directoryId;
195 mLookupKey = lookupKey;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700196 mId = id;
197 mEntities = new ArrayList<Entity>();
Dave Santoro39156002011-07-19 01:18:14 -0700198 mStreamItems = new ArrayList<StreamItemEntry>();
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700199 mStatuses = new HashMap<Long, DataStatus>();
200 mNameRawContactId = nameRawContactId;
201 mDisplayNameSource = displayNameSource;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700202 mPhotoId = photoId;
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700203 mPhotoUri = photoUri;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700204 mDisplayName = displayName;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700205 mAltDisplayName = altDisplayName;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700206 mPhoneticName = phoneticName;
207 mStarred = starred;
208 mPresence = presence;
Makoto Onuki69b4a882011-07-22 10:05:10 -0700209 mInvitableAccountTypes = Lists.newArrayList();
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700210 mSendToVoicemail = sendToVoicemail;
211 mCustomRingtone = customRingtone;
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700212 mIsUserProfile = isUserProfile;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700213 }
214
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800215 private Result(Result from) {
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700216 mStatus = from.mStatus;
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700217 mException = from.mException;
Daniel Lehmann685157e2011-08-29 21:07:01 -0700218 mRequestedUri = from.mRequestedUri;
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800219 mLookupUri = from.mLookupUri;
220 mUri = from.mUri;
221 mDirectoryId = from.mDirectoryId;
222 mLookupKey = from.mLookupKey;
223 mId = from.mId;
224 mNameRawContactId = from.mNameRawContactId;
225 mDisplayNameSource = from.mDisplayNameSource;
226 mPhotoId = from.mPhotoId;
227 mPhotoUri = from.mPhotoUri;
228 mDisplayName = from.mDisplayName;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700229 mAltDisplayName = from.mAltDisplayName;
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800230 mPhoneticName = from.mPhoneticName;
231 mStarred = from.mStarred;
232 mPresence = from.mPresence;
233 mEntities = from.mEntities;
Dave Santoro39156002011-07-19 01:18:14 -0700234 mStreamItems = from.mStreamItems;
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800235 mStatuses = from.mStatuses;
Makoto Onuki69b4a882011-07-22 10:05:10 -0700236 mInvitableAccountTypes = from.mInvitableAccountTypes;
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800237
238 mDirectoryDisplayName = from.mDirectoryDisplayName;
239 mDirectoryType = from.mDirectoryType;
240 mDirectoryAccountType = from.mDirectoryAccountType;
241 mDirectoryAccountName = from.mDirectoryAccountName;
242 mDirectoryExportSupport = from.mDirectoryExportSupport;
243
244 mGroups = from.mGroups;
245
246 mLoadingPhoto = from.mLoadingPhoto;
247 mPhotoBinaryData = from.mPhotoBinaryData;
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700248 mSendToVoicemail = from.mSendToVoicemail;
249 mCustomRingtone = from.mCustomRingtone;
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700250 mIsUserProfile = from.mIsUserProfile;
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800251 }
252
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700253 /**
254 * @param exportSupport See {@link Directory#EXPORT_SUPPORT}.
255 */
Daniel Lehmann1ad4d1b2010-10-18 19:20:41 -0700256 private void setDirectoryMetaData(String displayName, String directoryType,
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700257 String accountType, String accountName, int exportSupport) {
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700258 mDirectoryDisplayName = displayName;
259 mDirectoryType = directoryType;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700260 mDirectoryAccountType = accountType;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700261 mDirectoryAccountName = accountName;
262 mDirectoryExportSupport = exportSupport;
263 }
264
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800265 private void setLoadingPhoto(boolean flag) {
266 mLoadingPhoto = flag;
267 }
268
269 private void setPhotoBinaryData(byte[] photoBinaryData) {
270 mPhotoBinaryData = photoBinaryData;
271 }
272
Daniel Lehmann685157e2011-08-29 21:07:01 -0700273 /**
274 * Returns the URI for the contact that contains both the lookup key and the ID. This is
275 * the best URI to reference a contact.
276 * For directory contacts, this is the same a the URI as returned by {@link #getUri()}
277 */
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700278 public Uri getLookupUri() {
279 return mLookupUri;
280 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800281
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700282 public String getLookupKey() {
283 return mLookupKey;
284 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800285
Daniel Lehmann685157e2011-08-29 21:07:01 -0700286 /**
287 * Returns the contact Uri that was passed to the provider to make the query. This is
288 * the same as the requested Uri, unless the requested Uri doesn't specify a Contact:
289 * If it either references a Raw-Contact or a Person (a pre-Eclair style Uri), this Uri will
290 * always reference the full aggregate contact.
291 */
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700292 public Uri getUri() {
293 return mUri;
294 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800295
Daniel Lehmann685157e2011-08-29 21:07:01 -0700296 /**
297 * Returns the URI for which this {@link ContactLoader) was initially requested.
298 */
299 public Uri getRequestedUri() {
300 return mRequestedUri;
301 }
302
Flavio Lerda37a26842011-06-27 11:36:52 +0100303 @VisibleForTesting
304 /*package*/ long getId() {
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700305 return mId;
306 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800307
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700308 /**
309 * @return true when an exception happened during loading, in which case
310 * {@link #getException} returns the actual exception object.
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700311 * Note {@link #isNotFound()} and {@link #isError()} are mutually exclusive; If
312 * {@link #isError()} is {@code true}, {@link #isNotFound()} is always {@code false},
313 * and vice versa.
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700314 */
315 public boolean isError() {
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700316 return mStatus == Status.ERROR;
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700317 }
318
319 public Exception getException() {
320 return mException;
321 }
322
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700323 /**
324 * @return true when the specified contact is not found.
325 * Note {@link #isNotFound()} and {@link #isError()} are mutually exclusive; If
326 * {@link #isError()} is {@code true}, {@link #isNotFound()} is always {@code false},
327 * and vice versa.
328 */
329 public boolean isNotFound() {
330 return mStatus == Status.NOT_FOUND;
331 }
332
333 /**
334 * @return true if the specified contact is successfully loaded.
335 * i.e. neither {@link #isError()} nor {@link #isNotFound()}.
336 */
337 public boolean isLoaded() {
338 return mStatus == Status.LOADED;
339 }
340
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700341 public long getNameRawContactId() {
342 return mNameRawContactId;
343 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800344
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700345 public int getDisplayNameSource() {
346 return mDisplayNameSource;
347 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800348
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700349 public long getPhotoId() {
350 return mPhotoId;
351 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800352
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700353 public String getPhotoUri() {
354 return mPhotoUri;
355 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800356
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700357 public String getDisplayName() {
358 return mDisplayName;
359 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800360
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700361 public String getAltDisplayName() {
362 return mAltDisplayName;
363 }
364
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700365 public String getPhoneticName() {
366 return mPhoneticName;
367 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800368
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700369 public boolean getStarred() {
370 return mStarred;
371 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800372
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700373 public Integer getPresence() {
374 return mPresence;
375 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800376
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700377 public ArrayList<AccountType> getInvitableAccountTypes() {
Makoto Onuki69b4a882011-07-22 10:05:10 -0700378 return mInvitableAccountTypes;
379 }
380
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700381 public ArrayList<Entity> getEntities() {
382 return mEntities;
383 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800384
Dave Santoro39156002011-07-19 01:18:14 -0700385 public ArrayList<StreamItemEntry> getStreamItems() {
386 return mStreamItems;
387 }
388
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700389 public HashMap<Long, DataStatus> getStatuses() {
390 return mStatuses;
391 }
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700392
393 public long getDirectoryId() {
394 return mDirectoryId;
395 }
396
397 public boolean isDirectoryEntry() {
Dmitri Plotnikov5f72c1f2010-09-01 21:21:04 -0700398 return mDirectoryId != -1 && mDirectoryId != Directory.DEFAULT
399 && mDirectoryId != Directory.LOCAL_INVISIBLE;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700400 }
401
402 public int getDirectoryExportSupport() {
403 return mDirectoryExportSupport;
404 }
405
406 public String getDirectoryDisplayName() {
407 return mDirectoryDisplayName;
408 }
409
410 public String getDirectoryType() {
411 return mDirectoryType;
412 }
413
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700414 public String getDirectoryAccountType() {
415 return mDirectoryAccountType;
416 }
417
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700418 public String getDirectoryAccountName() {
419 return mDirectoryAccountName;
420 }
421
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800422 public boolean isLoadingPhoto() {
423 return mLoadingPhoto;
424 }
425
426 public byte[] getPhotoBinaryData() {
427 return mPhotoBinaryData;
428 }
429
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700430 public ArrayList<ContentValues> getContentValues() {
431 if (mEntities.size() != 1) {
432 throw new IllegalStateException(
433 "Cannot extract content values from an aggregated contact");
434 }
435
436 Entity entity = mEntities.get(0);
437 ArrayList<ContentValues> result = new ArrayList<ContentValues>();
438 ArrayList<NamedContentValues> subValues = entity.getSubValues();
439 if (subValues != null) {
440 int size = subValues.size();
441 for (int i = 0; i < size; i++) {
442 NamedContentValues pair = subValues.get(i);
443 if (Data.CONTENT_URI.equals(pair.uri)) {
444 result.add(pair.values);
445 }
446 }
447 }
Dmitri Plotnikov40ec3a82010-11-10 11:25:33 -0800448
449 // If the photo was loaded using the URI, create an entry for the photo
450 // binary data.
451 if (mPhotoId == 0 && mPhotoBinaryData != null) {
452 ContentValues photo = new ContentValues();
453 photo.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
454 photo.put(Photo.PHOTO, mPhotoBinaryData);
455 result.add(photo);
456 }
457
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700458 return result;
459 }
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700460
Daniel Lehmann1ad4d1b2010-10-18 19:20:41 -0700461 private void addGroupMetaData(GroupMetaData group) {
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700462 if (mGroups == null) {
Dmitri Plotnikove843f912010-09-16 15:21:48 -0700463 mGroups = new ArrayList<GroupMetaData>();
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700464 }
465 mGroups.add(group);
466 }
467
Dmitri Plotnikove843f912010-09-16 15:21:48 -0700468 public List<GroupMetaData> getGroupMetaData() {
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700469 return mGroups;
470 }
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700471
472 public boolean isSendToVoicemail() {
473 return mSendToVoicemail;
474 }
475
476 public String getCustomRingtone() {
477 return mCustomRingtone;
478 }
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700479
480 public boolean isUserProfile() {
481 return mIsUserProfile;
482 }
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700483 }
484
Dave Santoro39156002011-07-19 01:18:14 -0700485 /**
486 * Projection used for the query that loads all data for the entire contact (except for
487 * social stream items).
488 */
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700489 private static class ContactQuery {
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700490 final static String[] COLUMNS = new String[] {
491 Contacts.NAME_RAW_CONTACT_ID,
492 Contacts.DISPLAY_NAME_SOURCE,
493 Contacts.LOOKUP_KEY,
494 Contacts.DISPLAY_NAME,
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700495 Contacts.DISPLAY_NAME_ALTERNATIVE,
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700496 Contacts.PHONETIC_NAME,
497 Contacts.PHOTO_ID,
498 Contacts.STARRED,
499 Contacts.CONTACT_PRESENCE,
500 Contacts.CONTACT_STATUS,
501 Contacts.CONTACT_STATUS_TIMESTAMP,
502 Contacts.CONTACT_STATUS_RES_PACKAGE,
503 Contacts.CONTACT_STATUS_LABEL,
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700504 Contacts.Entity.CONTACT_ID,
505 Contacts.Entity.RAW_CONTACT_ID,
506
507 RawContacts.ACCOUNT_NAME,
508 RawContacts.ACCOUNT_TYPE,
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700509 RawContacts.DATA_SET,
510 RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700511 RawContacts.DIRTY,
512 RawContacts.VERSION,
513 RawContacts.SOURCE_ID,
514 RawContacts.SYNC1,
515 RawContacts.SYNC2,
516 RawContacts.SYNC3,
517 RawContacts.SYNC4,
518 RawContacts.DELETED,
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700519 RawContacts.NAME_VERIFIED,
520
521 Contacts.Entity.DATA_ID,
522 Data.DATA1,
523 Data.DATA2,
524 Data.DATA3,
525 Data.DATA4,
526 Data.DATA5,
527 Data.DATA6,
528 Data.DATA7,
529 Data.DATA8,
530 Data.DATA9,
531 Data.DATA10,
532 Data.DATA11,
533 Data.DATA12,
534 Data.DATA13,
535 Data.DATA14,
536 Data.DATA15,
537 Data.SYNC1,
538 Data.SYNC2,
539 Data.SYNC3,
540 Data.SYNC4,
541 Data.DATA_VERSION,
542 Data.IS_PRIMARY,
543 Data.IS_SUPER_PRIMARY,
544 Data.MIMETYPE,
545 Data.RES_PACKAGE,
546
547 GroupMembership.GROUP_SOURCE_ID,
548
549 Data.PRESENCE,
Daniel Lehmann8fd7bb62010-08-13 20:50:31 -0700550 Data.CHAT_CAPABILITY,
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700551 Data.STATUS,
552 Data.STATUS_RES_PACKAGE,
553 Data.STATUS_ICON,
554 Data.STATUS_LABEL,
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700555 Data.STATUS_TIMESTAMP,
556
557 Contacts.PHOTO_URI,
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700558 Contacts.SEND_TO_VOICEMAIL,
559 Contacts.CUSTOM_RINGTONE,
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700560 Contacts.IS_USER_PROFILE,
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700561 };
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700562
563 public final static int NAME_RAW_CONTACT_ID = 0;
564 public final static int DISPLAY_NAME_SOURCE = 1;
565 public final static int LOOKUP_KEY = 2;
566 public final static int DISPLAY_NAME = 3;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700567 public final static int ALT_DISPLAY_NAME = 4;
568 public final static int PHONETIC_NAME = 5;
569 public final static int PHOTO_ID = 6;
570 public final static int STARRED = 7;
571 public final static int CONTACT_PRESENCE = 8;
572 public final static int CONTACT_STATUS = 9;
573 public final static int CONTACT_STATUS_TIMESTAMP = 10;
574 public final static int CONTACT_STATUS_RES_PACKAGE = 11;
575 public final static int CONTACT_STATUS_LABEL = 12;
576 public final static int CONTACT_ID = 13;
577 public final static int RAW_CONTACT_ID = 14;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700578
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700579 public final static int ACCOUNT_NAME = 15;
580 public final static int ACCOUNT_TYPE = 16;
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700581 public final static int DATA_SET = 17;
582 public final static int ACCOUNT_TYPE_AND_DATA_SET = 18;
583 public final static int DIRTY = 19;
584 public final static int VERSION = 20;
585 public final static int SOURCE_ID = 21;
586 public final static int SYNC1 = 22;
587 public final static int SYNC2 = 23;
588 public final static int SYNC3 = 24;
589 public final static int SYNC4 = 25;
590 public final static int DELETED = 26;
591 public final static int NAME_VERIFIED = 27;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700592
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700593 public final static int DATA_ID = 28;
594 public final static int DATA1 = 29;
595 public final static int DATA2 = 30;
596 public final static int DATA3 = 31;
597 public final static int DATA4 = 32;
598 public final static int DATA5 = 33;
599 public final static int DATA6 = 34;
600 public final static int DATA7 = 35;
601 public final static int DATA8 = 36;
602 public final static int DATA9 = 37;
603 public final static int DATA10 = 38;
604 public final static int DATA11 = 39;
605 public final static int DATA12 = 40;
606 public final static int DATA13 = 41;
607 public final static int DATA14 = 42;
608 public final static int DATA15 = 43;
609 public final static int DATA_SYNC1 = 44;
610 public final static int DATA_SYNC2 = 45;
611 public final static int DATA_SYNC3 = 46;
612 public final static int DATA_SYNC4 = 47;
613 public final static int DATA_VERSION = 48;
614 public final static int IS_PRIMARY = 49;
615 public final static int IS_SUPERPRIMARY = 50;
616 public final static int MIMETYPE = 51;
617 public final static int RES_PACKAGE = 52;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700618
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700619 public final static int GROUP_SOURCE_ID = 53;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700620
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700621 public final static int PRESENCE = 54;
622 public final static int CHAT_CAPABILITY = 55;
623 public final static int STATUS = 56;
624 public final static int STATUS_RES_PACKAGE = 57;
625 public final static int STATUS_ICON = 58;
626 public final static int STATUS_LABEL = 59;
627 public final static int STATUS_TIMESTAMP = 60;
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700628
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700629 public final static int PHOTO_URI = 61;
630 public final static int SEND_TO_VOICEMAIL = 62;
631 public final static int CUSTOM_RINGTONE = 63;
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700632 public final static int IS_USER_PROFILE = 64;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700633 }
Daniel Lehmann1316b132010-04-13 15:08:53 -0700634
Dave Santoro39156002011-07-19 01:18:14 -0700635 /**
636 * Projection used for the query that loads all data for the entire contact.
637 */
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700638 private static class DirectoryQuery {
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700639 final static String[] COLUMNS = new String[] {
640 Directory.DISPLAY_NAME,
641 Directory.PACKAGE_NAME,
642 Directory.TYPE_RESOURCE_ID,
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700643 Directory.ACCOUNT_TYPE,
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700644 Directory.ACCOUNT_NAME,
645 Directory.EXPORT_SUPPORT,
646 };
647
648 public final static int DISPLAY_NAME = 0;
649 public final static int PACKAGE_NAME = 1;
650 public final static int TYPE_RESOURCE_ID = 2;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700651 public final static int ACCOUNT_TYPE = 3;
652 public final static int ACCOUNT_NAME = 4;
653 public final static int EXPORT_SUPPORT = 5;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700654 }
655
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700656 private static class GroupQuery {
657 final static String[] COLUMNS = new String[] {
658 Groups.ACCOUNT_NAME,
659 Groups.ACCOUNT_TYPE,
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700660 Groups.DATA_SET,
661 Groups.ACCOUNT_TYPE_AND_DATA_SET,
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700662 Groups._ID,
663 Groups.TITLE,
664 Groups.AUTO_ADD,
665 Groups.FAVORITES,
666 };
667
668 public final static int ACCOUNT_NAME = 0;
669 public final static int ACCOUNT_TYPE = 1;
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700670 public final static int DATA_SET = 2;
671 public final static int ACCOUNT_TYPE_AND_DATA_SET = 3;
672 public final static int ID = 4;
673 public final static int TITLE = 5;
674 public final static int AUTO_ADD = 6;
675 public final static int FAVORITES = 7;
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700676 }
677
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700678 private final class LoadContactTask extends AsyncTask<Void, Void, Result> {
Daniel Lehmann1316b132010-04-13 15:08:53 -0700679
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700680 @Override
681 protected Result doInBackground(Void... args) {
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700682 try {
683 final ContentResolver resolver = getContext().getContentResolver();
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700684 final Uri uriCurrentFormat = ensureIsContactUri(resolver, mLookupUri);
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700685 Result result = loadContactEntity(resolver, uriCurrentFormat);
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700686 if (!result.isNotFound()) {
Dmitri Plotnikov217245c2010-09-18 13:04:50 -0700687 if (result.isDirectoryEntry()) {
688 loadDirectoryMetaData(result);
689 } else if (mLoadGroupMetaData) {
690 loadGroupMetaData(result);
691 }
Dave Santoro39156002011-07-19 01:18:14 -0700692 if (mLoadStreamItems) {
693 loadStreamItems(result);
694 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800695 loadPhotoBinaryData(result);
Makoto Onuki870a87e2011-08-12 13:40:31 -0700696
697 // Note ME profile should never have "Add connection"
698 if (mLoadInvitableAccountTypes && !result.isUserProfile()) {
Makoto Onuki69b4a882011-07-22 10:05:10 -0700699 loadInvitableAccountTypes(result);
700 }
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700701 }
702 return result;
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700703 } catch (Exception e) {
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700704 Log.e(TAG, "Error loading the contact: " + mLookupUri, e);
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700705 return Result.forError(mRequestedUri, e);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700706 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700707 }
708
709 /**
Daniel Lehmann1316b132010-04-13 15:08:53 -0700710 * Transforms the given Uri and returns a Lookup-Uri that represents the contact.
711 * For legacy contacts, a raw-contact lookup is performed.
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700712 * @param resolver
Daniel Lehmann1316b132010-04-13 15:08:53 -0700713 */
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700714 private Uri ensureIsContactUri(final ContentResolver resolver, final Uri uri) {
Daniel Lehmann1316b132010-04-13 15:08:53 -0700715 if (uri == null) throw new IllegalArgumentException("uri must not be null");
716
717 final String authority = uri.getAuthority();
718
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700719 // Current Style Uri?
Daniel Lehmann1316b132010-04-13 15:08:53 -0700720 if (ContactsContract.AUTHORITY.equals(authority)) {
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700721 final String type = resolver.getType(uri);
722 // Contact-Uri? Good, return it
723 if (Contacts.CONTENT_ITEM_TYPE.equals(type)) {
724 return uri;
725 }
726
727 // RawContact-Uri? Transform it to ContactUri
728 if (RawContacts.CONTENT_ITEM_TYPE.equals(type)) {
729 final long rawContactId = ContentUris.parseId(uri);
730 return RawContacts.getContactLookupUri(getContext().getContentResolver(),
731 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId));
732 }
733
734 // Anything else? We don't know what this is
735 throw new IllegalArgumentException("uri format is unknown");
Daniel Lehmann1316b132010-04-13 15:08:53 -0700736 }
737
738 // Legacy Style? Convert to RawContact
739 final String OBSOLETE_AUTHORITY = "contacts";
740 if (OBSOLETE_AUTHORITY.equals(authority)) {
741 // Legacy Format. Convert to RawContact-Uri and then lookup the contact
742 final long rawContactId = ContentUris.parseId(uri);
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700743 return RawContacts.getContactLookupUri(resolver,
Daniel Lehmann1316b132010-04-13 15:08:53 -0700744 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId));
745 }
746
Daniel Lehmanncdef2b62010-06-06 18:25:49 -0700747 throw new IllegalArgumentException("uri authority is unknown");
Daniel Lehmann1316b132010-04-13 15:08:53 -0700748 }
749
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700750 private Result loadContactEntity(ContentResolver resolver, Uri contactUri) {
751 Uri entityUri = Uri.withAppendedPath(contactUri, Contacts.Entity.CONTENT_DIRECTORY);
752 Cursor cursor = resolver.query(entityUri, ContactQuery.COLUMNS, null, null,
753 Contacts.Entity.RAW_CONTACT_ID);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700754 if (cursor == null) {
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700755 Log.e(TAG, "No cursor returned in loadContactEntity");
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700756 return Result.forNotFound(mRequestedUri);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700757 }
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700758
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700759 try {
760 if (!cursor.moveToFirst()) {
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700761 cursor.close();
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700762 return Result.forNotFound(mRequestedUri);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700763 }
764
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700765 long currentRawContactId = -1;
766 Entity entity = null;
767 Result result = loadContactHeaderData(cursor, contactUri);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700768 ArrayList<Entity> entities = result.getEntities();
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700769 HashMap<Long, DataStatus> statuses = result.getStatuses();
770 for (; !cursor.isAfterLast(); cursor.moveToNext()) {
771 long rawContactId = cursor.getLong(ContactQuery.RAW_CONTACT_ID);
772 if (rawContactId != currentRawContactId) {
773 currentRawContactId = rawContactId;
774 entity = new android.content.Entity(loadRawContact(cursor));
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700775 entities.add(entity);
776 }
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700777 if (!cursor.isNull(ContactQuery.DATA_ID)) {
778 ContentValues data = loadData(cursor);
779 entity.addSubValue(ContactsContract.Data.CONTENT_URI, data);
780
781 if (!cursor.isNull(ContactQuery.PRESENCE)
782 || !cursor.isNull(ContactQuery.STATUS)) {
783 final DataStatus status = new DataStatus(cursor);
784 final long dataId = cursor.getLong(ContactQuery.DATA_ID);
785 statuses.put(dataId, status);
786 }
787 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700788 }
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700789
790 return result;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700791 } finally {
792 cursor.close();
793 }
794 }
795
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700796 /**
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800797 * Looks for the photo data item in entities. If found, creates a new Bitmap instance. If
798 * not found, returns null
799 */
800 private void loadPhotoBinaryData(Result contactData) {
Dave Santoro0a2a5db2011-06-29 00:37:06 -0700801
802 // If we have a photo URI, try loading that first.
803 String photoUri = contactData.getPhotoUri();
804 if (photoUri != null) {
805 try {
806 AssetFileDescriptor fd = getContext().getContentResolver()
807 .openAssetFileDescriptor(Uri.parse(photoUri), "r");
808 byte[] buffer = new byte[16 * 1024];
809 FileInputStream fis = fd.createInputStream();
810 ByteArrayOutputStream baos = new ByteArrayOutputStream();
811 try {
812 int size;
813 while ((size = fis.read(buffer)) != -1) {
814 baos.write(buffer, 0, size);
815 }
816 contactData.setPhotoBinaryData(baos.toByteArray());
817 } finally {
818 fis.close();
819 fd.close();
820 }
821 return;
822 } catch (IOException ioe) {
823 // Just fall back to the case below.
824 }
825 }
826
827 // If we couldn't load from a file, fall back to the data blob.
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800828 final long photoId = contactData.getPhotoId();
829 if (photoId <= 0) {
830 // No photo ID
831 return;
832 }
833
834 for (Entity entity : contactData.getEntities()) {
835 for (NamedContentValues subValue : entity.getSubValues()) {
836 final ContentValues entryValues = subValue.values;
837 final long dataId = entryValues.getAsLong(Data._ID);
838 if (dataId == photoId) {
839 final String mimeType = entryValues.getAsString(Data.MIMETYPE);
840 // Correct Data Id but incorrect MimeType? Don't load
841 if (!Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
842 return;
843 }
844 contactData.setPhotoBinaryData(entryValues.getAsByteArray(Photo.PHOTO));
845 break;
846 }
847 }
848 }
849 }
850
Makoto Onuki3e6991e2011-07-24 14:51:20 -0700851 /**
852 * Sets the "invitable" account types to {@link Result#mInvitableAccountTypes}.
853 *
854 * TODO Exclude the ones with no raw contacts in the database.
855 */
Makoto Onuki69b4a882011-07-22 10:05:10 -0700856 private void loadInvitableAccountTypes(Result contactData) {
Makoto Onuki6ad227f2011-08-15 13:46:59 -0700857 Map<AccountTypeWithDataSet, AccountType> allInvitables =
Makoto Onuki69b4a882011-07-22 10:05:10 -0700858 AccountTypeManager.getInstance(getContext()).getInvitableAccountTypes();
859 if (allInvitables.isEmpty()) {
860 return;
861 }
862
Makoto Onuki6ad227f2011-08-15 13:46:59 -0700863 HashMap<AccountTypeWithDataSet, AccountType> result = Maps.newHashMap(allInvitables);
Makoto Onuki69b4a882011-07-22 10:05:10 -0700864
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -0700865 // Remove the ones that already have a raw contact in the current contact
Makoto Onuki69b4a882011-07-22 10:05:10 -0700866 for (Entity entity : contactData.getEntities()) {
Makoto Onuki6ad227f2011-08-15 13:46:59 -0700867 final ContentValues values = entity.getEntityValues();
868 final AccountTypeWithDataSet type = AccountTypeWithDataSet.get(
869 values.getAsString(RawContacts.ACCOUNT_TYPE),
870 values.getAsString(RawContacts.DATA_SET));
871 result.remove(type);
Makoto Onuki69b4a882011-07-22 10:05:10 -0700872 }
873
874 // Set to mInvitableAccountTypes
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700875 contactData.mInvitableAccountTypes.addAll(result.values());
Makoto Onuki69b4a882011-07-22 10:05:10 -0700876 }
877
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800878 /**
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700879 * Extracts Contact level columns from the cursor.
880 */
881 private Result loadContactHeaderData(final Cursor cursor, Uri contactUri) {
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700882 final String directoryParameter =
883 contactUri.getQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY);
884 final long directoryId = directoryParameter == null
885 ? Directory.DEFAULT
886 : Long.parseLong(directoryParameter);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700887 final long contactId = cursor.getLong(ContactQuery.CONTACT_ID);
888 final String lookupKey = cursor.getString(ContactQuery.LOOKUP_KEY);
889 final long nameRawContactId = cursor.getLong(ContactQuery.NAME_RAW_CONTACT_ID);
890 final int displayNameSource = cursor.getInt(ContactQuery.DISPLAY_NAME_SOURCE);
891 final String displayName = cursor.getString(ContactQuery.DISPLAY_NAME);
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700892 final String altDisplayName = cursor.getString(ContactQuery.ALT_DISPLAY_NAME);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700893 final String phoneticName = cursor.getString(ContactQuery.PHONETIC_NAME);
894 final long photoId = cursor.getLong(ContactQuery.PHOTO_ID);
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700895 final String photoUri = cursor.getString(ContactQuery.PHOTO_URI);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700896 final boolean starred = cursor.getInt(ContactQuery.STARRED) != 0;
897 final Integer presence = cursor.isNull(ContactQuery.CONTACT_PRESENCE)
898 ? null
899 : cursor.getInt(ContactQuery.CONTACT_PRESENCE);
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700900 final boolean sendToVoicemail = cursor.getInt(ContactQuery.SEND_TO_VOICEMAIL) == 1;
901 final String customRingtone = cursor.getString(ContactQuery.CUSTOM_RINGTONE);
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700902 final boolean isUserProfile = cursor.getInt(ContactQuery.IS_USER_PROFILE) == 1;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700903
Dmitri Plotnikov1536ea12010-10-29 11:51:05 -0700904 Uri lookupUri;
905 if (directoryId == Directory.DEFAULT || directoryId == Directory.LOCAL_INVISIBLE) {
906 lookupUri = ContentUris.withAppendedId(
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700907 Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey), contactId);
Dmitri Plotnikov1536ea12010-10-29 11:51:05 -0700908 } else {
909 lookupUri = contactUri;
910 }
911
Daniel Lehmann685157e2011-08-29 21:07:01 -0700912 return new Result(mRequestedUri, contactUri, lookupUri, directoryId, lookupKey,
913 contactId, nameRawContactId, displayNameSource, photoId, photoUri, displayName,
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700914 altDisplayName, phoneticName, starred, presence, sendToVoicemail,
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700915 customRingtone, isUserProfile);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700916 }
917
918 /**
919 * Extracts RawContact level columns from the cursor.
920 */
921 private ContentValues loadRawContact(Cursor cursor) {
922 ContentValues cv = new ContentValues();
923
924 cv.put(RawContacts._ID, cursor.getLong(ContactQuery.RAW_CONTACT_ID));
925
926 cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_NAME);
927 cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_TYPE);
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700928 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SET);
929 cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_TYPE_AND_DATA_SET);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700930 cursorColumnToContentValues(cursor, cv, ContactQuery.DIRTY);
931 cursorColumnToContentValues(cursor, cv, ContactQuery.VERSION);
932 cursorColumnToContentValues(cursor, cv, ContactQuery.SOURCE_ID);
933 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC1);
934 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC2);
935 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC3);
936 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC4);
937 cursorColumnToContentValues(cursor, cv, ContactQuery.DELETED);
938 cursorColumnToContentValues(cursor, cv, ContactQuery.CONTACT_ID);
939 cursorColumnToContentValues(cursor, cv, ContactQuery.STARRED);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700940 cursorColumnToContentValues(cursor, cv, ContactQuery.NAME_VERIFIED);
941
942 return cv;
943 }
944
945 /**
946 * Extracts Data level columns from the cursor.
947 */
948 private ContentValues loadData(Cursor cursor) {
949 ContentValues cv = new ContentValues();
950
951 cv.put(Data._ID, cursor.getLong(ContactQuery.DATA_ID));
952
953 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA1);
954 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA2);
955 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA3);
956 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA4);
957 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA5);
958 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA6);
959 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA7);
960 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA8);
961 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA9);
962 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA10);
963 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA11);
964 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA12);
965 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA13);
966 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA14);
967 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA15);
968 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC1);
969 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC2);
970 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC3);
971 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC4);
972 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_VERSION);
973 cursorColumnToContentValues(cursor, cv, ContactQuery.IS_PRIMARY);
974 cursorColumnToContentValues(cursor, cv, ContactQuery.IS_SUPERPRIMARY);
975 cursorColumnToContentValues(cursor, cv, ContactQuery.MIMETYPE);
976 cursorColumnToContentValues(cursor, cv, ContactQuery.RES_PACKAGE);
977 cursorColumnToContentValues(cursor, cv, ContactQuery.GROUP_SOURCE_ID);
Daniel Lehmann8fd7bb62010-08-13 20:50:31 -0700978 cursorColumnToContentValues(cursor, cv, ContactQuery.CHAT_CAPABILITY);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700979
980 return cv;
981 }
982
983 private void cursorColumnToContentValues(
984 Cursor cursor, ContentValues values, int index) {
985 switch (cursor.getType(index)) {
986 case Cursor.FIELD_TYPE_NULL:
987 // don't put anything in the content values
988 break;
989 case Cursor.FIELD_TYPE_INTEGER:
990 values.put(ContactQuery.COLUMNS[index], cursor.getLong(index));
991 break;
992 case Cursor.FIELD_TYPE_STRING:
993 values.put(ContactQuery.COLUMNS[index], cursor.getString(index));
994 break;
995 case Cursor.FIELD_TYPE_BLOB:
996 values.put(ContactQuery.COLUMNS[index], cursor.getBlob(index));
997 break;
998 default:
999 throw new IllegalStateException("Invalid or unhandled data type");
1000 }
1001 }
1002
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -07001003 private void loadDirectoryMetaData(Result result) {
1004 long directoryId = result.getDirectoryId();
1005
1006 Cursor cursor = getContext().getContentResolver().query(
1007 ContentUris.withAppendedId(Directory.CONTENT_URI, directoryId),
1008 DirectoryQuery.COLUMNS, null, null, null);
1009 if (cursor == null) {
1010 return;
1011 }
1012 try {
1013 if (cursor.moveToFirst()) {
1014 final String displayName = cursor.getString(DirectoryQuery.DISPLAY_NAME);
1015 final String packageName = cursor.getString(DirectoryQuery.PACKAGE_NAME);
1016 final int typeResourceId = cursor.getInt(DirectoryQuery.TYPE_RESOURCE_ID);
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -07001017 final String accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE);
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -07001018 final String accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME);
1019 final int exportSupport = cursor.getInt(DirectoryQuery.EXPORT_SUPPORT);
1020 String directoryType = null;
1021 if (!TextUtils.isEmpty(packageName)) {
1022 PackageManager pm = getContext().getPackageManager();
1023 try {
1024 Resources resources = pm.getResourcesForApplication(packageName);
1025 directoryType = resources.getString(typeResourceId);
1026 } catch (NameNotFoundException e) {
1027 Log.w(TAG, "Contact directory resource not found: "
1028 + packageName + "." + typeResourceId);
1029 }
1030 }
1031
1032 result.setDirectoryMetaData(
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -07001033 displayName, directoryType, accountType, accountName, exportSupport);
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -07001034 }
1035 } finally {
1036 cursor.close();
1037 }
1038 }
1039
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001040 /**
1041 * Loads groups meta-data for all groups associated with all constituent raw contacts'
1042 * accounts.
1043 */
1044 private void loadGroupMetaData(Result result) {
1045 StringBuilder selection = new StringBuilder();
1046 ArrayList<String> selectionArgs = new ArrayList<String>();
1047 for (Entity entity : result.mEntities) {
1048 ContentValues values = entity.getEntityValues();
1049 String accountName = values.getAsString(RawContacts.ACCOUNT_NAME);
1050 String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
Dave Santoro2b3f3c52011-07-26 17:35:42 -07001051 String dataSet = values.getAsString(RawContacts.DATA_SET);
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001052 if (accountName != null && accountType != null) {
1053 if (selection.length() != 0) {
1054 selection.append(" OR ");
1055 }
1056 selection.append(
Dave Santoro2b3f3c52011-07-26 17:35:42 -07001057 "(" + Groups.ACCOUNT_NAME + "=? AND " + Groups.ACCOUNT_TYPE + "=?");
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001058 selectionArgs.add(accountName);
1059 selectionArgs.add(accountType);
Dave Santoro2b3f3c52011-07-26 17:35:42 -07001060
1061 if (dataSet != null) {
1062 selection.append(" AND " + Groups.DATA_SET + "=?");
1063 selectionArgs.add(dataSet);
1064 } else {
1065 selection.append(" AND " + Groups.DATA_SET + " IS NULL");
1066 }
1067 selection.append(")");
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001068 }
1069 }
1070 Cursor cursor = getContext().getContentResolver().query(Groups.CONTENT_URI,
1071 GroupQuery.COLUMNS, selection.toString(), selectionArgs.toArray(new String[0]),
1072 null);
1073 try {
1074 while (cursor.moveToNext()) {
1075 final String accountName = cursor.getString(GroupQuery.ACCOUNT_NAME);
1076 final String accountType = cursor.getString(GroupQuery.ACCOUNT_TYPE);
Dave Santoro2b3f3c52011-07-26 17:35:42 -07001077 final String dataSet = cursor.getString(GroupQuery.DATA_SET);
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001078 final long groupId = cursor.getLong(GroupQuery.ID);
1079 final String title = cursor.getString(GroupQuery.TITLE);
1080 final boolean defaultGroup = cursor.isNull(GroupQuery.AUTO_ADD)
1081 ? false
1082 : cursor.getInt(GroupQuery.AUTO_ADD) != 0;
1083 final boolean favorites = cursor.isNull(GroupQuery.FAVORITES)
1084 ? false
1085 : cursor.getInt(GroupQuery.FAVORITES) != 0;
1086
Dmitri Plotnikove843f912010-09-16 15:21:48 -07001087 result.addGroupMetaData(new GroupMetaData(
Dave Santoro2b3f3c52011-07-26 17:35:42 -07001088 accountName, accountType, dataSet, groupId, title, defaultGroup,
1089 favorites));
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001090 }
1091 } finally {
1092 cursor.close();
1093 }
1094 }
1095
Dave Santoro39156002011-07-19 01:18:14 -07001096 /**
1097 * Loads all stream items and stream item photos belonging to this contact.
1098 */
1099 private void loadStreamItems(Result result) {
1100 Cursor cursor = getContext().getContentResolver().query(
1101 Contacts.CONTENT_LOOKUP_URI.buildUpon()
1102 .appendPath(result.getLookupKey())
1103 .appendPath(Contacts.StreamItems.CONTENT_DIRECTORY).build(),
1104 null, null, null, null);
1105 Map<Long, StreamItemEntry> streamItemsById = new HashMap<Long, StreamItemEntry>();
1106 ArrayList<StreamItemEntry> streamItems = new ArrayList<StreamItemEntry>();
1107 try {
1108 while (cursor.moveToNext()) {
1109 StreamItemEntry streamItem = new StreamItemEntry(cursor);
1110 streamItemsById.put(streamItem.getId(), streamItem);
1111 streamItems.add(streamItem);
1112 }
1113 } finally {
1114 cursor.close();
1115 }
1116
1117 // Now retrieve any photo records associated with the stream items.
Dave Santoro39156002011-07-19 01:18:14 -07001118 if (!streamItems.isEmpty()) {
Dave Santoroa4400d52011-09-02 16:14:53 -07001119 if (result.isUserProfile()) {
1120 // If the stream items we're loading are for the profile, we can't bulk-load the
1121 // stream items with a custom selection.
1122 for (StreamItemEntry entry : streamItems) {
1123 Cursor siCursor = getContext().getContentResolver().query(
1124 Uri.withAppendedPath(
1125 ContentUris.withAppendedId(
1126 StreamItems.CONTENT_URI, entry.getId()),
1127 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY),
1128 null, null, null, null);
1129 try {
1130 while (siCursor.moveToNext()) {
1131 entry.addPhoto(new StreamItemPhotoEntry(siCursor));
1132 }
1133 } finally {
1134 siCursor.close();
1135 }
Dave Santoro39156002011-07-19 01:18:14 -07001136 }
Dave Santoroa4400d52011-09-02 16:14:53 -07001137 } else {
1138 String[] streamItemIdArr = new String[streamItems.size()];
1139 StringBuilder streamItemPhotoSelection = new StringBuilder();
1140 streamItemPhotoSelection.append(StreamItemPhotos.STREAM_ITEM_ID + " IN (");
1141 for (int i = 0; i < streamItems.size(); i++) {
1142 if (i > 0) {
1143 streamItemPhotoSelection.append(",");
1144 }
1145 streamItemPhotoSelection.append("?");
1146 streamItemIdArr[i] = String.valueOf(streamItems.get(i).getId());
Dave Santoro39156002011-07-19 01:18:14 -07001147 }
Dave Santoroa4400d52011-09-02 16:14:53 -07001148 streamItemPhotoSelection.append(")");
1149 Cursor sipCursor = getContext().getContentResolver().query(
1150 StreamItems.CONTENT_PHOTO_URI,
1151 null, streamItemPhotoSelection.toString(), streamItemIdArr,
1152 StreamItemPhotos.STREAM_ITEM_ID);
1153 try {
1154 while (sipCursor.moveToNext()) {
1155 long streamItemId = sipCursor.getLong(
1156 sipCursor.getColumnIndex(StreamItemPhotos.STREAM_ITEM_ID));
1157 StreamItemEntry streamItem = streamItemsById.get(streamItemId);
1158 streamItem.addPhoto(new StreamItemPhotoEntry(sipCursor));
1159 }
1160 } finally {
1161 sipCursor.close();
1162 }
Dave Santoro39156002011-07-19 01:18:14 -07001163 }
1164 }
1165
1166 // Set the sorted stream items on the result.
1167 Collections.sort(streamItems);
1168 result.mStreamItems.addAll(streamItems);
1169 }
1170
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001171 @Override
1172 protected void onPostExecute(Result result) {
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001173 unregisterObserver();
1174
Daniel Lehmann1316b132010-04-13 15:08:53 -07001175 // The creator isn't interested in any further updates
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001176 if (mDestroyed || result == null) {
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001177 return;
1178 }
1179
1180 mContact = result;
Daniel Lehmann18f104f2010-05-07 15:41:11 -07001181
Makoto Onuki2621c5b2011-10-03 12:56:16 -07001182 if (result.isLoaded()) {
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001183 mLookupUri = result.getLookupUri();
1184
1185 if (!result.isDirectoryEntry()) {
1186 Log.i(TAG, "Registering content observer for " + mLookupUri);
1187 if (mObserver == null) {
1188 mObserver = new ForceLoadContentObserver();
1189 }
1190 getContext().getContentResolver().registerContentObserver(
1191 mLookupUri, true, mObserver);
Daniel Lehmann18f104f2010-05-07 15:41:11 -07001192 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -08001193
1194 if (mContact.getPhotoBinaryData() == null && mContact.getPhotoUri() != null) {
1195 mContact.setLoadingPhoto(true);
1196 new AsyncPhotoLoader().execute(mContact.getPhotoUri());
1197 }
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001198
1199 // inform the source of the data that this contact is being looked at
1200 postViewNotificationToSyncAdapter();
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001201 }
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001202
1203 deliverResult(mContact);
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001204 }
1205 }
1206
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001207 /**
1208 * Posts a message to the contributing sync adapters that have opted-in, notifying them
1209 * that the contact has just been loaded
1210 */
1211 private void postViewNotificationToSyncAdapter() {
1212 Context context = getContext();
1213 for (Entity entity : mContact.getEntities()) {
1214 final ContentValues entityValues = entity.getEntityValues();
Makoto Onukiaba2b832011-08-12 15:44:53 -07001215 final long rawContactId = entityValues.getAsLong(RawContacts.Entity._ID);
1216 if (mNotifiedRawContactIds.contains(rawContactId)) {
1217 continue; // Already notified for this raw contact.
1218 }
1219 mNotifiedRawContactIds.add(rawContactId);
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001220 final String type = entityValues.getAsString(RawContacts.ACCOUNT_TYPE);
1221 final String dataSet = entityValues.getAsString(RawContacts.DATA_SET);
Flavio Lerda59a887e2011-08-14 18:13:17 +01001222 final AccountType accountType = AccountTypeManager.getInstance(context).getAccountType(
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001223 type, dataSet);
1224 final String serviceName = accountType.getViewContactNotifyServiceClassName();
1225 final String resPackageName = accountType.resPackageName;
1226 if (!TextUtils.isEmpty(serviceName) && !TextUtils.isEmpty(resPackageName)) {
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001227 final Uri uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
1228 final Intent intent = new Intent();
1229 intent.setClassName(resPackageName, serviceName);
1230 intent.setAction(Intent.ACTION_VIEW);
1231 intent.setDataAndType(uri, RawContacts.CONTENT_ITEM_TYPE);
1232 try {
1233 context.startService(intent);
1234 } catch (Exception e) {
1235 Log.e(TAG, "Error sending message to source-app", e);
1236 }
1237 }
1238 }
1239 }
1240
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -08001241 private class AsyncPhotoLoader extends AsyncTask<String, Void, byte[]> {
1242
1243 private static final int BUFFER_SIZE = 1024*16;
1244
1245 @Override
1246 protected byte[] doInBackground(String... params) {
1247 Uri uri = Uri.parse(params[0]);
1248 byte[] data = null;
1249 try {
1250 InputStream is = getContext().getContentResolver().openInputStream(uri);
1251 if (is != null) {
1252 ByteArrayOutputStream baos = new ByteArrayOutputStream();
1253 try {
1254 byte[] mBuffer = new byte[BUFFER_SIZE];
1255
1256 int size;
1257 while ((size = is.read(mBuffer)) != -1) {
1258 baos.write(mBuffer, 0, size);
1259 }
1260 data = baos.toByteArray();
1261 } finally {
1262 is.close();
1263 }
1264 } else {
1265 Log.v(TAG, "Cannot load photo " + uri);
1266 }
1267 } catch (IOException e) {
1268 Log.e(TAG, "Cannot load photo " + uri, e);
1269 }
1270
1271 return data;
1272 }
1273
1274 @Override
1275 protected void onPostExecute(byte[] data) {
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001276 if (mContact != null) {
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -08001277 mContact = new Result(mContact);
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001278 mContact.setPhotoBinaryData(data);
1279 mContact.setLoadingPhoto(false);
1280 deliverResult(mContact);
1281 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -08001282 }
1283 }
1284
Daniel Lehmann3a120772010-06-21 16:21:35 -07001285 private void unregisterObserver() {
1286 if (mObserver != null) {
1287 getContext().getContentResolver().unregisterContentObserver(mObserver);
1288 mObserver = null;
1289 }
1290 }
1291
Daniel Lehmanncdef2b62010-06-06 18:25:49 -07001292 public ContactLoader(Context context, Uri lookupUri) {
Makoto Onuki69b4a882011-07-22 10:05:10 -07001293 this(context, lookupUri, false, false, false);
Dmitri Plotnikove843f912010-09-16 15:21:48 -07001294 }
1295
Dave Santoro39156002011-07-19 01:18:14 -07001296 public ContactLoader(Context context, Uri lookupUri, boolean loadGroupMetaData,
Makoto Onuki69b4a882011-07-22 10:05:10 -07001297 boolean loadStreamItems, boolean loadInvitableAccountTypes) {
Daniel Lehmann74a2dc52010-04-15 16:52:33 -07001298 super(context);
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001299 mLookupUri = lookupUri;
Daniel Lehmann685157e2011-08-29 21:07:01 -07001300 mRequestedUri = lookupUri;
Dmitri Plotnikove843f912010-09-16 15:21:48 -07001301 mLoadGroupMetaData = loadGroupMetaData;
Dave Santoro39156002011-07-19 01:18:14 -07001302 mLoadStreamItems = loadStreamItems;
Makoto Onuki69b4a882011-07-22 10:05:10 -07001303 mLoadInvitableAccountTypes = loadInvitableAccountTypes;
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001304 }
1305
Dmitri Plotnikov5a30d9a2010-11-23 14:59:50 -08001306 public Uri getLookupUri() {
1307 return mLookupUri;
1308 }
1309
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001310 @Override
Dianne Hackbornc04fc272010-12-20 23:13:10 -08001311 protected void onStartLoading() {
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001312 if (mContact != null) {
Daniel Lehmanncbcc4492010-04-12 18:03:54 -07001313 deliverResult(mContact);
Dmitri Plotnikov97e90c62011-01-03 11:58:13 -08001314 }
1315
1316 if (takeContentChanged() || mContact == null) {
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001317 forceLoad();
1318 }
1319 }
1320
1321 @Override
Dianne Hackbornc04fc272010-12-20 23:13:10 -08001322 protected void onForceLoad() {
Daniel Lehmann18f104f2010-05-07 15:41:11 -07001323 final LoadContactTask task = new LoadContactTask();
Makoto Onuki73c341f2011-08-29 09:31:44 -07001324 task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[])null);
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001325 }
1326
1327 @Override
Dianne Hackbornc04fc272010-12-20 23:13:10 -08001328 protected void onReset() {
Dianne Hackborn4ef95cc2010-12-16 00:44:33 -08001329 unregisterObserver();
1330 mContact = null;
1331 mDestroyed = true;
1332 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001333}