blob: c9fbeae1b8d62fc028a6cf785d887b3c70163c55 [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}.
Makoto Onuki3e6991e2011-07-24 14:51:20 -0700853 */
Makoto Onuki69b4a882011-07-22 10:05:10 -0700854 private void loadInvitableAccountTypes(Result contactData) {
Katherine Kuan08bcf712011-10-09 13:43:53 -0700855 Map<AccountTypeWithDataSet, AccountType> invitables =
856 AccountTypeManager.getInstance(getContext()).getUsableInvitableAccountTypes();
857 if (invitables.isEmpty()) {
Makoto Onuki69b4a882011-07-22 10:05:10 -0700858 return;
859 }
860
Katherine Kuan08bcf712011-10-09 13:43:53 -0700861 HashMap<AccountTypeWithDataSet, AccountType> result = Maps.newHashMap(invitables);
Makoto Onuki69b4a882011-07-22 10:05:10 -0700862
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -0700863 // Remove the ones that already have a raw contact in the current contact
Makoto Onuki69b4a882011-07-22 10:05:10 -0700864 for (Entity entity : contactData.getEntities()) {
Makoto Onuki6ad227f2011-08-15 13:46:59 -0700865 final ContentValues values = entity.getEntityValues();
866 final AccountTypeWithDataSet type = AccountTypeWithDataSet.get(
867 values.getAsString(RawContacts.ACCOUNT_TYPE),
868 values.getAsString(RawContacts.DATA_SET));
869 result.remove(type);
Makoto Onuki69b4a882011-07-22 10:05:10 -0700870 }
871
872 // Set to mInvitableAccountTypes
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700873 contactData.mInvitableAccountTypes.addAll(result.values());
Makoto Onuki69b4a882011-07-22 10:05:10 -0700874 }
875
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800876 /**
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700877 * Extracts Contact level columns from the cursor.
878 */
879 private Result loadContactHeaderData(final Cursor cursor, Uri contactUri) {
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700880 final String directoryParameter =
881 contactUri.getQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY);
882 final long directoryId = directoryParameter == null
883 ? Directory.DEFAULT
884 : Long.parseLong(directoryParameter);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700885 final long contactId = cursor.getLong(ContactQuery.CONTACT_ID);
886 final String lookupKey = cursor.getString(ContactQuery.LOOKUP_KEY);
887 final long nameRawContactId = cursor.getLong(ContactQuery.NAME_RAW_CONTACT_ID);
888 final int displayNameSource = cursor.getInt(ContactQuery.DISPLAY_NAME_SOURCE);
889 final String displayName = cursor.getString(ContactQuery.DISPLAY_NAME);
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700890 final String altDisplayName = cursor.getString(ContactQuery.ALT_DISPLAY_NAME);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700891 final String phoneticName = cursor.getString(ContactQuery.PHONETIC_NAME);
892 final long photoId = cursor.getLong(ContactQuery.PHOTO_ID);
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700893 final String photoUri = cursor.getString(ContactQuery.PHOTO_URI);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700894 final boolean starred = cursor.getInt(ContactQuery.STARRED) != 0;
895 final Integer presence = cursor.isNull(ContactQuery.CONTACT_PRESENCE)
896 ? null
897 : cursor.getInt(ContactQuery.CONTACT_PRESENCE);
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700898 final boolean sendToVoicemail = cursor.getInt(ContactQuery.SEND_TO_VOICEMAIL) == 1;
899 final String customRingtone = cursor.getString(ContactQuery.CUSTOM_RINGTONE);
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700900 final boolean isUserProfile = cursor.getInt(ContactQuery.IS_USER_PROFILE) == 1;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700901
Dmitri Plotnikov1536ea12010-10-29 11:51:05 -0700902 Uri lookupUri;
903 if (directoryId == Directory.DEFAULT || directoryId == Directory.LOCAL_INVISIBLE) {
904 lookupUri = ContentUris.withAppendedId(
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700905 Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey), contactId);
Dmitri Plotnikov1536ea12010-10-29 11:51:05 -0700906 } else {
907 lookupUri = contactUri;
908 }
909
Daniel Lehmann685157e2011-08-29 21:07:01 -0700910 return new Result(mRequestedUri, contactUri, lookupUri, directoryId, lookupKey,
911 contactId, nameRawContactId, displayNameSource, photoId, photoUri, displayName,
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700912 altDisplayName, phoneticName, starred, presence, sendToVoicemail,
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700913 customRingtone, isUserProfile);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700914 }
915
916 /**
917 * Extracts RawContact level columns from the cursor.
918 */
919 private ContentValues loadRawContact(Cursor cursor) {
920 ContentValues cv = new ContentValues();
921
922 cv.put(RawContacts._ID, cursor.getLong(ContactQuery.RAW_CONTACT_ID));
923
924 cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_NAME);
925 cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_TYPE);
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700926 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SET);
927 cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_TYPE_AND_DATA_SET);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700928 cursorColumnToContentValues(cursor, cv, ContactQuery.DIRTY);
929 cursorColumnToContentValues(cursor, cv, ContactQuery.VERSION);
930 cursorColumnToContentValues(cursor, cv, ContactQuery.SOURCE_ID);
931 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC1);
932 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC2);
933 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC3);
934 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC4);
935 cursorColumnToContentValues(cursor, cv, ContactQuery.DELETED);
936 cursorColumnToContentValues(cursor, cv, ContactQuery.CONTACT_ID);
937 cursorColumnToContentValues(cursor, cv, ContactQuery.STARRED);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700938 cursorColumnToContentValues(cursor, cv, ContactQuery.NAME_VERIFIED);
939
940 return cv;
941 }
942
943 /**
944 * Extracts Data level columns from the cursor.
945 */
946 private ContentValues loadData(Cursor cursor) {
947 ContentValues cv = new ContentValues();
948
949 cv.put(Data._ID, cursor.getLong(ContactQuery.DATA_ID));
950
951 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA1);
952 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA2);
953 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA3);
954 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA4);
955 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA5);
956 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA6);
957 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA7);
958 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA8);
959 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA9);
960 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA10);
961 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA11);
962 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA12);
963 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA13);
964 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA14);
965 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA15);
966 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC1);
967 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC2);
968 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC3);
969 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC4);
970 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_VERSION);
971 cursorColumnToContentValues(cursor, cv, ContactQuery.IS_PRIMARY);
972 cursorColumnToContentValues(cursor, cv, ContactQuery.IS_SUPERPRIMARY);
973 cursorColumnToContentValues(cursor, cv, ContactQuery.MIMETYPE);
974 cursorColumnToContentValues(cursor, cv, ContactQuery.RES_PACKAGE);
975 cursorColumnToContentValues(cursor, cv, ContactQuery.GROUP_SOURCE_ID);
Daniel Lehmann8fd7bb62010-08-13 20:50:31 -0700976 cursorColumnToContentValues(cursor, cv, ContactQuery.CHAT_CAPABILITY);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700977
978 return cv;
979 }
980
981 private void cursorColumnToContentValues(
982 Cursor cursor, ContentValues values, int index) {
983 switch (cursor.getType(index)) {
984 case Cursor.FIELD_TYPE_NULL:
985 // don't put anything in the content values
986 break;
987 case Cursor.FIELD_TYPE_INTEGER:
988 values.put(ContactQuery.COLUMNS[index], cursor.getLong(index));
989 break;
990 case Cursor.FIELD_TYPE_STRING:
991 values.put(ContactQuery.COLUMNS[index], cursor.getString(index));
992 break;
993 case Cursor.FIELD_TYPE_BLOB:
994 values.put(ContactQuery.COLUMNS[index], cursor.getBlob(index));
995 break;
996 default:
997 throw new IllegalStateException("Invalid or unhandled data type");
998 }
999 }
1000
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -07001001 private void loadDirectoryMetaData(Result result) {
1002 long directoryId = result.getDirectoryId();
1003
1004 Cursor cursor = getContext().getContentResolver().query(
1005 ContentUris.withAppendedId(Directory.CONTENT_URI, directoryId),
1006 DirectoryQuery.COLUMNS, null, null, null);
1007 if (cursor == null) {
1008 return;
1009 }
1010 try {
1011 if (cursor.moveToFirst()) {
1012 final String displayName = cursor.getString(DirectoryQuery.DISPLAY_NAME);
1013 final String packageName = cursor.getString(DirectoryQuery.PACKAGE_NAME);
1014 final int typeResourceId = cursor.getInt(DirectoryQuery.TYPE_RESOURCE_ID);
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -07001015 final String accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE);
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -07001016 final String accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME);
1017 final int exportSupport = cursor.getInt(DirectoryQuery.EXPORT_SUPPORT);
1018 String directoryType = null;
1019 if (!TextUtils.isEmpty(packageName)) {
1020 PackageManager pm = getContext().getPackageManager();
1021 try {
1022 Resources resources = pm.getResourcesForApplication(packageName);
1023 directoryType = resources.getString(typeResourceId);
1024 } catch (NameNotFoundException e) {
1025 Log.w(TAG, "Contact directory resource not found: "
1026 + packageName + "." + typeResourceId);
1027 }
1028 }
1029
1030 result.setDirectoryMetaData(
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -07001031 displayName, directoryType, accountType, accountName, exportSupport);
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -07001032 }
1033 } finally {
1034 cursor.close();
1035 }
1036 }
1037
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001038 /**
1039 * Loads groups meta-data for all groups associated with all constituent raw contacts'
1040 * accounts.
1041 */
1042 private void loadGroupMetaData(Result result) {
1043 StringBuilder selection = new StringBuilder();
1044 ArrayList<String> selectionArgs = new ArrayList<String>();
1045 for (Entity entity : result.mEntities) {
1046 ContentValues values = entity.getEntityValues();
1047 String accountName = values.getAsString(RawContacts.ACCOUNT_NAME);
1048 String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
Dave Santoro2b3f3c52011-07-26 17:35:42 -07001049 String dataSet = values.getAsString(RawContacts.DATA_SET);
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001050 if (accountName != null && accountType != null) {
1051 if (selection.length() != 0) {
1052 selection.append(" OR ");
1053 }
1054 selection.append(
Dave Santoro2b3f3c52011-07-26 17:35:42 -07001055 "(" + Groups.ACCOUNT_NAME + "=? AND " + Groups.ACCOUNT_TYPE + "=?");
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001056 selectionArgs.add(accountName);
1057 selectionArgs.add(accountType);
Dave Santoro2b3f3c52011-07-26 17:35:42 -07001058
1059 if (dataSet != null) {
1060 selection.append(" AND " + Groups.DATA_SET + "=?");
1061 selectionArgs.add(dataSet);
1062 } else {
1063 selection.append(" AND " + Groups.DATA_SET + " IS NULL");
1064 }
1065 selection.append(")");
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001066 }
1067 }
1068 Cursor cursor = getContext().getContentResolver().query(Groups.CONTENT_URI,
1069 GroupQuery.COLUMNS, selection.toString(), selectionArgs.toArray(new String[0]),
1070 null);
1071 try {
1072 while (cursor.moveToNext()) {
1073 final String accountName = cursor.getString(GroupQuery.ACCOUNT_NAME);
1074 final String accountType = cursor.getString(GroupQuery.ACCOUNT_TYPE);
Dave Santoro2b3f3c52011-07-26 17:35:42 -07001075 final String dataSet = cursor.getString(GroupQuery.DATA_SET);
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001076 final long groupId = cursor.getLong(GroupQuery.ID);
1077 final String title = cursor.getString(GroupQuery.TITLE);
1078 final boolean defaultGroup = cursor.isNull(GroupQuery.AUTO_ADD)
1079 ? false
1080 : cursor.getInt(GroupQuery.AUTO_ADD) != 0;
1081 final boolean favorites = cursor.isNull(GroupQuery.FAVORITES)
1082 ? false
1083 : cursor.getInt(GroupQuery.FAVORITES) != 0;
1084
Dmitri Plotnikove843f912010-09-16 15:21:48 -07001085 result.addGroupMetaData(new GroupMetaData(
Dave Santoro2b3f3c52011-07-26 17:35:42 -07001086 accountName, accountType, dataSet, groupId, title, defaultGroup,
1087 favorites));
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001088 }
1089 } finally {
1090 cursor.close();
1091 }
1092 }
1093
Dave Santoro39156002011-07-19 01:18:14 -07001094 /**
1095 * Loads all stream items and stream item photos belonging to this contact.
1096 */
1097 private void loadStreamItems(Result result) {
1098 Cursor cursor = getContext().getContentResolver().query(
1099 Contacts.CONTENT_LOOKUP_URI.buildUpon()
1100 .appendPath(result.getLookupKey())
1101 .appendPath(Contacts.StreamItems.CONTENT_DIRECTORY).build(),
1102 null, null, null, null);
1103 Map<Long, StreamItemEntry> streamItemsById = new HashMap<Long, StreamItemEntry>();
1104 ArrayList<StreamItemEntry> streamItems = new ArrayList<StreamItemEntry>();
1105 try {
1106 while (cursor.moveToNext()) {
1107 StreamItemEntry streamItem = new StreamItemEntry(cursor);
1108 streamItemsById.put(streamItem.getId(), streamItem);
1109 streamItems.add(streamItem);
1110 }
1111 } finally {
1112 cursor.close();
1113 }
1114
1115 // Now retrieve any photo records associated with the stream items.
Dave Santoro39156002011-07-19 01:18:14 -07001116 if (!streamItems.isEmpty()) {
Dave Santoroa4400d52011-09-02 16:14:53 -07001117 if (result.isUserProfile()) {
1118 // If the stream items we're loading are for the profile, we can't bulk-load the
1119 // stream items with a custom selection.
1120 for (StreamItemEntry entry : streamItems) {
1121 Cursor siCursor = getContext().getContentResolver().query(
1122 Uri.withAppendedPath(
1123 ContentUris.withAppendedId(
1124 StreamItems.CONTENT_URI, entry.getId()),
1125 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY),
1126 null, null, null, null);
1127 try {
1128 while (siCursor.moveToNext()) {
1129 entry.addPhoto(new StreamItemPhotoEntry(siCursor));
1130 }
1131 } finally {
1132 siCursor.close();
1133 }
Dave Santoro39156002011-07-19 01:18:14 -07001134 }
Dave Santoroa4400d52011-09-02 16:14:53 -07001135 } else {
1136 String[] streamItemIdArr = new String[streamItems.size()];
1137 StringBuilder streamItemPhotoSelection = new StringBuilder();
1138 streamItemPhotoSelection.append(StreamItemPhotos.STREAM_ITEM_ID + " IN (");
1139 for (int i = 0; i < streamItems.size(); i++) {
1140 if (i > 0) {
1141 streamItemPhotoSelection.append(",");
1142 }
1143 streamItemPhotoSelection.append("?");
1144 streamItemIdArr[i] = String.valueOf(streamItems.get(i).getId());
Dave Santoro39156002011-07-19 01:18:14 -07001145 }
Dave Santoroa4400d52011-09-02 16:14:53 -07001146 streamItemPhotoSelection.append(")");
1147 Cursor sipCursor = getContext().getContentResolver().query(
1148 StreamItems.CONTENT_PHOTO_URI,
1149 null, streamItemPhotoSelection.toString(), streamItemIdArr,
1150 StreamItemPhotos.STREAM_ITEM_ID);
1151 try {
1152 while (sipCursor.moveToNext()) {
1153 long streamItemId = sipCursor.getLong(
1154 sipCursor.getColumnIndex(StreamItemPhotos.STREAM_ITEM_ID));
1155 StreamItemEntry streamItem = streamItemsById.get(streamItemId);
1156 streamItem.addPhoto(new StreamItemPhotoEntry(sipCursor));
1157 }
1158 } finally {
1159 sipCursor.close();
1160 }
Dave Santoro39156002011-07-19 01:18:14 -07001161 }
1162 }
1163
1164 // Set the sorted stream items on the result.
1165 Collections.sort(streamItems);
1166 result.mStreamItems.addAll(streamItems);
1167 }
1168
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001169 @Override
1170 protected void onPostExecute(Result result) {
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001171 unregisterObserver();
1172
Daniel Lehmann1316b132010-04-13 15:08:53 -07001173 // The creator isn't interested in any further updates
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001174 if (mDestroyed || result == null) {
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001175 return;
1176 }
1177
1178 mContact = result;
Daniel Lehmann18f104f2010-05-07 15:41:11 -07001179
Makoto Onuki2621c5b2011-10-03 12:56:16 -07001180 if (result.isLoaded()) {
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001181 mLookupUri = result.getLookupUri();
1182
1183 if (!result.isDirectoryEntry()) {
1184 Log.i(TAG, "Registering content observer for " + mLookupUri);
1185 if (mObserver == null) {
1186 mObserver = new ForceLoadContentObserver();
1187 }
1188 getContext().getContentResolver().registerContentObserver(
1189 mLookupUri, true, mObserver);
Daniel Lehmann18f104f2010-05-07 15:41:11 -07001190 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -08001191
1192 if (mContact.getPhotoBinaryData() == null && mContact.getPhotoUri() != null) {
1193 mContact.setLoadingPhoto(true);
1194 new AsyncPhotoLoader().execute(mContact.getPhotoUri());
1195 }
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001196
1197 // inform the source of the data that this contact is being looked at
1198 postViewNotificationToSyncAdapter();
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001199 }
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001200
1201 deliverResult(mContact);
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001202 }
1203 }
1204
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001205 /**
1206 * Posts a message to the contributing sync adapters that have opted-in, notifying them
1207 * that the contact has just been loaded
1208 */
1209 private void postViewNotificationToSyncAdapter() {
1210 Context context = getContext();
1211 for (Entity entity : mContact.getEntities()) {
1212 final ContentValues entityValues = entity.getEntityValues();
Makoto Onukiaba2b832011-08-12 15:44:53 -07001213 final long rawContactId = entityValues.getAsLong(RawContacts.Entity._ID);
1214 if (mNotifiedRawContactIds.contains(rawContactId)) {
1215 continue; // Already notified for this raw contact.
1216 }
1217 mNotifiedRawContactIds.add(rawContactId);
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001218 final String type = entityValues.getAsString(RawContacts.ACCOUNT_TYPE);
1219 final String dataSet = entityValues.getAsString(RawContacts.DATA_SET);
Flavio Lerda59a887e2011-08-14 18:13:17 +01001220 final AccountType accountType = AccountTypeManager.getInstance(context).getAccountType(
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001221 type, dataSet);
1222 final String serviceName = accountType.getViewContactNotifyServiceClassName();
1223 final String resPackageName = accountType.resPackageName;
1224 if (!TextUtils.isEmpty(serviceName) && !TextUtils.isEmpty(resPackageName)) {
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001225 final Uri uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
1226 final Intent intent = new Intent();
1227 intent.setClassName(resPackageName, serviceName);
1228 intent.setAction(Intent.ACTION_VIEW);
1229 intent.setDataAndType(uri, RawContacts.CONTENT_ITEM_TYPE);
1230 try {
1231 context.startService(intent);
1232 } catch (Exception e) {
1233 Log.e(TAG, "Error sending message to source-app", e);
1234 }
1235 }
1236 }
1237 }
1238
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -08001239 private class AsyncPhotoLoader extends AsyncTask<String, Void, byte[]> {
1240
1241 private static final int BUFFER_SIZE = 1024*16;
1242
1243 @Override
1244 protected byte[] doInBackground(String... params) {
1245 Uri uri = Uri.parse(params[0]);
1246 byte[] data = null;
1247 try {
1248 InputStream is = getContext().getContentResolver().openInputStream(uri);
1249 if (is != null) {
1250 ByteArrayOutputStream baos = new ByteArrayOutputStream();
1251 try {
1252 byte[] mBuffer = new byte[BUFFER_SIZE];
1253
1254 int size;
1255 while ((size = is.read(mBuffer)) != -1) {
1256 baos.write(mBuffer, 0, size);
1257 }
1258 data = baos.toByteArray();
1259 } finally {
1260 is.close();
1261 }
1262 } else {
1263 Log.v(TAG, "Cannot load photo " + uri);
1264 }
1265 } catch (IOException e) {
1266 Log.e(TAG, "Cannot load photo " + uri, e);
1267 }
1268
1269 return data;
1270 }
1271
1272 @Override
1273 protected void onPostExecute(byte[] data) {
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001274 if (mContact != null) {
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -08001275 mContact = new Result(mContact);
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001276 mContact.setPhotoBinaryData(data);
1277 mContact.setLoadingPhoto(false);
1278 deliverResult(mContact);
1279 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -08001280 }
1281 }
1282
Daniel Lehmann3a120772010-06-21 16:21:35 -07001283 private void unregisterObserver() {
1284 if (mObserver != null) {
1285 getContext().getContentResolver().unregisterContentObserver(mObserver);
1286 mObserver = null;
1287 }
1288 }
1289
Daniel Lehmanncdef2b62010-06-06 18:25:49 -07001290 public ContactLoader(Context context, Uri lookupUri) {
Makoto Onuki69b4a882011-07-22 10:05:10 -07001291 this(context, lookupUri, false, false, false);
Dmitri Plotnikove843f912010-09-16 15:21:48 -07001292 }
1293
Dave Santoro39156002011-07-19 01:18:14 -07001294 public ContactLoader(Context context, Uri lookupUri, boolean loadGroupMetaData,
Makoto Onuki69b4a882011-07-22 10:05:10 -07001295 boolean loadStreamItems, boolean loadInvitableAccountTypes) {
Daniel Lehmann74a2dc52010-04-15 16:52:33 -07001296 super(context);
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001297 mLookupUri = lookupUri;
Daniel Lehmann685157e2011-08-29 21:07:01 -07001298 mRequestedUri = lookupUri;
Dmitri Plotnikove843f912010-09-16 15:21:48 -07001299 mLoadGroupMetaData = loadGroupMetaData;
Dave Santoro39156002011-07-19 01:18:14 -07001300 mLoadStreamItems = loadStreamItems;
Makoto Onuki69b4a882011-07-22 10:05:10 -07001301 mLoadInvitableAccountTypes = loadInvitableAccountTypes;
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001302 }
1303
Dmitri Plotnikov5a30d9a2010-11-23 14:59:50 -08001304 public Uri getLookupUri() {
1305 return mLookupUri;
1306 }
1307
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001308 @Override
Dianne Hackbornc04fc272010-12-20 23:13:10 -08001309 protected void onStartLoading() {
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001310 if (mContact != null) {
Daniel Lehmanncbcc4492010-04-12 18:03:54 -07001311 deliverResult(mContact);
Dmitri Plotnikov97e90c62011-01-03 11:58:13 -08001312 }
1313
1314 if (takeContentChanged() || mContact == null) {
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001315 forceLoad();
1316 }
1317 }
1318
1319 @Override
Dianne Hackbornc04fc272010-12-20 23:13:10 -08001320 protected void onForceLoad() {
Daniel Lehmann18f104f2010-05-07 15:41:11 -07001321 final LoadContactTask task = new LoadContactTask();
Makoto Onuki73c341f2011-08-29 09:31:44 -07001322 task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[])null);
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001323 }
1324
1325 @Override
Dianne Hackbornc04fc272010-12-20 23:13:10 -08001326 protected void onReset() {
Dianne Hackborn4ef95cc2010-12-16 00:44:33 -08001327 unregisterObserver();
1328 mContact = null;
1329 mDestroyed = true;
1330 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001331}