blob: 195eb78445bba6fb37c7fc8a7fc8ad4780350d11 [file] [log] [blame]
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001/*
Daniel Lehmannef3f8f02010-07-26 18:55:25 -07002 * Copyright (C) 2010 The Android Open Source Project
Daniel Lehmann4cd94412010-04-08 16:44:36 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
Dmitri Plotnikov18ffaa22010-12-03 14:28:00 -080017package com.android.contacts;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070018
Makoto Onuki69b4a882011-07-22 10:05:10 -070019import com.android.contacts.model.AccountType;
20import com.android.contacts.model.AccountTypeManager;
Makoto Onuki6ad227f2011-08-15 13:46:59 -070021import com.android.contacts.model.AccountTypeWithDataSet;
Katherine Kuan6cd5b0a2011-09-16 11:46:01 -070022import com.android.contacts.util.ContactLoaderUtils;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070023import com.android.contacts.util.DataStatus;
Dave Santoro39156002011-07-19 01:18:14 -070024import com.android.contacts.util.StreamItemEntry;
25import com.android.contacts.util.StreamItemPhotoEntry;
Makoto Onuki69b4a882011-07-22 10:05:10 -070026import com.google.android.collect.Lists;
Flavio Lerda37a26842011-06-27 11:36:52 +010027import com.google.common.annotations.VisibleForTesting;
Makoto Onuki6ad227f2011-08-15 13:46:59 -070028import com.google.common.collect.Maps;
Makoto Onukiaba2b832011-08-12 15:44:53 -070029import com.google.common.collect.Sets;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070030
31import android.content.ContentResolver;
32import android.content.ContentUris;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -070033import android.content.ContentValues;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070034import android.content.Context;
35import android.content.Entity;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -070036import android.content.Entity.NamedContentValues;
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -070037import android.content.Intent;
Jeff Hamilton3c462912010-05-15 02:20:01 -050038import android.content.Loader;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -070039import android.content.pm.PackageManager;
40import android.content.pm.PackageManager.NameNotFoundException;
Dave Santoro0a2a5db2011-06-29 00:37:06 -070041import android.content.res.AssetFileDescriptor;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -070042import android.content.res.Resources;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070043import android.database.Cursor;
44import android.net.Uri;
45import android.os.AsyncTask;
Daniel Lehmann1316b132010-04-13 15:08:53 -070046import android.provider.ContactsContract;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -070047import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -080048import android.provider.ContactsContract.CommonDataKinds.Photo;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070049import android.provider.ContactsContract.Contacts;
50import android.provider.ContactsContract.Data;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -070051import android.provider.ContactsContract.Directory;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070052import android.provider.ContactsContract.DisplayNameSources;
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -070053import android.provider.ContactsContract.Groups;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070054import android.provider.ContactsContract.RawContacts;
Dave Santoro39156002011-07-19 01:18:14 -070055import android.provider.ContactsContract.StreamItemPhotos;
Makoto Onuki69b4a882011-07-22 10:05:10 -070056import android.provider.ContactsContract.StreamItems;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -070057import android.text.TextUtils;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070058import android.util.Log;
59
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -080060import java.io.ByteArrayOutputStream;
Dave Santoro0a2a5db2011-06-29 00:37:06 -070061import java.io.FileInputStream;
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -080062import java.io.IOException;
63import java.io.InputStream;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070064import java.util.ArrayList;
Dave Santoro39156002011-07-19 01:18:14 -070065import java.util.Collections;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070066import java.util.HashMap;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -070067import java.util.List;
Dave Santoro39156002011-07-19 01:18:14 -070068import java.util.Map;
Makoto Onukiaba2b832011-08-12 15:44:53 -070069import java.util.Set;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070070
71/**
72 * Loads a single Contact and all it constituent RawContacts.
73 */
Daniel Lehmanncdef2b62010-06-06 18:25:49 -070074public class ContactLoader extends Loader<ContactLoader.Result> {
Daniel Lehmann18f104f2010-05-07 15:41:11 -070075 private static final String TAG = "ContactLoader";
76
Makoto Onukida9cdc12012-02-27 16:11:50 -080077 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
78
Daniel Lehmann685157e2011-08-29 21:07:01 -070079 private final Uri mRequestedUri;
Makoto Onuki2621c5b2011-10-03 12:56:16 -070080 private Uri mLookupUri;
Dmitri Plotnikove843f912010-09-16 15:21:48 -070081 private boolean mLoadGroupMetaData;
Dave Santoro39156002011-07-19 01:18:14 -070082 private boolean mLoadStreamItems;
Makoto Onuki69b4a882011-07-22 10:05:10 -070083 private final boolean mLoadInvitableAccountTypes;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070084 private Result mContact;
85 private ForceLoadContentObserver mObserver;
86 private boolean mDestroyed;
Makoto Onukiaba2b832011-08-12 15:44:53 -070087 private final Set<Long> mNotifiedRawContactIds = Sets.newHashSet();
Dmitri Plotnikove843f912010-09-16 15:21:48 -070088
Daniel Lehmanncdef2b62010-06-06 18:25:49 -070089 public interface Listener {
Daniel Lehmann4cd94412010-04-08 16:44:36 -070090 public void onContactLoaded(Result contact);
91 }
92
93 /**
94 * The result of a load operation. Contains all data necessary to display the contact.
95 */
96 public static final class Result {
Makoto Onuki2621c5b2011-10-03 12:56:16 -070097 private enum Status {
98 /** Contact is successfully loaded */
99 LOADED,
100 /** There was an error loading the contact */
101 ERROR,
102 /** Contact is not found */
103 NOT_FOUND,
104 }
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700105
Daniel Lehmann685157e2011-08-29 21:07:01 -0700106 private final Uri mRequestedUri;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700107 private final Uri mLookupUri;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700108 private final Uri mUri;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700109 private final long mDirectoryId;
110 private final String mLookupKey;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700111 private final long mId;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700112 private final long mNameRawContactId;
113 private final int mDisplayNameSource;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700114 private final long mPhotoId;
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700115 private final String mPhotoUri;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700116 private final String mDisplayName;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700117 private final String mAltDisplayName;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700118 private final String mPhoneticName;
119 private final boolean mStarred;
120 private final Integer mPresence;
121 private final ArrayList<Entity> mEntities;
Makoto Onuki870a87e2011-08-12 13:40:31 -0700122 private final ArrayList<StreamItemEntry> mStreamItems;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700123 private final HashMap<Long, DataStatus> mStatuses;
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700124 private final ArrayList<AccountType> mInvitableAccountTypes;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700125
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700126 private String mDirectoryDisplayName;
127 private String mDirectoryType;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700128 private String mDirectoryAccountType;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700129 private String mDirectoryAccountName;
130 private int mDirectoryExportSupport;
131
Dmitri Plotnikove843f912010-09-16 15:21:48 -0700132 private ArrayList<GroupMetaData> mGroups;
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700133
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800134 private boolean mLoadingPhoto;
135 private byte[] mPhotoBinaryData;
Makoto Onuki870a87e2011-08-12 13:40:31 -0700136 private final boolean mSendToVoicemail;
137 private final String mCustomRingtone;
138 private final boolean mIsUserProfile;
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800139
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700140 private final Status mStatus;
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700141 private final Exception mException;
142
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700143 /**
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700144 * Constructor for special results, namely "no contact found" and "error".
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700145 */
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700146 private Result(Uri requestedUri, Status status, Exception exception) {
147 if (status == Status.ERROR && exception == null) {
148 throw new IllegalArgumentException("ERROR result must have exception");
149 }
150 mStatus = status;
151 mException = exception;
152 mRequestedUri = requestedUri;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700153 mLookupUri = null;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700154 mUri = null;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700155 mDirectoryId = -1;
156 mLookupKey = null;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700157 mId = -1;
158 mEntities = null;
Dave Santoro39156002011-07-19 01:18:14 -0700159 mStreamItems = new ArrayList<StreamItemEntry>();
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700160 mStatuses = null;
161 mNameRawContactId = -1;
162 mDisplayNameSource = DisplayNameSources.UNDEFINED;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700163 mPhotoId = -1;
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700164 mPhotoUri = null;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700165 mDisplayName = null;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700166 mAltDisplayName = null;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700167 mPhoneticName = null;
168 mStarred = false;
169 mPresence = null;
Makoto Onuki69b4a882011-07-22 10:05:10 -0700170 mInvitableAccountTypes = null;
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700171 mSendToVoicemail = false;
172 mCustomRingtone = null;
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700173 mIsUserProfile = false;
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700174 }
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700175
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700176 private static Result forError(Uri requestedUri, Exception exception) {
177 return new Result(requestedUri, Status.ERROR, exception);
178 }
179
180 private static Result forNotFound(Uri requestedUri) {
181 return new Result(requestedUri, Status.NOT_FOUND, null);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700182 }
183
184 /**
185 * Constructor to call when contact was found
186 */
Daniel Lehmann685157e2011-08-29 21:07:01 -0700187 private Result(Uri requestedUri, Uri uri, Uri lookupUri, long directoryId, String lookupKey,
188 long id, long nameRawContactId, int displayNameSource, long photoId,
189 String photoUri, String displayName, String altDisplayName, String phoneticName,
190 boolean starred, Integer presence, boolean sendToVoicemail, String customRingtone,
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700191 boolean isUserProfile) {
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700192 mStatus = Status.LOADED;
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700193 mException = null;
Daniel Lehmann685157e2011-08-29 21:07:01 -0700194 mRequestedUri = requestedUri;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700195 mLookupUri = lookupUri;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700196 mUri = uri;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700197 mDirectoryId = directoryId;
198 mLookupKey = lookupKey;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700199 mId = id;
200 mEntities = new ArrayList<Entity>();
Dave Santoro39156002011-07-19 01:18:14 -0700201 mStreamItems = new ArrayList<StreamItemEntry>();
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700202 mStatuses = new HashMap<Long, DataStatus>();
203 mNameRawContactId = nameRawContactId;
204 mDisplayNameSource = displayNameSource;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700205 mPhotoId = photoId;
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700206 mPhotoUri = photoUri;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700207 mDisplayName = displayName;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700208 mAltDisplayName = altDisplayName;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700209 mPhoneticName = phoneticName;
210 mStarred = starred;
211 mPresence = presence;
Makoto Onuki69b4a882011-07-22 10:05:10 -0700212 mInvitableAccountTypes = Lists.newArrayList();
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700213 mSendToVoicemail = sendToVoicemail;
214 mCustomRingtone = customRingtone;
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700215 mIsUserProfile = isUserProfile;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700216 }
217
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800218 private Result(Result from) {
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700219 mStatus = from.mStatus;
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700220 mException = from.mException;
Daniel Lehmann685157e2011-08-29 21:07:01 -0700221 mRequestedUri = from.mRequestedUri;
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800222 mLookupUri = from.mLookupUri;
223 mUri = from.mUri;
224 mDirectoryId = from.mDirectoryId;
225 mLookupKey = from.mLookupKey;
226 mId = from.mId;
227 mNameRawContactId = from.mNameRawContactId;
228 mDisplayNameSource = from.mDisplayNameSource;
229 mPhotoId = from.mPhotoId;
230 mPhotoUri = from.mPhotoUri;
231 mDisplayName = from.mDisplayName;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700232 mAltDisplayName = from.mAltDisplayName;
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800233 mPhoneticName = from.mPhoneticName;
234 mStarred = from.mStarred;
235 mPresence = from.mPresence;
236 mEntities = from.mEntities;
Dave Santoro39156002011-07-19 01:18:14 -0700237 mStreamItems = from.mStreamItems;
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800238 mStatuses = from.mStatuses;
Makoto Onuki69b4a882011-07-22 10:05:10 -0700239 mInvitableAccountTypes = from.mInvitableAccountTypes;
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800240
241 mDirectoryDisplayName = from.mDirectoryDisplayName;
242 mDirectoryType = from.mDirectoryType;
243 mDirectoryAccountType = from.mDirectoryAccountType;
244 mDirectoryAccountName = from.mDirectoryAccountName;
245 mDirectoryExportSupport = from.mDirectoryExportSupport;
246
247 mGroups = from.mGroups;
248
249 mLoadingPhoto = from.mLoadingPhoto;
250 mPhotoBinaryData = from.mPhotoBinaryData;
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700251 mSendToVoicemail = from.mSendToVoicemail;
252 mCustomRingtone = from.mCustomRingtone;
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700253 mIsUserProfile = from.mIsUserProfile;
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800254 }
255
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700256 /**
257 * @param exportSupport See {@link Directory#EXPORT_SUPPORT}.
258 */
Daniel Lehmann1ad4d1b2010-10-18 19:20:41 -0700259 private void setDirectoryMetaData(String displayName, String directoryType,
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700260 String accountType, String accountName, int exportSupport) {
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700261 mDirectoryDisplayName = displayName;
262 mDirectoryType = directoryType;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700263 mDirectoryAccountType = accountType;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700264 mDirectoryAccountName = accountName;
265 mDirectoryExportSupport = exportSupport;
266 }
267
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800268 private void setLoadingPhoto(boolean flag) {
269 mLoadingPhoto = flag;
270 }
271
272 private void setPhotoBinaryData(byte[] photoBinaryData) {
273 mPhotoBinaryData = photoBinaryData;
274 }
275
Daniel Lehmann685157e2011-08-29 21:07:01 -0700276 /**
277 * Returns the URI for the contact that contains both the lookup key and the ID. This is
278 * the best URI to reference a contact.
279 * For directory contacts, this is the same a the URI as returned by {@link #getUri()}
280 */
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700281 public Uri getLookupUri() {
282 return mLookupUri;
283 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800284
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700285 public String getLookupKey() {
286 return mLookupKey;
287 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800288
Daniel Lehmann685157e2011-08-29 21:07:01 -0700289 /**
290 * Returns the contact Uri that was passed to the provider to make the query. This is
291 * the same as the requested Uri, unless the requested Uri doesn't specify a Contact:
292 * If it either references a Raw-Contact or a Person (a pre-Eclair style Uri), this Uri will
293 * always reference the full aggregate contact.
294 */
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700295 public Uri getUri() {
296 return mUri;
297 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800298
Daniel Lehmann685157e2011-08-29 21:07:01 -0700299 /**
300 * Returns the URI for which this {@link ContactLoader) was initially requested.
301 */
302 public Uri getRequestedUri() {
303 return mRequestedUri;
304 }
305
Dave Santoro6fa73842011-09-28 14:37:06 -0700306 /**
307 * Returns the contact ID.
308 */
Makoto Onuki98306102011-11-28 15:16:58 -0800309 @VisibleForTesting
310 /* package */ long getId() {
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700311 return mId;
312 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800313
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700314 /**
315 * @return true when an exception happened during loading, in which case
316 * {@link #getException} returns the actual exception object.
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700317 * Note {@link #isNotFound()} and {@link #isError()} are mutually exclusive; If
318 * {@link #isError()} is {@code true}, {@link #isNotFound()} is always {@code false},
319 * and vice versa.
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700320 */
321 public boolean isError() {
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700322 return mStatus == Status.ERROR;
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700323 }
324
325 public Exception getException() {
326 return mException;
327 }
328
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700329 /**
330 * @return true when the specified contact is not found.
331 * Note {@link #isNotFound()} and {@link #isError()} are mutually exclusive; If
332 * {@link #isError()} is {@code true}, {@link #isNotFound()} is always {@code false},
333 * and vice versa.
334 */
335 public boolean isNotFound() {
336 return mStatus == Status.NOT_FOUND;
337 }
338
339 /**
340 * @return true if the specified contact is successfully loaded.
341 * i.e. neither {@link #isError()} nor {@link #isNotFound()}.
342 */
343 public boolean isLoaded() {
344 return mStatus == Status.LOADED;
345 }
346
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700347 public long getNameRawContactId() {
348 return mNameRawContactId;
349 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800350
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700351 public int getDisplayNameSource() {
352 return mDisplayNameSource;
353 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800354
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700355 public long getPhotoId() {
356 return mPhotoId;
357 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800358
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700359 public String getPhotoUri() {
360 return mPhotoUri;
361 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800362
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700363 public String getDisplayName() {
364 return mDisplayName;
365 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800366
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700367 public String getAltDisplayName() {
368 return mAltDisplayName;
369 }
370
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700371 public String getPhoneticName() {
372 return mPhoneticName;
373 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800374
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700375 public boolean getStarred() {
376 return mStarred;
377 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800378
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700379 public Integer getPresence() {
380 return mPresence;
381 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800382
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700383 public ArrayList<AccountType> getInvitableAccountTypes() {
Makoto Onuki69b4a882011-07-22 10:05:10 -0700384 return mInvitableAccountTypes;
385 }
386
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700387 public ArrayList<Entity> getEntities() {
388 return mEntities;
389 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800390
Dave Santoro39156002011-07-19 01:18:14 -0700391 public ArrayList<StreamItemEntry> getStreamItems() {
392 return mStreamItems;
393 }
394
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700395 public HashMap<Long, DataStatus> getStatuses() {
396 return mStatuses;
397 }
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700398
399 public long getDirectoryId() {
400 return mDirectoryId;
401 }
402
403 public boolean isDirectoryEntry() {
Dmitri Plotnikov5f72c1f2010-09-01 21:21:04 -0700404 return mDirectoryId != -1 && mDirectoryId != Directory.DEFAULT
405 && mDirectoryId != Directory.LOCAL_INVISIBLE;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700406 }
407
408 public int getDirectoryExportSupport() {
409 return mDirectoryExportSupport;
410 }
411
412 public String getDirectoryDisplayName() {
413 return mDirectoryDisplayName;
414 }
415
416 public String getDirectoryType() {
417 return mDirectoryType;
418 }
419
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700420 public String getDirectoryAccountType() {
421 return mDirectoryAccountType;
422 }
423
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700424 public String getDirectoryAccountName() {
425 return mDirectoryAccountName;
426 }
427
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800428 public boolean isLoadingPhoto() {
429 return mLoadingPhoto;
430 }
431
432 public byte[] getPhotoBinaryData() {
433 return mPhotoBinaryData;
434 }
435
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700436 public ArrayList<ContentValues> getContentValues() {
437 if (mEntities.size() != 1) {
438 throw new IllegalStateException(
439 "Cannot extract content values from an aggregated contact");
440 }
441
442 Entity entity = mEntities.get(0);
443 ArrayList<ContentValues> result = new ArrayList<ContentValues>();
444 ArrayList<NamedContentValues> subValues = entity.getSubValues();
445 if (subValues != null) {
446 int size = subValues.size();
447 for (int i = 0; i < size; i++) {
448 NamedContentValues pair = subValues.get(i);
449 if (Data.CONTENT_URI.equals(pair.uri)) {
450 result.add(pair.values);
451 }
452 }
453 }
Dmitri Plotnikov40ec3a82010-11-10 11:25:33 -0800454
455 // If the photo was loaded using the URI, create an entry for the photo
456 // binary data.
457 if (mPhotoId == 0 && mPhotoBinaryData != null) {
458 ContentValues photo = new ContentValues();
459 photo.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
460 photo.put(Photo.PHOTO, mPhotoBinaryData);
461 result.add(photo);
462 }
463
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700464 return result;
465 }
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700466
Daniel Lehmann1ad4d1b2010-10-18 19:20:41 -0700467 private void addGroupMetaData(GroupMetaData group) {
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700468 if (mGroups == null) {
Dmitri Plotnikove843f912010-09-16 15:21:48 -0700469 mGroups = new ArrayList<GroupMetaData>();
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700470 }
471 mGroups.add(group);
472 }
473
Dmitri Plotnikove843f912010-09-16 15:21:48 -0700474 public List<GroupMetaData> getGroupMetaData() {
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700475 return mGroups;
476 }
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700477
478 public boolean isSendToVoicemail() {
479 return mSendToVoicemail;
480 }
481
482 public String getCustomRingtone() {
483 return mCustomRingtone;
484 }
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700485
486 public boolean isUserProfile() {
487 return mIsUserProfile;
488 }
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700489 }
490
Dave Santoro39156002011-07-19 01:18:14 -0700491 /**
492 * Projection used for the query that loads all data for the entire contact (except for
493 * social stream items).
494 */
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700495 private static class ContactQuery {
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700496 final static String[] COLUMNS = new String[] {
497 Contacts.NAME_RAW_CONTACT_ID,
498 Contacts.DISPLAY_NAME_SOURCE,
499 Contacts.LOOKUP_KEY,
500 Contacts.DISPLAY_NAME,
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700501 Contacts.DISPLAY_NAME_ALTERNATIVE,
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700502 Contacts.PHONETIC_NAME,
503 Contacts.PHOTO_ID,
504 Contacts.STARRED,
505 Contacts.CONTACT_PRESENCE,
506 Contacts.CONTACT_STATUS,
507 Contacts.CONTACT_STATUS_TIMESTAMP,
508 Contacts.CONTACT_STATUS_RES_PACKAGE,
509 Contacts.CONTACT_STATUS_LABEL,
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700510 Contacts.Entity.CONTACT_ID,
511 Contacts.Entity.RAW_CONTACT_ID,
512
513 RawContacts.ACCOUNT_NAME,
514 RawContacts.ACCOUNT_TYPE,
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700515 RawContacts.DATA_SET,
516 RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700517 RawContacts.DIRTY,
518 RawContacts.VERSION,
519 RawContacts.SOURCE_ID,
520 RawContacts.SYNC1,
521 RawContacts.SYNC2,
522 RawContacts.SYNC3,
523 RawContacts.SYNC4,
524 RawContacts.DELETED,
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700525 RawContacts.NAME_VERIFIED,
526
527 Contacts.Entity.DATA_ID,
528 Data.DATA1,
529 Data.DATA2,
530 Data.DATA3,
531 Data.DATA4,
532 Data.DATA5,
533 Data.DATA6,
534 Data.DATA7,
535 Data.DATA8,
536 Data.DATA9,
537 Data.DATA10,
538 Data.DATA11,
539 Data.DATA12,
540 Data.DATA13,
541 Data.DATA14,
542 Data.DATA15,
543 Data.SYNC1,
544 Data.SYNC2,
545 Data.SYNC3,
546 Data.SYNC4,
547 Data.DATA_VERSION,
548 Data.IS_PRIMARY,
549 Data.IS_SUPER_PRIMARY,
550 Data.MIMETYPE,
551 Data.RES_PACKAGE,
552
553 GroupMembership.GROUP_SOURCE_ID,
554
555 Data.PRESENCE,
Daniel Lehmann8fd7bb62010-08-13 20:50:31 -0700556 Data.CHAT_CAPABILITY,
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700557 Data.STATUS,
558 Data.STATUS_RES_PACKAGE,
559 Data.STATUS_ICON,
560 Data.STATUS_LABEL,
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700561 Data.STATUS_TIMESTAMP,
562
563 Contacts.PHOTO_URI,
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700564 Contacts.SEND_TO_VOICEMAIL,
565 Contacts.CUSTOM_RINGTONE,
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700566 Contacts.IS_USER_PROFILE,
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700567 };
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700568
569 public final static int NAME_RAW_CONTACT_ID = 0;
570 public final static int DISPLAY_NAME_SOURCE = 1;
571 public final static int LOOKUP_KEY = 2;
572 public final static int DISPLAY_NAME = 3;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700573 public final static int ALT_DISPLAY_NAME = 4;
574 public final static int PHONETIC_NAME = 5;
575 public final static int PHOTO_ID = 6;
576 public final static int STARRED = 7;
577 public final static int CONTACT_PRESENCE = 8;
578 public final static int CONTACT_STATUS = 9;
579 public final static int CONTACT_STATUS_TIMESTAMP = 10;
580 public final static int CONTACT_STATUS_RES_PACKAGE = 11;
581 public final static int CONTACT_STATUS_LABEL = 12;
582 public final static int CONTACT_ID = 13;
583 public final static int RAW_CONTACT_ID = 14;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700584
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700585 public final static int ACCOUNT_NAME = 15;
586 public final static int ACCOUNT_TYPE = 16;
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700587 public final static int DATA_SET = 17;
588 public final static int ACCOUNT_TYPE_AND_DATA_SET = 18;
589 public final static int DIRTY = 19;
590 public final static int VERSION = 20;
591 public final static int SOURCE_ID = 21;
592 public final static int SYNC1 = 22;
593 public final static int SYNC2 = 23;
594 public final static int SYNC3 = 24;
595 public final static int SYNC4 = 25;
596 public final static int DELETED = 26;
597 public final static int NAME_VERIFIED = 27;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700598
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700599 public final static int DATA_ID = 28;
600 public final static int DATA1 = 29;
601 public final static int DATA2 = 30;
602 public final static int DATA3 = 31;
603 public final static int DATA4 = 32;
604 public final static int DATA5 = 33;
605 public final static int DATA6 = 34;
606 public final static int DATA7 = 35;
607 public final static int DATA8 = 36;
608 public final static int DATA9 = 37;
609 public final static int DATA10 = 38;
610 public final static int DATA11 = 39;
611 public final static int DATA12 = 40;
612 public final static int DATA13 = 41;
613 public final static int DATA14 = 42;
614 public final static int DATA15 = 43;
615 public final static int DATA_SYNC1 = 44;
616 public final static int DATA_SYNC2 = 45;
617 public final static int DATA_SYNC3 = 46;
618 public final static int DATA_SYNC4 = 47;
619 public final static int DATA_VERSION = 48;
620 public final static int IS_PRIMARY = 49;
621 public final static int IS_SUPERPRIMARY = 50;
622 public final static int MIMETYPE = 51;
623 public final static int RES_PACKAGE = 52;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700624
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700625 public final static int GROUP_SOURCE_ID = 53;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700626
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700627 public final static int PRESENCE = 54;
628 public final static int CHAT_CAPABILITY = 55;
629 public final static int STATUS = 56;
630 public final static int STATUS_RES_PACKAGE = 57;
631 public final static int STATUS_ICON = 58;
632 public final static int STATUS_LABEL = 59;
633 public final static int STATUS_TIMESTAMP = 60;
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700634
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700635 public final static int PHOTO_URI = 61;
636 public final static int SEND_TO_VOICEMAIL = 62;
637 public final static int CUSTOM_RINGTONE = 63;
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700638 public final static int IS_USER_PROFILE = 64;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700639 }
Daniel Lehmann1316b132010-04-13 15:08:53 -0700640
Dave Santoro39156002011-07-19 01:18:14 -0700641 /**
642 * Projection used for the query that loads all data for the entire contact.
643 */
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700644 private static class DirectoryQuery {
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700645 final static String[] COLUMNS = new String[] {
646 Directory.DISPLAY_NAME,
647 Directory.PACKAGE_NAME,
648 Directory.TYPE_RESOURCE_ID,
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700649 Directory.ACCOUNT_TYPE,
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700650 Directory.ACCOUNT_NAME,
651 Directory.EXPORT_SUPPORT,
652 };
653
654 public final static int DISPLAY_NAME = 0;
655 public final static int PACKAGE_NAME = 1;
656 public final static int TYPE_RESOURCE_ID = 2;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700657 public final static int ACCOUNT_TYPE = 3;
658 public final static int ACCOUNT_NAME = 4;
659 public final static int EXPORT_SUPPORT = 5;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700660 }
661
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700662 private static class GroupQuery {
663 final static String[] COLUMNS = new String[] {
664 Groups.ACCOUNT_NAME,
665 Groups.ACCOUNT_TYPE,
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700666 Groups.DATA_SET,
667 Groups.ACCOUNT_TYPE_AND_DATA_SET,
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700668 Groups._ID,
669 Groups.TITLE,
670 Groups.AUTO_ADD,
671 Groups.FAVORITES,
672 };
673
674 public final static int ACCOUNT_NAME = 0;
675 public final static int ACCOUNT_TYPE = 1;
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700676 public final static int DATA_SET = 2;
677 public final static int ACCOUNT_TYPE_AND_DATA_SET = 3;
678 public final static int ID = 4;
679 public final static int TITLE = 5;
680 public final static int AUTO_ADD = 6;
681 public final static int FAVORITES = 7;
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700682 }
683
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700684 private final class LoadContactTask extends AsyncTask<Void, Void, Result> {
Daniel Lehmann1316b132010-04-13 15:08:53 -0700685
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700686 @Override
687 protected Result doInBackground(Void... args) {
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700688 try {
689 final ContentResolver resolver = getContext().getContentResolver();
Katherine Kuan6cd5b0a2011-09-16 11:46:01 -0700690 final Uri uriCurrentFormat = ContactLoaderUtils.ensureIsContactUri(
691 resolver, mLookupUri);
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700692 Result result = loadContactEntity(resolver, uriCurrentFormat);
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700693 if (!result.isNotFound()) {
Dmitri Plotnikov217245c2010-09-18 13:04:50 -0700694 if (result.isDirectoryEntry()) {
695 loadDirectoryMetaData(result);
696 } else if (mLoadGroupMetaData) {
697 loadGroupMetaData(result);
698 }
Dave Santoro39156002011-07-19 01:18:14 -0700699 if (mLoadStreamItems) {
700 loadStreamItems(result);
701 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800702 loadPhotoBinaryData(result);
Makoto Onuki870a87e2011-08-12 13:40:31 -0700703
704 // Note ME profile should never have "Add connection"
705 if (mLoadInvitableAccountTypes && !result.isUserProfile()) {
Makoto Onuki69b4a882011-07-22 10:05:10 -0700706 loadInvitableAccountTypes(result);
707 }
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700708 }
709 return result;
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700710 } catch (Exception e) {
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700711 Log.e(TAG, "Error loading the contact: " + mLookupUri, e);
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700712 return Result.forError(mRequestedUri, e);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700713 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700714 }
715
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700716 private Result loadContactEntity(ContentResolver resolver, Uri contactUri) {
717 Uri entityUri = Uri.withAppendedPath(contactUri, Contacts.Entity.CONTENT_DIRECTORY);
718 Cursor cursor = resolver.query(entityUri, ContactQuery.COLUMNS, null, null,
719 Contacts.Entity.RAW_CONTACT_ID);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700720 if (cursor == null) {
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700721 Log.e(TAG, "No cursor returned in loadContactEntity");
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700722 return Result.forNotFound(mRequestedUri);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700723 }
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700724
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700725 try {
726 if (!cursor.moveToFirst()) {
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700727 cursor.close();
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700728 return Result.forNotFound(mRequestedUri);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700729 }
730
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700731 long currentRawContactId = -1;
732 Entity entity = null;
733 Result result = loadContactHeaderData(cursor, contactUri);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700734 ArrayList<Entity> entities = result.getEntities();
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700735 HashMap<Long, DataStatus> statuses = result.getStatuses();
736 for (; !cursor.isAfterLast(); cursor.moveToNext()) {
737 long rawContactId = cursor.getLong(ContactQuery.RAW_CONTACT_ID);
738 if (rawContactId != currentRawContactId) {
739 currentRawContactId = rawContactId;
740 entity = new android.content.Entity(loadRawContact(cursor));
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700741 entities.add(entity);
742 }
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700743 if (!cursor.isNull(ContactQuery.DATA_ID)) {
744 ContentValues data = loadData(cursor);
745 entity.addSubValue(ContactsContract.Data.CONTENT_URI, data);
746
747 if (!cursor.isNull(ContactQuery.PRESENCE)
748 || !cursor.isNull(ContactQuery.STATUS)) {
749 final DataStatus status = new DataStatus(cursor);
750 final long dataId = cursor.getLong(ContactQuery.DATA_ID);
751 statuses.put(dataId, status);
752 }
753 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700754 }
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700755
756 return result;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700757 } finally {
758 cursor.close();
759 }
760 }
761
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700762 /**
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800763 * Looks for the photo data item in entities. If found, creates a new Bitmap instance. If
764 * not found, returns null
765 */
766 private void loadPhotoBinaryData(Result contactData) {
Dave Santoro0a2a5db2011-06-29 00:37:06 -0700767
768 // If we have a photo URI, try loading that first.
769 String photoUri = contactData.getPhotoUri();
770 if (photoUri != null) {
771 try {
772 AssetFileDescriptor fd = getContext().getContentResolver()
773 .openAssetFileDescriptor(Uri.parse(photoUri), "r");
774 byte[] buffer = new byte[16 * 1024];
775 FileInputStream fis = fd.createInputStream();
776 ByteArrayOutputStream baos = new ByteArrayOutputStream();
777 try {
778 int size;
779 while ((size = fis.read(buffer)) != -1) {
780 baos.write(buffer, 0, size);
781 }
782 contactData.setPhotoBinaryData(baos.toByteArray());
783 } finally {
784 fis.close();
785 fd.close();
786 }
787 return;
788 } catch (IOException ioe) {
789 // Just fall back to the case below.
790 }
791 }
792
793 // If we couldn't load from a file, fall back to the data blob.
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800794 final long photoId = contactData.getPhotoId();
795 if (photoId <= 0) {
796 // No photo ID
797 return;
798 }
799
800 for (Entity entity : contactData.getEntities()) {
801 for (NamedContentValues subValue : entity.getSubValues()) {
802 final ContentValues entryValues = subValue.values;
803 final long dataId = entryValues.getAsLong(Data._ID);
804 if (dataId == photoId) {
805 final String mimeType = entryValues.getAsString(Data.MIMETYPE);
806 // Correct Data Id but incorrect MimeType? Don't load
807 if (!Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
808 return;
809 }
810 contactData.setPhotoBinaryData(entryValues.getAsByteArray(Photo.PHOTO));
811 break;
812 }
813 }
814 }
815 }
816
Makoto Onuki3e6991e2011-07-24 14:51:20 -0700817 /**
818 * Sets the "invitable" account types to {@link Result#mInvitableAccountTypes}.
Makoto Onuki3e6991e2011-07-24 14:51:20 -0700819 */
Makoto Onuki69b4a882011-07-22 10:05:10 -0700820 private void loadInvitableAccountTypes(Result contactData) {
Katherine Kuan08bcf712011-10-09 13:43:53 -0700821 Map<AccountTypeWithDataSet, AccountType> invitables =
822 AccountTypeManager.getInstance(getContext()).getUsableInvitableAccountTypes();
823 if (invitables.isEmpty()) {
Makoto Onuki69b4a882011-07-22 10:05:10 -0700824 return;
825 }
826
Katherine Kuan08bcf712011-10-09 13:43:53 -0700827 HashMap<AccountTypeWithDataSet, AccountType> result = Maps.newHashMap(invitables);
Makoto Onuki69b4a882011-07-22 10:05:10 -0700828
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -0700829 // Remove the ones that already have a raw contact in the current contact
Makoto Onuki69b4a882011-07-22 10:05:10 -0700830 for (Entity entity : contactData.getEntities()) {
Makoto Onuki6ad227f2011-08-15 13:46:59 -0700831 final ContentValues values = entity.getEntityValues();
832 final AccountTypeWithDataSet type = AccountTypeWithDataSet.get(
833 values.getAsString(RawContacts.ACCOUNT_TYPE),
834 values.getAsString(RawContacts.DATA_SET));
835 result.remove(type);
Makoto Onuki69b4a882011-07-22 10:05:10 -0700836 }
837
838 // Set to mInvitableAccountTypes
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700839 contactData.mInvitableAccountTypes.addAll(result.values());
Makoto Onuki69b4a882011-07-22 10:05:10 -0700840 }
841
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800842 /**
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700843 * Extracts Contact level columns from the cursor.
844 */
845 private Result loadContactHeaderData(final Cursor cursor, Uri contactUri) {
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700846 final String directoryParameter =
847 contactUri.getQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY);
848 final long directoryId = directoryParameter == null
849 ? Directory.DEFAULT
850 : Long.parseLong(directoryParameter);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700851 final long contactId = cursor.getLong(ContactQuery.CONTACT_ID);
852 final String lookupKey = cursor.getString(ContactQuery.LOOKUP_KEY);
853 final long nameRawContactId = cursor.getLong(ContactQuery.NAME_RAW_CONTACT_ID);
854 final int displayNameSource = cursor.getInt(ContactQuery.DISPLAY_NAME_SOURCE);
855 final String displayName = cursor.getString(ContactQuery.DISPLAY_NAME);
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700856 final String altDisplayName = cursor.getString(ContactQuery.ALT_DISPLAY_NAME);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700857 final String phoneticName = cursor.getString(ContactQuery.PHONETIC_NAME);
858 final long photoId = cursor.getLong(ContactQuery.PHOTO_ID);
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700859 final String photoUri = cursor.getString(ContactQuery.PHOTO_URI);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700860 final boolean starred = cursor.getInt(ContactQuery.STARRED) != 0;
861 final Integer presence = cursor.isNull(ContactQuery.CONTACT_PRESENCE)
862 ? null
863 : cursor.getInt(ContactQuery.CONTACT_PRESENCE);
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700864 final boolean sendToVoicemail = cursor.getInt(ContactQuery.SEND_TO_VOICEMAIL) == 1;
865 final String customRingtone = cursor.getString(ContactQuery.CUSTOM_RINGTONE);
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700866 final boolean isUserProfile = cursor.getInt(ContactQuery.IS_USER_PROFILE) == 1;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700867
Dmitri Plotnikov1536ea12010-10-29 11:51:05 -0700868 Uri lookupUri;
869 if (directoryId == Directory.DEFAULT || directoryId == Directory.LOCAL_INVISIBLE) {
870 lookupUri = ContentUris.withAppendedId(
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700871 Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey), contactId);
Dmitri Plotnikov1536ea12010-10-29 11:51:05 -0700872 } else {
873 lookupUri = contactUri;
874 }
875
Daniel Lehmann685157e2011-08-29 21:07:01 -0700876 return new Result(mRequestedUri, contactUri, lookupUri, directoryId, lookupKey,
877 contactId, nameRawContactId, displayNameSource, photoId, photoUri, displayName,
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700878 altDisplayName, phoneticName, starred, presence, sendToVoicemail,
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700879 customRingtone, isUserProfile);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700880 }
881
882 /**
883 * Extracts RawContact level columns from the cursor.
884 */
885 private ContentValues loadRawContact(Cursor cursor) {
886 ContentValues cv = new ContentValues();
887
888 cv.put(RawContacts._ID, cursor.getLong(ContactQuery.RAW_CONTACT_ID));
889
890 cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_NAME);
891 cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_TYPE);
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700892 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SET);
893 cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_TYPE_AND_DATA_SET);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700894 cursorColumnToContentValues(cursor, cv, ContactQuery.DIRTY);
895 cursorColumnToContentValues(cursor, cv, ContactQuery.VERSION);
896 cursorColumnToContentValues(cursor, cv, ContactQuery.SOURCE_ID);
897 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC1);
898 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC2);
899 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC3);
900 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC4);
901 cursorColumnToContentValues(cursor, cv, ContactQuery.DELETED);
902 cursorColumnToContentValues(cursor, cv, ContactQuery.CONTACT_ID);
903 cursorColumnToContentValues(cursor, cv, ContactQuery.STARRED);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700904 cursorColumnToContentValues(cursor, cv, ContactQuery.NAME_VERIFIED);
905
906 return cv;
907 }
908
909 /**
910 * Extracts Data level columns from the cursor.
911 */
912 private ContentValues loadData(Cursor cursor) {
913 ContentValues cv = new ContentValues();
914
915 cv.put(Data._ID, cursor.getLong(ContactQuery.DATA_ID));
916
917 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA1);
918 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA2);
919 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA3);
920 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA4);
921 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA5);
922 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA6);
923 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA7);
924 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA8);
925 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA9);
926 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA10);
927 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA11);
928 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA12);
929 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA13);
930 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA14);
931 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA15);
932 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC1);
933 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC2);
934 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC3);
935 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC4);
936 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_VERSION);
937 cursorColumnToContentValues(cursor, cv, ContactQuery.IS_PRIMARY);
938 cursorColumnToContentValues(cursor, cv, ContactQuery.IS_SUPERPRIMARY);
939 cursorColumnToContentValues(cursor, cv, ContactQuery.MIMETYPE);
940 cursorColumnToContentValues(cursor, cv, ContactQuery.RES_PACKAGE);
941 cursorColumnToContentValues(cursor, cv, ContactQuery.GROUP_SOURCE_ID);
Daniel Lehmann8fd7bb62010-08-13 20:50:31 -0700942 cursorColumnToContentValues(cursor, cv, ContactQuery.CHAT_CAPABILITY);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700943
944 return cv;
945 }
946
947 private void cursorColumnToContentValues(
948 Cursor cursor, ContentValues values, int index) {
949 switch (cursor.getType(index)) {
950 case Cursor.FIELD_TYPE_NULL:
951 // don't put anything in the content values
952 break;
953 case Cursor.FIELD_TYPE_INTEGER:
954 values.put(ContactQuery.COLUMNS[index], cursor.getLong(index));
955 break;
956 case Cursor.FIELD_TYPE_STRING:
957 values.put(ContactQuery.COLUMNS[index], cursor.getString(index));
958 break;
959 case Cursor.FIELD_TYPE_BLOB:
960 values.put(ContactQuery.COLUMNS[index], cursor.getBlob(index));
961 break;
962 default:
963 throw new IllegalStateException("Invalid or unhandled data type");
964 }
965 }
966
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700967 private void loadDirectoryMetaData(Result result) {
968 long directoryId = result.getDirectoryId();
969
970 Cursor cursor = getContext().getContentResolver().query(
971 ContentUris.withAppendedId(Directory.CONTENT_URI, directoryId),
972 DirectoryQuery.COLUMNS, null, null, null);
973 if (cursor == null) {
974 return;
975 }
976 try {
977 if (cursor.moveToFirst()) {
978 final String displayName = cursor.getString(DirectoryQuery.DISPLAY_NAME);
979 final String packageName = cursor.getString(DirectoryQuery.PACKAGE_NAME);
980 final int typeResourceId = cursor.getInt(DirectoryQuery.TYPE_RESOURCE_ID);
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700981 final String accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE);
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700982 final String accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME);
983 final int exportSupport = cursor.getInt(DirectoryQuery.EXPORT_SUPPORT);
984 String directoryType = null;
985 if (!TextUtils.isEmpty(packageName)) {
986 PackageManager pm = getContext().getPackageManager();
987 try {
988 Resources resources = pm.getResourcesForApplication(packageName);
989 directoryType = resources.getString(typeResourceId);
990 } catch (NameNotFoundException e) {
991 Log.w(TAG, "Contact directory resource not found: "
992 + packageName + "." + typeResourceId);
993 }
994 }
995
996 result.setDirectoryMetaData(
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700997 displayName, directoryType, accountType, accountName, exportSupport);
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700998 }
999 } finally {
1000 cursor.close();
1001 }
1002 }
1003
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001004 /**
1005 * Loads groups meta-data for all groups associated with all constituent raw contacts'
1006 * accounts.
1007 */
1008 private void loadGroupMetaData(Result result) {
1009 StringBuilder selection = new StringBuilder();
1010 ArrayList<String> selectionArgs = new ArrayList<String>();
1011 for (Entity entity : result.mEntities) {
1012 ContentValues values = entity.getEntityValues();
1013 String accountName = values.getAsString(RawContacts.ACCOUNT_NAME);
1014 String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
Dave Santoro2b3f3c52011-07-26 17:35:42 -07001015 String dataSet = values.getAsString(RawContacts.DATA_SET);
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001016 if (accountName != null && accountType != null) {
1017 if (selection.length() != 0) {
1018 selection.append(" OR ");
1019 }
1020 selection.append(
Dave Santoro2b3f3c52011-07-26 17:35:42 -07001021 "(" + Groups.ACCOUNT_NAME + "=? AND " + Groups.ACCOUNT_TYPE + "=?");
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001022 selectionArgs.add(accountName);
1023 selectionArgs.add(accountType);
Dave Santoro2b3f3c52011-07-26 17:35:42 -07001024
1025 if (dataSet != null) {
1026 selection.append(" AND " + Groups.DATA_SET + "=?");
1027 selectionArgs.add(dataSet);
1028 } else {
1029 selection.append(" AND " + Groups.DATA_SET + " IS NULL");
1030 }
1031 selection.append(")");
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001032 }
1033 }
1034 Cursor cursor = getContext().getContentResolver().query(Groups.CONTENT_URI,
1035 GroupQuery.COLUMNS, selection.toString(), selectionArgs.toArray(new String[0]),
1036 null);
1037 try {
1038 while (cursor.moveToNext()) {
1039 final String accountName = cursor.getString(GroupQuery.ACCOUNT_NAME);
1040 final String accountType = cursor.getString(GroupQuery.ACCOUNT_TYPE);
Dave Santoro2b3f3c52011-07-26 17:35:42 -07001041 final String dataSet = cursor.getString(GroupQuery.DATA_SET);
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001042 final long groupId = cursor.getLong(GroupQuery.ID);
1043 final String title = cursor.getString(GroupQuery.TITLE);
1044 final boolean defaultGroup = cursor.isNull(GroupQuery.AUTO_ADD)
1045 ? false
1046 : cursor.getInt(GroupQuery.AUTO_ADD) != 0;
1047 final boolean favorites = cursor.isNull(GroupQuery.FAVORITES)
1048 ? false
1049 : cursor.getInt(GroupQuery.FAVORITES) != 0;
1050
Dmitri Plotnikove843f912010-09-16 15:21:48 -07001051 result.addGroupMetaData(new GroupMetaData(
Dave Santoro2b3f3c52011-07-26 17:35:42 -07001052 accountName, accountType, dataSet, groupId, title, defaultGroup,
1053 favorites));
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -07001054 }
1055 } finally {
1056 cursor.close();
1057 }
1058 }
1059
Dave Santoro39156002011-07-19 01:18:14 -07001060 /**
1061 * Loads all stream items and stream item photos belonging to this contact.
1062 */
1063 private void loadStreamItems(Result result) {
1064 Cursor cursor = getContext().getContentResolver().query(
1065 Contacts.CONTENT_LOOKUP_URI.buildUpon()
1066 .appendPath(result.getLookupKey())
1067 .appendPath(Contacts.StreamItems.CONTENT_DIRECTORY).build(),
1068 null, null, null, null);
1069 Map<Long, StreamItemEntry> streamItemsById = new HashMap<Long, StreamItemEntry>();
1070 ArrayList<StreamItemEntry> streamItems = new ArrayList<StreamItemEntry>();
1071 try {
1072 while (cursor.moveToNext()) {
1073 StreamItemEntry streamItem = new StreamItemEntry(cursor);
1074 streamItemsById.put(streamItem.getId(), streamItem);
1075 streamItems.add(streamItem);
1076 }
1077 } finally {
1078 cursor.close();
1079 }
1080
Makoto Onukida9cdc12012-02-27 16:11:50 -08001081 // Pre-decode all HTMLs
1082 final long start = System.currentTimeMillis();
1083 for (StreamItemEntry streamItem : streamItems) {
1084 streamItem.decodeHtml(getContext());
1085 }
1086 final long end = System.currentTimeMillis();
1087 if (DEBUG) {
1088 Log.d(TAG, "Decoded HTML for " + streamItems.size() + " items, took "
1089 + (end - start) + " ms");
1090 }
1091
Dave Santoro39156002011-07-19 01:18:14 -07001092 // Now retrieve any photo records associated with the stream items.
Dave Santoro39156002011-07-19 01:18:14 -07001093 if (!streamItems.isEmpty()) {
Dave Santoroa4400d52011-09-02 16:14:53 -07001094 if (result.isUserProfile()) {
1095 // If the stream items we're loading are for the profile, we can't bulk-load the
1096 // stream items with a custom selection.
1097 for (StreamItemEntry entry : streamItems) {
1098 Cursor siCursor = getContext().getContentResolver().query(
1099 Uri.withAppendedPath(
1100 ContentUris.withAppendedId(
1101 StreamItems.CONTENT_URI, entry.getId()),
1102 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY),
1103 null, null, null, null);
1104 try {
1105 while (siCursor.moveToNext()) {
1106 entry.addPhoto(new StreamItemPhotoEntry(siCursor));
1107 }
1108 } finally {
1109 siCursor.close();
1110 }
Dave Santoro39156002011-07-19 01:18:14 -07001111 }
Dave Santoroa4400d52011-09-02 16:14:53 -07001112 } else {
1113 String[] streamItemIdArr = new String[streamItems.size()];
1114 StringBuilder streamItemPhotoSelection = new StringBuilder();
1115 streamItemPhotoSelection.append(StreamItemPhotos.STREAM_ITEM_ID + " IN (");
1116 for (int i = 0; i < streamItems.size(); i++) {
1117 if (i > 0) {
1118 streamItemPhotoSelection.append(",");
1119 }
1120 streamItemPhotoSelection.append("?");
1121 streamItemIdArr[i] = String.valueOf(streamItems.get(i).getId());
Dave Santoro39156002011-07-19 01:18:14 -07001122 }
Dave Santoroa4400d52011-09-02 16:14:53 -07001123 streamItemPhotoSelection.append(")");
1124 Cursor sipCursor = getContext().getContentResolver().query(
1125 StreamItems.CONTENT_PHOTO_URI,
1126 null, streamItemPhotoSelection.toString(), streamItemIdArr,
1127 StreamItemPhotos.STREAM_ITEM_ID);
1128 try {
1129 while (sipCursor.moveToNext()) {
1130 long streamItemId = sipCursor.getLong(
1131 sipCursor.getColumnIndex(StreamItemPhotos.STREAM_ITEM_ID));
1132 StreamItemEntry streamItem = streamItemsById.get(streamItemId);
1133 streamItem.addPhoto(new StreamItemPhotoEntry(sipCursor));
1134 }
1135 } finally {
1136 sipCursor.close();
1137 }
Dave Santoro39156002011-07-19 01:18:14 -07001138 }
1139 }
1140
1141 // Set the sorted stream items on the result.
1142 Collections.sort(streamItems);
1143 result.mStreamItems.addAll(streamItems);
1144 }
1145
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001146 @Override
1147 protected void onPostExecute(Result result) {
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001148 unregisterObserver();
1149
Daniel Lehmann1316b132010-04-13 15:08:53 -07001150 // The creator isn't interested in any further updates
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001151 if (mDestroyed || result == null) {
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001152 return;
1153 }
1154
1155 mContact = result;
Daniel Lehmann18f104f2010-05-07 15:41:11 -07001156
Makoto Onuki2621c5b2011-10-03 12:56:16 -07001157 if (result.isLoaded()) {
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001158 mLookupUri = result.getLookupUri();
1159
1160 if (!result.isDirectoryEntry()) {
1161 Log.i(TAG, "Registering content observer for " + mLookupUri);
1162 if (mObserver == null) {
1163 mObserver = new ForceLoadContentObserver();
1164 }
1165 getContext().getContentResolver().registerContentObserver(
1166 mLookupUri, true, mObserver);
Daniel Lehmann18f104f2010-05-07 15:41:11 -07001167 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -08001168
1169 if (mContact.getPhotoBinaryData() == null && mContact.getPhotoUri() != null) {
1170 mContact.setLoadingPhoto(true);
1171 new AsyncPhotoLoader().execute(mContact.getPhotoUri());
1172 }
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001173
1174 // inform the source of the data that this contact is being looked at
1175 postViewNotificationToSyncAdapter();
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001176 }
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001177
1178 deliverResult(mContact);
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001179 }
1180 }
1181
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001182 /**
1183 * Posts a message to the contributing sync adapters that have opted-in, notifying them
1184 * that the contact has just been loaded
1185 */
1186 private void postViewNotificationToSyncAdapter() {
1187 Context context = getContext();
1188 for (Entity entity : mContact.getEntities()) {
1189 final ContentValues entityValues = entity.getEntityValues();
Makoto Onukiaba2b832011-08-12 15:44:53 -07001190 final long rawContactId = entityValues.getAsLong(RawContacts.Entity._ID);
1191 if (mNotifiedRawContactIds.contains(rawContactId)) {
1192 continue; // Already notified for this raw contact.
1193 }
1194 mNotifiedRawContactIds.add(rawContactId);
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001195 final String type = entityValues.getAsString(RawContacts.ACCOUNT_TYPE);
1196 final String dataSet = entityValues.getAsString(RawContacts.DATA_SET);
Flavio Lerda59a887e2011-08-14 18:13:17 +01001197 final AccountType accountType = AccountTypeManager.getInstance(context).getAccountType(
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001198 type, dataSet);
1199 final String serviceName = accountType.getViewContactNotifyServiceClassName();
1200 final String resPackageName = accountType.resPackageName;
1201 if (!TextUtils.isEmpty(serviceName) && !TextUtils.isEmpty(resPackageName)) {
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001202 final Uri uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
1203 final Intent intent = new Intent();
1204 intent.setClassName(resPackageName, serviceName);
1205 intent.setAction(Intent.ACTION_VIEW);
1206 intent.setDataAndType(uri, RawContacts.CONTENT_ITEM_TYPE);
1207 try {
1208 context.startService(intent);
1209 } catch (Exception e) {
1210 Log.e(TAG, "Error sending message to source-app", e);
1211 }
1212 }
1213 }
1214 }
1215
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -08001216 private class AsyncPhotoLoader extends AsyncTask<String, Void, byte[]> {
1217
1218 private static final int BUFFER_SIZE = 1024*16;
1219
1220 @Override
1221 protected byte[] doInBackground(String... params) {
1222 Uri uri = Uri.parse(params[0]);
1223 byte[] data = null;
1224 try {
1225 InputStream is = getContext().getContentResolver().openInputStream(uri);
1226 if (is != null) {
1227 ByteArrayOutputStream baos = new ByteArrayOutputStream();
1228 try {
1229 byte[] mBuffer = new byte[BUFFER_SIZE];
1230
1231 int size;
1232 while ((size = is.read(mBuffer)) != -1) {
1233 baos.write(mBuffer, 0, size);
1234 }
1235 data = baos.toByteArray();
1236 } finally {
1237 is.close();
1238 }
1239 } else {
1240 Log.v(TAG, "Cannot load photo " + uri);
1241 }
1242 } catch (IOException e) {
1243 Log.e(TAG, "Cannot load photo " + uri, e);
1244 }
1245
1246 return data;
1247 }
1248
1249 @Override
1250 protected void onPostExecute(byte[] data) {
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001251 if (mContact != null) {
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -08001252 mContact = new Result(mContact);
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001253 mContact.setPhotoBinaryData(data);
1254 mContact.setLoadingPhoto(false);
1255 deliverResult(mContact);
1256 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -08001257 }
1258 }
1259
Daniel Lehmann3a120772010-06-21 16:21:35 -07001260 private void unregisterObserver() {
1261 if (mObserver != null) {
1262 getContext().getContentResolver().unregisterContentObserver(mObserver);
1263 mObserver = null;
1264 }
1265 }
1266
Daniel Lehmanncdef2b62010-06-06 18:25:49 -07001267 public ContactLoader(Context context, Uri lookupUri) {
Makoto Onuki69b4a882011-07-22 10:05:10 -07001268 this(context, lookupUri, false, false, false);
Dmitri Plotnikove843f912010-09-16 15:21:48 -07001269 }
1270
Dave Santoro39156002011-07-19 01:18:14 -07001271 public ContactLoader(Context context, Uri lookupUri, boolean loadGroupMetaData,
Makoto Onuki69b4a882011-07-22 10:05:10 -07001272 boolean loadStreamItems, boolean loadInvitableAccountTypes) {
Daniel Lehmann74a2dc52010-04-15 16:52:33 -07001273 super(context);
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001274 mLookupUri = lookupUri;
Daniel Lehmann685157e2011-08-29 21:07:01 -07001275 mRequestedUri = lookupUri;
Dmitri Plotnikove843f912010-09-16 15:21:48 -07001276 mLoadGroupMetaData = loadGroupMetaData;
Dave Santoro39156002011-07-19 01:18:14 -07001277 mLoadStreamItems = loadStreamItems;
Makoto Onuki69b4a882011-07-22 10:05:10 -07001278 mLoadInvitableAccountTypes = loadInvitableAccountTypes;
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001279 }
1280
Daniel Lehmann2a45e352012-02-13 15:03:58 -08001281 /**
1282 * Sets whether to load stream items. Will trigger a reload if the value has changed.
1283 * At the moment, this is only used for debugging purposes
1284 */
1285 public void setLoadStreamItems(boolean value) {
1286 if (mLoadStreamItems != value) {
1287 mLoadStreamItems = value;
1288 onContentChanged();
1289 }
1290 }
1291
1292 public boolean getLoadStreamItems() {
1293 return mLoadStreamItems;
1294 }
1295
Dmitri Plotnikov5a30d9a2010-11-23 14:59:50 -08001296 public Uri getLookupUri() {
1297 return mLookupUri;
1298 }
1299
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001300 @Override
Dianne Hackbornc04fc272010-12-20 23:13:10 -08001301 protected void onStartLoading() {
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001302 if (mContact != null) {
Daniel Lehmanncbcc4492010-04-12 18:03:54 -07001303 deliverResult(mContact);
Dmitri Plotnikov97e90c62011-01-03 11:58:13 -08001304 }
1305
1306 if (takeContentChanged() || mContact == null) {
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001307 forceLoad();
1308 }
1309 }
1310
1311 @Override
Dianne Hackbornc04fc272010-12-20 23:13:10 -08001312 protected void onForceLoad() {
Daniel Lehmann18f104f2010-05-07 15:41:11 -07001313 final LoadContactTask task = new LoadContactTask();
Makoto Onuki73c341f2011-08-29 09:31:44 -07001314 task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[])null);
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001315 }
1316
1317 @Override
Dianne Hackbornc04fc272010-12-20 23:13:10 -08001318 protected void onReset() {
Dianne Hackborn4ef95cc2010-12-16 00:44:33 -08001319 unregisterObserver();
1320 mContact = null;
1321 mDestroyed = true;
1322 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001323}