blob: 17cd1e73654cd35f63b0779fa9036bdfbbe48d1c [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;
Daniel Lehmann9815d7f2012-04-16 18:28:03 -070026import com.android.contacts.util.UriUtils;
Flavio Lerda37a26842011-06-27 11:36:52 +010027import com.google.common.annotations.VisibleForTesting;
Daniel Lehmann49ea2d22012-04-17 11:17:12 -070028import com.google.common.collect.Lists;
Makoto Onuki6ad227f2011-08-15 13:46:59 -070029import com.google.common.collect.Maps;
Makoto Onukiaba2b832011-08-12 15:44:53 -070030import com.google.common.collect.Sets;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070031
Daniel Lehmann72ff4df2012-02-28 20:03:01 -080032import android.content.AsyncTaskLoader;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070033import android.content.ContentResolver;
34import android.content.ContentUris;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -070035import android.content.ContentValues;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070036import android.content.Context;
37import android.content.Entity;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -070038import android.content.Entity.NamedContentValues;
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -070039import android.content.Intent;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -070040import android.content.pm.PackageManager;
41import android.content.pm.PackageManager.NameNotFoundException;
Dave Santoro0a2a5db2011-06-29 00:37:06 -070042import android.content.res.AssetFileDescriptor;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -070043import android.content.res.Resources;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070044import android.database.Cursor;
45import android.net.Uri;
Daniel Lehmann1316b132010-04-13 15:08:53 -070046import android.provider.ContactsContract;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -070047import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -080048import android.provider.ContactsContract.CommonDataKinds.Photo;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070049import android.provider.ContactsContract.Contacts;
50import android.provider.ContactsContract.Data;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -070051import android.provider.ContactsContract.Directory;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070052import android.provider.ContactsContract.DisplayNameSources;
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -070053import android.provider.ContactsContract.Groups;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070054import android.provider.ContactsContract.RawContacts;
Dave Santoro39156002011-07-19 01:18:14 -070055import android.provider.ContactsContract.StreamItemPhotos;
Makoto Onuki69b4a882011-07-22 10:05:10 -070056import android.provider.ContactsContract.StreamItems;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -070057import android.text.TextUtils;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070058import android.util.Log;
Daniel Lehmann18958a22012-02-28 17:45:25 -080059import android.util.LongSparseArray;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070060
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -080061import java.io.ByteArrayOutputStream;
Dave Santoro0a2a5db2011-06-29 00:37:06 -070062import java.io.FileInputStream;
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -080063import java.io.IOException;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070064import java.util.ArrayList;
Dave Santoro39156002011-07-19 01:18:14 -070065import java.util.Collections;
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 Lehmann72ff4df2012-02-28 20:03:01 -080073public class ContactLoader extends AsyncTaskLoader<ContactLoader.Result> {
Daniel Lehmann18f104f2010-05-07 15:41:11 -070074 private static final String TAG = "ContactLoader";
75
Makoto Onukida9cdc12012-02-27 16:11:50 -080076 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
77
Daniel Lehmann9815d7f2012-04-16 18:28:03 -070078 /** A short-lived cache that can be set by {@link #cacheResult()} */
79 private static Result sCachedResult = null;
80
Daniel Lehmann685157e2011-08-29 21:07:01 -070081 private final Uri mRequestedUri;
Makoto Onuki2621c5b2011-10-03 12:56:16 -070082 private Uri mLookupUri;
Dmitri Plotnikove843f912010-09-16 15:21:48 -070083 private boolean mLoadGroupMetaData;
Dave Santoro39156002011-07-19 01:18:14 -070084 private boolean mLoadStreamItems;
Daniel Lehmann9815d7f2012-04-16 18:28:03 -070085 private boolean mLoadInvitableAccountTypes;
Daniel Lehmann4cd94412010-04-08 16:44:36 -070086 private Result mContact;
87 private ForceLoadContentObserver mObserver;
Makoto Onukiaba2b832011-08-12 15:44:53 -070088 private final Set<Long> mNotifiedRawContactIds = Sets.newHashSet();
Dmitri Plotnikove843f912010-09-16 15:21:48 -070089
Daniel Lehmanncdef2b62010-06-06 18:25:49 -070090 public interface Listener {
Daniel Lehmann4cd94412010-04-08 16:44:36 -070091 public void onContactLoaded(Result contact);
92 }
93
Daniel Lehmann72ff4df2012-02-28 20:03:01 -080094 public ContactLoader(Context context, Uri lookupUri) {
95 this(context, lookupUri, false, false, false);
96 }
97
98 public ContactLoader(Context context, Uri lookupUri, boolean loadGroupMetaData,
99 boolean loadStreamItems, boolean loadInvitableAccountTypes) {
100 super(context);
101 mLookupUri = lookupUri;
102 mRequestedUri = lookupUri;
103 mLoadGroupMetaData = loadGroupMetaData;
104 mLoadStreamItems = loadStreamItems;
105 mLoadInvitableAccountTypes = loadInvitableAccountTypes;
106 }
107
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700108 /**
109 * The result of a load operation. Contains all data necessary to display the contact.
110 */
111 public static final class Result {
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700112 private enum Status {
113 /** Contact is successfully loaded */
114 LOADED,
115 /** There was an error loading the contact */
116 ERROR,
117 /** Contact is not found */
118 NOT_FOUND,
119 }
Daniel Lehmann18f104f2010-05-07 15:41:11 -0700120
Daniel Lehmann685157e2011-08-29 21:07:01 -0700121 private final Uri mRequestedUri;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700122 private final Uri mLookupUri;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700123 private final Uri mUri;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700124 private final long mDirectoryId;
125 private final String mLookupKey;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700126 private final long mId;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700127 private final long mNameRawContactId;
128 private final int mDisplayNameSource;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700129 private final long mPhotoId;
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700130 private final String mPhotoUri;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700131 private final String mDisplayName;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700132 private final String mAltDisplayName;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700133 private final String mPhoneticName;
134 private final boolean mStarred;
135 private final Integer mPresence;
136 private final ArrayList<Entity> mEntities;
Daniel Lehmann9815d7f2012-04-16 18:28:03 -0700137 private ArrayList<StreamItemEntry> mStreamItems;
Daniel Lehmann18958a22012-02-28 17:45:25 -0800138 private final LongSparseArray<DataStatus> mStatuses;
Daniel Lehmann9815d7f2012-04-16 18:28:03 -0700139 private ArrayList<AccountType> mInvitableAccountTypes;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700140
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700141 private String mDirectoryDisplayName;
142 private String mDirectoryType;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700143 private String mDirectoryAccountType;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700144 private String mDirectoryAccountName;
145 private int mDirectoryExportSupport;
146
Dmitri Plotnikove843f912010-09-16 15:21:48 -0700147 private ArrayList<GroupMetaData> mGroups;
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700148
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800149 private byte[] mPhotoBinaryData;
Makoto Onuki870a87e2011-08-12 13:40:31 -0700150 private final boolean mSendToVoicemail;
151 private final String mCustomRingtone;
152 private final boolean mIsUserProfile;
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800153
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700154 private final Status mStatus;
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700155 private final Exception mException;
156
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700157 /**
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700158 * Constructor for special results, namely "no contact found" and "error".
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700159 */
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700160 private Result(Uri requestedUri, Status status, Exception exception) {
161 if (status == Status.ERROR && exception == null) {
162 throw new IllegalArgumentException("ERROR result must have exception");
163 }
164 mStatus = status;
165 mException = exception;
166 mRequestedUri = requestedUri;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700167 mLookupUri = null;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700168 mUri = null;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700169 mDirectoryId = -1;
170 mLookupKey = null;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700171 mId = -1;
172 mEntities = null;
Daniel Lehmann9815d7f2012-04-16 18:28:03 -0700173 mStreamItems = null;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700174 mStatuses = null;
175 mNameRawContactId = -1;
176 mDisplayNameSource = DisplayNameSources.UNDEFINED;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700177 mPhotoId = -1;
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700178 mPhotoUri = null;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700179 mDisplayName = null;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700180 mAltDisplayName = null;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700181 mPhoneticName = null;
182 mStarred = false;
183 mPresence = null;
Makoto Onuki69b4a882011-07-22 10:05:10 -0700184 mInvitableAccountTypes = null;
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700185 mSendToVoicemail = false;
186 mCustomRingtone = null;
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700187 mIsUserProfile = false;
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700188 }
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700189
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700190 private static Result forError(Uri requestedUri, Exception exception) {
191 return new Result(requestedUri, Status.ERROR, exception);
192 }
193
194 private static Result forNotFound(Uri requestedUri) {
195 return new Result(requestedUri, Status.NOT_FOUND, null);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700196 }
197
198 /**
199 * Constructor to call when contact was found
200 */
Daniel Lehmann685157e2011-08-29 21:07:01 -0700201 private Result(Uri requestedUri, Uri uri, Uri lookupUri, long directoryId, String lookupKey,
202 long id, long nameRawContactId, int displayNameSource, long photoId,
203 String photoUri, String displayName, String altDisplayName, String phoneticName,
204 boolean starred, Integer presence, boolean sendToVoicemail, String customRingtone,
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700205 boolean isUserProfile) {
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700206 mStatus = Status.LOADED;
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700207 mException = null;
Daniel Lehmann685157e2011-08-29 21:07:01 -0700208 mRequestedUri = requestedUri;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700209 mLookupUri = lookupUri;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700210 mUri = uri;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700211 mDirectoryId = directoryId;
212 mLookupKey = lookupKey;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700213 mId = id;
214 mEntities = new ArrayList<Entity>();
Daniel Lehmann9815d7f2012-04-16 18:28:03 -0700215 mStreamItems = null;
Daniel Lehmann18958a22012-02-28 17:45:25 -0800216 mStatuses = new LongSparseArray<DataStatus>();
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700217 mNameRawContactId = nameRawContactId;
218 mDisplayNameSource = displayNameSource;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700219 mPhotoId = photoId;
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700220 mPhotoUri = photoUri;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700221 mDisplayName = displayName;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700222 mAltDisplayName = altDisplayName;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700223 mPhoneticName = phoneticName;
224 mStarred = starred;
225 mPresence = presence;
Daniel Lehmann9815d7f2012-04-16 18:28:03 -0700226 mInvitableAccountTypes = null;
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700227 mSendToVoicemail = sendToVoicemail;
228 mCustomRingtone = customRingtone;
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700229 mIsUserProfile = isUserProfile;
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700230 }
231
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800232 private Result(Result from) {
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700233 mStatus = from.mStatus;
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700234 mException = from.mException;
Daniel Lehmann685157e2011-08-29 21:07:01 -0700235 mRequestedUri = from.mRequestedUri;
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800236 mLookupUri = from.mLookupUri;
237 mUri = from.mUri;
238 mDirectoryId = from.mDirectoryId;
239 mLookupKey = from.mLookupKey;
240 mId = from.mId;
241 mNameRawContactId = from.mNameRawContactId;
242 mDisplayNameSource = from.mDisplayNameSource;
243 mPhotoId = from.mPhotoId;
244 mPhotoUri = from.mPhotoUri;
245 mDisplayName = from.mDisplayName;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700246 mAltDisplayName = from.mAltDisplayName;
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800247 mPhoneticName = from.mPhoneticName;
248 mStarred = from.mStarred;
249 mPresence = from.mPresence;
250 mEntities = from.mEntities;
Dave Santoro39156002011-07-19 01:18:14 -0700251 mStreamItems = from.mStreamItems;
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800252 mStatuses = from.mStatuses;
Makoto Onuki69b4a882011-07-22 10:05:10 -0700253 mInvitableAccountTypes = from.mInvitableAccountTypes;
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800254
255 mDirectoryDisplayName = from.mDirectoryDisplayName;
256 mDirectoryType = from.mDirectoryType;
257 mDirectoryAccountType = from.mDirectoryAccountType;
258 mDirectoryAccountName = from.mDirectoryAccountName;
259 mDirectoryExportSupport = from.mDirectoryExportSupport;
260
261 mGroups = from.mGroups;
262
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800263 mPhotoBinaryData = from.mPhotoBinaryData;
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700264 mSendToVoicemail = from.mSendToVoicemail;
265 mCustomRingtone = from.mCustomRingtone;
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700266 mIsUserProfile = from.mIsUserProfile;
Dmitri Plotnikov7cee7742011-01-13 17:11:06 -0800267 }
268
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700269 /**
270 * @param exportSupport See {@link Directory#EXPORT_SUPPORT}.
271 */
Daniel Lehmann1ad4d1b2010-10-18 19:20:41 -0700272 private void setDirectoryMetaData(String displayName, String directoryType,
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700273 String accountType, String accountName, int exportSupport) {
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700274 mDirectoryDisplayName = displayName;
275 mDirectoryType = directoryType;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700276 mDirectoryAccountType = accountType;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700277 mDirectoryAccountName = accountName;
278 mDirectoryExportSupport = exportSupport;
279 }
280
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800281 private void setPhotoBinaryData(byte[] photoBinaryData) {
282 mPhotoBinaryData = photoBinaryData;
283 }
284
Daniel Lehmann685157e2011-08-29 21:07:01 -0700285 /**
286 * Returns the URI for the contact that contains both the lookup key and the ID. This is
287 * the best URI to reference a contact.
288 * For directory contacts, this is the same a the URI as returned by {@link #getUri()}
289 */
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700290 public Uri getLookupUri() {
291 return mLookupUri;
292 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800293
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700294 public String getLookupKey() {
295 return mLookupKey;
296 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800297
Daniel Lehmann685157e2011-08-29 21:07:01 -0700298 /**
299 * Returns the contact Uri that was passed to the provider to make the query. This is
300 * the same as the requested Uri, unless the requested Uri doesn't specify a Contact:
301 * If it either references a Raw-Contact or a Person (a pre-Eclair style Uri), this Uri will
302 * always reference the full aggregate contact.
303 */
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700304 public Uri getUri() {
305 return mUri;
306 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800307
Daniel Lehmann685157e2011-08-29 21:07:01 -0700308 /**
309 * Returns the URI for which this {@link ContactLoader) was initially requested.
310 */
311 public Uri getRequestedUri() {
312 return mRequestedUri;
313 }
314
Dave Santoro6fa73842011-09-28 14:37:06 -0700315 /**
316 * Returns the contact ID.
317 */
Makoto Onuki98306102011-11-28 15:16:58 -0800318 @VisibleForTesting
319 /* package */ long getId() {
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700320 return mId;
321 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800322
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700323 /**
324 * @return true when an exception happened during loading, in which case
325 * {@link #getException} returns the actual exception object.
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700326 * Note {@link #isNotFound()} and {@link #isError()} are mutually exclusive; If
327 * {@link #isError()} is {@code true}, {@link #isNotFound()} is always {@code false},
328 * and vice versa.
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700329 */
330 public boolean isError() {
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700331 return mStatus == Status.ERROR;
Makoto Onuki9e7b5da2011-08-22 15:51:28 -0700332 }
333
334 public Exception getException() {
335 return mException;
336 }
337
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700338 /**
339 * @return true when the specified contact is not found.
340 * Note {@link #isNotFound()} and {@link #isError()} are mutually exclusive; If
341 * {@link #isError()} is {@code true}, {@link #isNotFound()} is always {@code false},
342 * and vice versa.
343 */
344 public boolean isNotFound() {
345 return mStatus == Status.NOT_FOUND;
346 }
347
348 /**
349 * @return true if the specified contact is successfully loaded.
350 * i.e. neither {@link #isError()} nor {@link #isNotFound()}.
351 */
352 public boolean isLoaded() {
353 return mStatus == Status.LOADED;
354 }
355
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700356 public long getNameRawContactId() {
357 return mNameRawContactId;
358 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800359
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700360 public int getDisplayNameSource() {
361 return mDisplayNameSource;
362 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800363
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700364 public long getPhotoId() {
365 return mPhotoId;
366 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800367
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700368 public String getPhotoUri() {
369 return mPhotoUri;
370 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800371
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700372 public String getDisplayName() {
373 return mDisplayName;
374 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800375
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700376 public String getAltDisplayName() {
377 return mAltDisplayName;
378 }
379
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700380 public String getPhoneticName() {
381 return mPhoneticName;
382 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800383
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700384 public boolean getStarred() {
385 return mStarred;
386 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800387
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700388 public Integer getPresence() {
389 return mPresence;
390 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800391
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700392 public ArrayList<AccountType> getInvitableAccountTypes() {
Makoto Onuki69b4a882011-07-22 10:05:10 -0700393 return mInvitableAccountTypes;
394 }
395
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700396 public ArrayList<Entity> getEntities() {
397 return mEntities;
398 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800399
Dave Santoro39156002011-07-19 01:18:14 -0700400 public ArrayList<StreamItemEntry> getStreamItems() {
401 return mStreamItems;
402 }
403
Daniel Lehmann18958a22012-02-28 17:45:25 -0800404 public LongSparseArray<DataStatus> getStatuses() {
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700405 return mStatuses;
406 }
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700407
408 public long getDirectoryId() {
409 return mDirectoryId;
410 }
411
412 public boolean isDirectoryEntry() {
Dmitri Plotnikov5f72c1f2010-09-01 21:21:04 -0700413 return mDirectoryId != -1 && mDirectoryId != Directory.DEFAULT
414 && mDirectoryId != Directory.LOCAL_INVISIBLE;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700415 }
416
Josh Gargus187c8162012-03-13 17:06:53 -0700417 /**
418 * @return true if this is a contact (not group, etc.) with at least one
Josh Gargus84edfd92012-03-15 18:25:58 -0700419 * writable raw-contact, and false otherwise.
Josh Gargus187c8162012-03-13 17:06:53 -0700420 */
Josh Gargus84edfd92012-03-15 18:25:58 -0700421 public boolean isWritableContact(final Context context) {
Josh Gargus187c8162012-03-13 17:06:53 -0700422 if (isDirectoryEntry()) return false;
423 final AccountTypeManager accountTypes = AccountTypeManager.getInstance(context);
424 for (Entity rawContact : getEntities()) {
425 final ContentValues rawValues = rawContact.getEntityValues();
426 final String accountType = rawValues.getAsString(RawContacts.ACCOUNT_TYPE);
427 final String dataSet = rawValues.getAsString(RawContacts.DATA_SET);
428 final AccountType type = accountTypes.getAccountType(accountType, dataSet);
429 if (type != null && type.areContactsWritable()) return true;
430 }
431 return false;
432 }
433
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700434 public int getDirectoryExportSupport() {
435 return mDirectoryExportSupport;
436 }
437
438 public String getDirectoryDisplayName() {
439 return mDirectoryDisplayName;
440 }
441
442 public String getDirectoryType() {
443 return mDirectoryType;
444 }
445
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700446 public String getDirectoryAccountType() {
447 return mDirectoryAccountType;
448 }
449
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700450 public String getDirectoryAccountName() {
451 return mDirectoryAccountName;
452 }
453
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -0800454 public byte[] getPhotoBinaryData() {
455 return mPhotoBinaryData;
456 }
457
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700458 public ArrayList<ContentValues> getContentValues() {
459 if (mEntities.size() != 1) {
460 throw new IllegalStateException(
461 "Cannot extract content values from an aggregated contact");
462 }
463
464 Entity entity = mEntities.get(0);
465 ArrayList<ContentValues> result = new ArrayList<ContentValues>();
466 ArrayList<NamedContentValues> subValues = entity.getSubValues();
467 if (subValues != null) {
468 int size = subValues.size();
469 for (int i = 0; i < size; i++) {
470 NamedContentValues pair = subValues.get(i);
471 if (Data.CONTENT_URI.equals(pair.uri)) {
472 result.add(pair.values);
473 }
474 }
475 }
Dmitri Plotnikov40ec3a82010-11-10 11:25:33 -0800476
477 // If the photo was loaded using the URI, create an entry for the photo
478 // binary data.
479 if (mPhotoId == 0 && mPhotoBinaryData != null) {
480 ContentValues photo = new ContentValues();
481 photo.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
482 photo.put(Photo.PHOTO, mPhotoBinaryData);
483 result.add(photo);
484 }
485
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700486 return result;
487 }
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700488
Dmitri Plotnikove843f912010-09-16 15:21:48 -0700489 public List<GroupMetaData> getGroupMetaData() {
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700490 return mGroups;
491 }
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700492
493 public boolean isSendToVoicemail() {
494 return mSendToVoicemail;
495 }
496
497 public String getCustomRingtone() {
498 return mCustomRingtone;
499 }
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700500
501 public boolean isUserProfile() {
502 return mIsUserProfile;
503 }
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700504 }
505
Dave Santoro39156002011-07-19 01:18:14 -0700506 /**
507 * Projection used for the query that loads all data for the entire contact (except for
508 * social stream items).
509 */
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700510 private static class ContactQuery {
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700511 final static String[] COLUMNS = new String[] {
512 Contacts.NAME_RAW_CONTACT_ID,
513 Contacts.DISPLAY_NAME_SOURCE,
514 Contacts.LOOKUP_KEY,
515 Contacts.DISPLAY_NAME,
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700516 Contacts.DISPLAY_NAME_ALTERNATIVE,
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700517 Contacts.PHONETIC_NAME,
518 Contacts.PHOTO_ID,
519 Contacts.STARRED,
520 Contacts.CONTACT_PRESENCE,
521 Contacts.CONTACT_STATUS,
522 Contacts.CONTACT_STATUS_TIMESTAMP,
523 Contacts.CONTACT_STATUS_RES_PACKAGE,
524 Contacts.CONTACT_STATUS_LABEL,
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700525 Contacts.Entity.CONTACT_ID,
526 Contacts.Entity.RAW_CONTACT_ID,
527
528 RawContacts.ACCOUNT_NAME,
529 RawContacts.ACCOUNT_TYPE,
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700530 RawContacts.DATA_SET,
531 RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700532 RawContacts.DIRTY,
533 RawContacts.VERSION,
534 RawContacts.SOURCE_ID,
535 RawContacts.SYNC1,
536 RawContacts.SYNC2,
537 RawContacts.SYNC3,
538 RawContacts.SYNC4,
539 RawContacts.DELETED,
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700540 RawContacts.NAME_VERIFIED,
541
542 Contacts.Entity.DATA_ID,
543 Data.DATA1,
544 Data.DATA2,
545 Data.DATA3,
546 Data.DATA4,
547 Data.DATA5,
548 Data.DATA6,
549 Data.DATA7,
550 Data.DATA8,
551 Data.DATA9,
552 Data.DATA10,
553 Data.DATA11,
554 Data.DATA12,
555 Data.DATA13,
556 Data.DATA14,
557 Data.DATA15,
558 Data.SYNC1,
559 Data.SYNC2,
560 Data.SYNC3,
561 Data.SYNC4,
562 Data.DATA_VERSION,
563 Data.IS_PRIMARY,
564 Data.IS_SUPER_PRIMARY,
565 Data.MIMETYPE,
566 Data.RES_PACKAGE,
567
568 GroupMembership.GROUP_SOURCE_ID,
569
570 Data.PRESENCE,
Daniel Lehmann8fd7bb62010-08-13 20:50:31 -0700571 Data.CHAT_CAPABILITY,
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700572 Data.STATUS,
573 Data.STATUS_RES_PACKAGE,
574 Data.STATUS_ICON,
575 Data.STATUS_LABEL,
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700576 Data.STATUS_TIMESTAMP,
577
578 Contacts.PHOTO_URI,
Isaac Katzenelson683b57e2011-07-20 17:06:11 -0700579 Contacts.SEND_TO_VOICEMAIL,
580 Contacts.CUSTOM_RINGTONE,
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700581 Contacts.IS_USER_PROFILE,
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700582 };
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700583
584 public final static int NAME_RAW_CONTACT_ID = 0;
585 public final static int DISPLAY_NAME_SOURCE = 1;
586 public final static int LOOKUP_KEY = 2;
587 public final static int DISPLAY_NAME = 3;
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700588 public final static int ALT_DISPLAY_NAME = 4;
589 public final static int PHONETIC_NAME = 5;
590 public final static int PHOTO_ID = 6;
591 public final static int STARRED = 7;
592 public final static int CONTACT_PRESENCE = 8;
593 public final static int CONTACT_STATUS = 9;
594 public final static int CONTACT_STATUS_TIMESTAMP = 10;
595 public final static int CONTACT_STATUS_RES_PACKAGE = 11;
596 public final static int CONTACT_STATUS_LABEL = 12;
597 public final static int CONTACT_ID = 13;
598 public final static int RAW_CONTACT_ID = 14;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700599
Dave Santoroda5bf1c2011-05-03 10:30:34 -0700600 public final static int ACCOUNT_NAME = 15;
601 public final static int ACCOUNT_TYPE = 16;
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700602 public final static int DATA_SET = 17;
603 public final static int ACCOUNT_TYPE_AND_DATA_SET = 18;
604 public final static int DIRTY = 19;
605 public final static int VERSION = 20;
606 public final static int SOURCE_ID = 21;
607 public final static int SYNC1 = 22;
608 public final static int SYNC2 = 23;
609 public final static int SYNC3 = 24;
610 public final static int SYNC4 = 25;
611 public final static int DELETED = 26;
612 public final static int NAME_VERIFIED = 27;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700613
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700614 public final static int DATA_ID = 28;
615 public final static int DATA1 = 29;
616 public final static int DATA2 = 30;
617 public final static int DATA3 = 31;
618 public final static int DATA4 = 32;
619 public final static int DATA5 = 33;
620 public final static int DATA6 = 34;
621 public final static int DATA7 = 35;
622 public final static int DATA8 = 36;
623 public final static int DATA9 = 37;
624 public final static int DATA10 = 38;
625 public final static int DATA11 = 39;
626 public final static int DATA12 = 40;
627 public final static int DATA13 = 41;
628 public final static int DATA14 = 42;
629 public final static int DATA15 = 43;
630 public final static int DATA_SYNC1 = 44;
631 public final static int DATA_SYNC2 = 45;
632 public final static int DATA_SYNC3 = 46;
633 public final static int DATA_SYNC4 = 47;
634 public final static int DATA_VERSION = 48;
635 public final static int IS_PRIMARY = 49;
636 public final static int IS_SUPERPRIMARY = 50;
637 public final static int MIMETYPE = 51;
638 public final static int RES_PACKAGE = 52;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700639
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700640 public final static int GROUP_SOURCE_ID = 53;
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700641
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700642 public final static int PRESENCE = 54;
643 public final static int CHAT_CAPABILITY = 55;
644 public final static int STATUS = 56;
645 public final static int STATUS_RES_PACKAGE = 57;
646 public final static int STATUS_ICON = 58;
647 public final static int STATUS_LABEL = 59;
648 public final static int STATUS_TIMESTAMP = 60;
Dmitri Plotnikovf9eb73f2010-10-21 11:48:56 -0700649
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700650 public final static int PHOTO_URI = 61;
651 public final static int SEND_TO_VOICEMAIL = 62;
652 public final static int CUSTOM_RINGTONE = 63;
Isaac Katzenelsonead19c52011-07-29 18:24:53 -0700653 public final static int IS_USER_PROFILE = 64;
Daniel Lehmannd3e0cdb2010-04-19 13:45:53 -0700654 }
Daniel Lehmann1316b132010-04-13 15:08:53 -0700655
Dave Santoro39156002011-07-19 01:18:14 -0700656 /**
657 * Projection used for the query that loads all data for the entire contact.
658 */
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700659 private static class DirectoryQuery {
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700660 final static String[] COLUMNS = new String[] {
661 Directory.DISPLAY_NAME,
662 Directory.PACKAGE_NAME,
663 Directory.TYPE_RESOURCE_ID,
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700664 Directory.ACCOUNT_TYPE,
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700665 Directory.ACCOUNT_NAME,
666 Directory.EXPORT_SUPPORT,
667 };
668
669 public final static int DISPLAY_NAME = 0;
670 public final static int PACKAGE_NAME = 1;
671 public final static int TYPE_RESOURCE_ID = 2;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700672 public final static int ACCOUNT_TYPE = 3;
673 public final static int ACCOUNT_NAME = 4;
674 public final static int EXPORT_SUPPORT = 5;
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700675 }
676
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700677 private static class GroupQuery {
678 final static String[] COLUMNS = new String[] {
679 Groups.ACCOUNT_NAME,
680 Groups.ACCOUNT_TYPE,
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700681 Groups.DATA_SET,
682 Groups.ACCOUNT_TYPE_AND_DATA_SET,
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700683 Groups._ID,
684 Groups.TITLE,
685 Groups.AUTO_ADD,
686 Groups.FAVORITES,
687 };
688
689 public final static int ACCOUNT_NAME = 0;
690 public final static int ACCOUNT_TYPE = 1;
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700691 public final static int DATA_SET = 2;
692 public final static int ACCOUNT_TYPE_AND_DATA_SET = 3;
693 public final static int ID = 4;
694 public final static int TITLE = 5;
695 public final static int AUTO_ADD = 6;
696 public final static int FAVORITES = 7;
Dmitri Plotnikov2deaee12010-09-15 15:42:08 -0700697 }
698
Daniel Lehmann72ff4df2012-02-28 20:03:01 -0800699 @Override
700 public Result loadInBackground() {
701 try {
702 final ContentResolver resolver = getContext().getContentResolver();
703 final Uri uriCurrentFormat = ContactLoaderUtils.ensureIsContactUri(
704 resolver, mLookupUri);
Daniel Lehmann9815d7f2012-04-16 18:28:03 -0700705 final Result cachedResult = sCachedResult;
706 sCachedResult = null;
707 // Is this the same Uri as what we had before already? In that case, reuse that result
708 final Result result;
709 final boolean resultIsCached;
710 if (cachedResult != null &&
711 UriUtils.areEqual(cachedResult.getLookupUri(), mLookupUri)) {
712 // We are using a cached result from earlier. Below, we should make sure
713 // we are not doing any more network or disc accesses
714 result = cachedResult;
715 resultIsCached = true;
716 } else {
717 result = loadContactEntity(resolver, uriCurrentFormat);
718 resultIsCached = false;
719 }
Daniel Lehmann72ff4df2012-02-28 20:03:01 -0800720 if (!result.isNotFound()) {
721 if (result.isDirectoryEntry()) {
Daniel Lehmann9815d7f2012-04-16 18:28:03 -0700722 if (!resultIsCached) {
723 loadDirectoryMetaData(result);
724 }
Daniel Lehmann72ff4df2012-02-28 20:03:01 -0800725 } else if (mLoadGroupMetaData) {
Daniel Lehmann9815d7f2012-04-16 18:28:03 -0700726 if (result.getGroupMetaData() == null) {
727 loadGroupMetaData(result);
728 }
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -0700729 }
Daniel Lehmann9815d7f2012-04-16 18:28:03 -0700730 if (mLoadStreamItems && result.getStreamItems() == null) {
Daniel Lehmann72ff4df2012-02-28 20:03:01 -0800731 loadStreamItems(result);
732 }
Daniel Lehmann9815d7f2012-04-16 18:28:03 -0700733 if (!resultIsCached) loadPhotoBinaryData(result);
Daniel Lehmann72ff4df2012-02-28 20:03:01 -0800734
735 // Note ME profile should never have "Add connection"
Daniel Lehmann49ea2d22012-04-17 11:17:12 -0700736 if (mLoadInvitableAccountTypes && result.getInvitableAccountTypes() == null) {
Daniel Lehmann72ff4df2012-02-28 20:03:01 -0800737 loadInvitableAccountTypes(result);
738 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700739 }
Daniel Lehmann72ff4df2012-02-28 20:03:01 -0800740 return result;
741 } catch (Exception e) {
742 Log.e(TAG, "Error loading the contact: " + mLookupUri, e);
743 return Result.forError(mRequestedUri, e);
744 }
745 }
746
747 private Result loadContactEntity(ContentResolver resolver, Uri contactUri) {
748 Uri entityUri = Uri.withAppendedPath(contactUri, Contacts.Entity.CONTENT_DIRECTORY);
749 Cursor cursor = resolver.query(entityUri, ContactQuery.COLUMNS, null, null,
750 Contacts.Entity.RAW_CONTACT_ID);
751 if (cursor == null) {
752 Log.e(TAG, "No cursor returned in loadContactEntity");
753 return Result.forNotFound(mRequestedUri);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700754 }
755
Daniel Lehmann72ff4df2012-02-28 20:03:01 -0800756 try {
757 if (!cursor.moveToFirst()) {
758 cursor.close();
Makoto Onuki2621c5b2011-10-03 12:56:16 -0700759 return Result.forNotFound(mRequestedUri);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700760 }
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700761
Maurice Chu8a0dd0f2012-04-12 16:16:32 -0700762 // Create the loaded result starting with the Contact data.
763 Result result = loadContactHeaderData(cursor, contactUri);
764
765 // Fill in the raw contacts, which is wrapped in an Entity and any
766 // status data. Initially, result has empty entities and statuses.
Daniel Lehmann72ff4df2012-02-28 20:03:01 -0800767 long currentRawContactId = -1;
768 Entity entity = null;
Daniel Lehmann72ff4df2012-02-28 20:03:01 -0800769 ArrayList<Entity> entities = result.getEntities();
770 LongSparseArray<DataStatus> statuses = result.getStatuses();
771 for (; !cursor.isAfterLast(); cursor.moveToNext()) {
772 long rawContactId = cursor.getLong(ContactQuery.RAW_CONTACT_ID);
773 if (rawContactId != currentRawContactId) {
Maurice Chu8a0dd0f2012-04-12 16:16:32 -0700774 // First time to see this raw contact id, so create a new entity, and
775 // add it to the result's entities.
Daniel Lehmann72ff4df2012-02-28 20:03:01 -0800776 currentRawContactId = rawContactId;
777 entity = new android.content.Entity(loadRawContact(cursor));
778 entities.add(entity);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700779 }
Daniel Lehmann72ff4df2012-02-28 20:03:01 -0800780 if (!cursor.isNull(ContactQuery.DATA_ID)) {
781 ContentValues data = loadData(cursor);
782 entity.addSubValue(ContactsContract.Data.CONTENT_URI, data);
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700783
Daniel Lehmann72ff4df2012-02-28 20:03:01 -0800784 if (!cursor.isNull(ContactQuery.PRESENCE)
785 || !cursor.isNull(ContactQuery.STATUS)) {
786 final DataStatus status = new DataStatus(cursor);
787 final long dataId = cursor.getLong(ContactQuery.DATA_ID);
788 statuses.put(dataId, status);
Dmitri Plotnikov4d444242010-07-30 11:39:39 -0700789 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700790 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700791 }
Daniel Lehmann72ff4df2012-02-28 20:03:01 -0800792
793 return result;
794 } finally {
795 cursor.close();
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700796 }
Daniel Lehmann72ff4df2012-02-28 20:03:01 -0800797 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -0700798
Daniel Lehmann72ff4df2012-02-28 20:03:01 -0800799 /**
800 * Looks for the photo data item in entities. If found, creates a new Bitmap instance. If
801 * not found, returns null
802 */
803 private void loadPhotoBinaryData(Result contactData) {
Dave Santoro0a2a5db2011-06-29 00:37:06 -0700804
Daniel Lehmann72ff4df2012-02-28 20:03:01 -0800805 // If we have a photo URI, try loading that first.
806 String photoUri = contactData.getPhotoUri();
807 if (photoUri != null) {
808 try {
809 AssetFileDescriptor fd = getContext().getContentResolver()
810 .openAssetFileDescriptor(Uri.parse(photoUri), "r");
811 byte[] buffer = new byte[16 * 1024];
812 FileInputStream fis = fd.createInputStream();
813 ByteArrayOutputStream baos = new ByteArrayOutputStream();
Dave Santoro0a2a5db2011-06-29 00:37:06 -0700814 try {
Daniel Lehmann72ff4df2012-02-28 20:03:01 -0800815 int size;
816 while ((size = fis.read(buffer)) != -1) {
817 baos.write(buffer, 0, size);
818 }
819 contactData.setPhotoBinaryData(baos.toByteArray());
820 } finally {
821 fis.close();
822 fd.close();
823 }
824 return;
825 } catch (IOException ioe) {
826 // Just fall back to the case below.
827 }
828 }
829
830 // If we couldn't load from a file, fall back to the data blob.
831 final long photoId = contactData.getPhotoId();
832 if (photoId <= 0) {
833 // No photo ID
834 return;
835 }
836
837 for (Entity entity : contactData.getEntities()) {
838 for (NamedContentValues subValue : entity.getSubValues()) {
839 final ContentValues entryValues = subValue.values;
840 final long dataId = entryValues.getAsLong(Data._ID);
841 if (dataId == photoId) {
842 final String mimeType = entryValues.getAsString(Data.MIMETYPE);
843 // Correct Data Id but incorrect MimeType? Don't load
844 if (!Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
845 return;
846 }
847 contactData.setPhotoBinaryData(entryValues.getAsByteArray(Photo.PHOTO));
848 break;
849 }
850 }
851 }
852 }
853
854 /**
855 * Sets the "invitable" account types to {@link Result#mInvitableAccountTypes}.
856 */
857 private void loadInvitableAccountTypes(Result contactData) {
Daniel Lehmann49ea2d22012-04-17 11:17:12 -0700858 final ArrayList<AccountType> resultList = Lists.newArrayList();
859 if (!contactData.isUserProfile()) {
860 Map<AccountTypeWithDataSet, AccountType> invitables =
861 AccountTypeManager.getInstance(getContext()).getUsableInvitableAccountTypes();
862 if (!invitables.isEmpty()) {
863 final Map<AccountTypeWithDataSet, AccountType> resultMap =
864 Maps.newHashMap(invitables);
Daniel Lehmann72ff4df2012-02-28 20:03:01 -0800865
Daniel Lehmann49ea2d22012-04-17 11:17:12 -0700866 // Remove the ones that already have a raw contact in the current contact
867 for (Entity entity : contactData.getEntities()) {
868 final ContentValues values = entity.getEntityValues();
869 final AccountTypeWithDataSet type = AccountTypeWithDataSet.get(
870 values.getAsString(RawContacts.ACCOUNT_TYPE),
871 values.getAsString(RawContacts.DATA_SET));
872 resultMap.remove(type);
873 }
Daniel Lehmann72ff4df2012-02-28 20:03:01 -0800874
Daniel Lehmann49ea2d22012-04-17 11:17:12 -0700875 resultList.addAll(resultMap.values());
876 }
Daniel Lehmann72ff4df2012-02-28 20:03:01 -0800877 }
878
879 // Set to mInvitableAccountTypes
Daniel Lehmann49ea2d22012-04-17 11:17:12 -0700880 contactData.mInvitableAccountTypes = resultList;
Daniel Lehmann72ff4df2012-02-28 20:03:01 -0800881 }
882
883 /**
884 * Extracts Contact level columns from the cursor.
885 */
886 private Result loadContactHeaderData(final Cursor cursor, Uri contactUri) {
887 final String directoryParameter =
888 contactUri.getQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY);
889 final long directoryId = directoryParameter == null
890 ? Directory.DEFAULT
891 : Long.parseLong(directoryParameter);
892 final long contactId = cursor.getLong(ContactQuery.CONTACT_ID);
893 final String lookupKey = cursor.getString(ContactQuery.LOOKUP_KEY);
894 final long nameRawContactId = cursor.getLong(ContactQuery.NAME_RAW_CONTACT_ID);
895 final int displayNameSource = cursor.getInt(ContactQuery.DISPLAY_NAME_SOURCE);
896 final String displayName = cursor.getString(ContactQuery.DISPLAY_NAME);
897 final String altDisplayName = cursor.getString(ContactQuery.ALT_DISPLAY_NAME);
898 final String phoneticName = cursor.getString(ContactQuery.PHONETIC_NAME);
899 final long photoId = cursor.getLong(ContactQuery.PHOTO_ID);
900 final String photoUri = cursor.getString(ContactQuery.PHOTO_URI);
901 final boolean starred = cursor.getInt(ContactQuery.STARRED) != 0;
902 final Integer presence = cursor.isNull(ContactQuery.CONTACT_PRESENCE)
903 ? null
904 : cursor.getInt(ContactQuery.CONTACT_PRESENCE);
905 final boolean sendToVoicemail = cursor.getInt(ContactQuery.SEND_TO_VOICEMAIL) == 1;
906 final String customRingtone = cursor.getString(ContactQuery.CUSTOM_RINGTONE);
907 final boolean isUserProfile = cursor.getInt(ContactQuery.IS_USER_PROFILE) == 1;
908
909 Uri lookupUri;
910 if (directoryId == Directory.DEFAULT || directoryId == Directory.LOCAL_INVISIBLE) {
911 lookupUri = ContentUris.withAppendedId(
912 Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey), contactId);
913 } else {
914 lookupUri = contactUri;
915 }
916
917 return new Result(mRequestedUri, contactUri, lookupUri, directoryId, lookupKey,
918 contactId, nameRawContactId, displayNameSource, photoId, photoUri, displayName,
919 altDisplayName, phoneticName, starred, presence, sendToVoicemail,
920 customRingtone, isUserProfile);
921 }
922
923 /**
924 * Extracts RawContact level columns from the cursor.
925 */
926 private ContentValues loadRawContact(Cursor cursor) {
927 ContentValues cv = new ContentValues();
928
929 cv.put(RawContacts._ID, cursor.getLong(ContactQuery.RAW_CONTACT_ID));
930
931 cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_NAME);
932 cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_TYPE);
933 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SET);
934 cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_TYPE_AND_DATA_SET);
935 cursorColumnToContentValues(cursor, cv, ContactQuery.DIRTY);
936 cursorColumnToContentValues(cursor, cv, ContactQuery.VERSION);
937 cursorColumnToContentValues(cursor, cv, ContactQuery.SOURCE_ID);
938 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC1);
939 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC2);
940 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC3);
941 cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC4);
942 cursorColumnToContentValues(cursor, cv, ContactQuery.DELETED);
943 cursorColumnToContentValues(cursor, cv, ContactQuery.CONTACT_ID);
944 cursorColumnToContentValues(cursor, cv, ContactQuery.STARRED);
945 cursorColumnToContentValues(cursor, cv, ContactQuery.NAME_VERIFIED);
946
947 return cv;
948 }
949
950 /**
951 * Extracts Data level columns from the cursor.
952 */
953 private ContentValues loadData(Cursor cursor) {
954 ContentValues cv = new ContentValues();
955
956 cv.put(Data._ID, cursor.getLong(ContactQuery.DATA_ID));
957
958 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA1);
959 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA2);
960 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA3);
961 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA4);
962 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA5);
963 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA6);
964 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA7);
965 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA8);
966 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA9);
967 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA10);
968 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA11);
969 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA12);
970 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA13);
971 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA14);
972 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA15);
973 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC1);
974 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC2);
975 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC3);
976 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC4);
977 cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_VERSION);
978 cursorColumnToContentValues(cursor, cv, ContactQuery.IS_PRIMARY);
979 cursorColumnToContentValues(cursor, cv, ContactQuery.IS_SUPERPRIMARY);
980 cursorColumnToContentValues(cursor, cv, ContactQuery.MIMETYPE);
981 cursorColumnToContentValues(cursor, cv, ContactQuery.RES_PACKAGE);
982 cursorColumnToContentValues(cursor, cv, ContactQuery.GROUP_SOURCE_ID);
983 cursorColumnToContentValues(cursor, cv, ContactQuery.CHAT_CAPABILITY);
984
985 return cv;
986 }
987
988 private void cursorColumnToContentValues(
989 Cursor cursor, ContentValues values, int index) {
990 switch (cursor.getType(index)) {
991 case Cursor.FIELD_TYPE_NULL:
992 // don't put anything in the content values
993 break;
994 case Cursor.FIELD_TYPE_INTEGER:
995 values.put(ContactQuery.COLUMNS[index], cursor.getLong(index));
996 break;
997 case Cursor.FIELD_TYPE_STRING:
998 values.put(ContactQuery.COLUMNS[index], cursor.getString(index));
999 break;
1000 case Cursor.FIELD_TYPE_BLOB:
1001 values.put(ContactQuery.COLUMNS[index], cursor.getBlob(index));
1002 break;
1003 default:
1004 throw new IllegalStateException("Invalid or unhandled data type");
1005 }
1006 }
1007
1008 private void loadDirectoryMetaData(Result result) {
1009 long directoryId = result.getDirectoryId();
1010
1011 Cursor cursor = getContext().getContentResolver().query(
1012 ContentUris.withAppendedId(Directory.CONTENT_URI, directoryId),
1013 DirectoryQuery.COLUMNS, null, null, null);
1014 if (cursor == null) {
1015 return;
1016 }
1017 try {
1018 if (cursor.moveToFirst()) {
1019 final String displayName = cursor.getString(DirectoryQuery.DISPLAY_NAME);
1020 final String packageName = cursor.getString(DirectoryQuery.PACKAGE_NAME);
1021 final int typeResourceId = cursor.getInt(DirectoryQuery.TYPE_RESOURCE_ID);
1022 final String accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE);
1023 final String accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME);
1024 final int exportSupport = cursor.getInt(DirectoryQuery.EXPORT_SUPPORT);
1025 String directoryType = null;
1026 if (!TextUtils.isEmpty(packageName)) {
1027 PackageManager pm = getContext().getPackageManager();
Dave Santoro0a2a5db2011-06-29 00:37:06 -07001028 try {
Daniel Lehmann72ff4df2012-02-28 20:03:01 -08001029 Resources resources = pm.getResourcesForApplication(packageName);
1030 directoryType = resources.getString(typeResourceId);
1031 } catch (NameNotFoundException e) {
1032 Log.w(TAG, "Contact directory resource not found: "
1033 + packageName + "." + typeResourceId);
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -08001034 }
1035 }
Daniel Lehmann72ff4df2012-02-28 20:03:01 -08001036
1037 result.setDirectoryMetaData(
1038 displayName, directoryType, accountType, accountName, exportSupport);
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -08001039 }
Daniel Lehmann72ff4df2012-02-28 20:03:01 -08001040 } finally {
1041 cursor.close();
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -08001042 }
Daniel Lehmann72ff4df2012-02-28 20:03:01 -08001043 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -08001044
Daniel Lehmann72ff4df2012-02-28 20:03:01 -08001045 /**
1046 * Loads groups meta-data for all groups associated with all constituent raw contacts'
1047 * accounts.
1048 */
1049 private void loadGroupMetaData(Result result) {
1050 StringBuilder selection = new StringBuilder();
1051 ArrayList<String> selectionArgs = new ArrayList<String>();
1052 for (Entity entity : result.mEntities) {
1053 ContentValues values = entity.getEntityValues();
1054 String accountName = values.getAsString(RawContacts.ACCOUNT_NAME);
1055 String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
1056 String dataSet = values.getAsString(RawContacts.DATA_SET);
1057 if (accountName != null && accountType != null) {
1058 if (selection.length() != 0) {
1059 selection.append(" OR ");
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -07001060 }
Daniel Lehmann72ff4df2012-02-28 20:03:01 -08001061 selection.append(
1062 "(" + Groups.ACCOUNT_NAME + "=? AND " + Groups.ACCOUNT_TYPE + "=?");
1063 selectionArgs.add(accountName);
1064 selectionArgs.add(accountType);
Dmitri Plotnikov02cd4912010-09-01 20:42:17 -07001065
Daniel Lehmann72ff4df2012-02-28 20:03:01 -08001066 if (dataSet != null) {
1067 selection.append(" AND " + Groups.DATA_SET + "=?");
1068 selectionArgs.add(dataSet);
Dave Santoroa4400d52011-09-02 16:14:53 -07001069 } else {
Daniel Lehmann72ff4df2012-02-28 20:03:01 -08001070 selection.append(" AND " + Groups.DATA_SET + " IS NULL");
1071 }
1072 selection.append(")");
1073 }
1074 }
Daniel Lehmann9815d7f2012-04-16 18:28:03 -07001075 final ArrayList<GroupMetaData> groupList = new ArrayList<GroupMetaData>();
1076 final Cursor cursor = getContext().getContentResolver().query(Groups.CONTENT_URI,
Daniel Lehmann72ff4df2012-02-28 20:03:01 -08001077 GroupQuery.COLUMNS, selection.toString(), selectionArgs.toArray(new String[0]),
1078 null);
1079 try {
1080 while (cursor.moveToNext()) {
1081 final String accountName = cursor.getString(GroupQuery.ACCOUNT_NAME);
1082 final String accountType = cursor.getString(GroupQuery.ACCOUNT_TYPE);
1083 final String dataSet = cursor.getString(GroupQuery.DATA_SET);
1084 final long groupId = cursor.getLong(GroupQuery.ID);
1085 final String title = cursor.getString(GroupQuery.TITLE);
1086 final boolean defaultGroup = cursor.isNull(GroupQuery.AUTO_ADD)
1087 ? false
1088 : cursor.getInt(GroupQuery.AUTO_ADD) != 0;
1089 final boolean favorites = cursor.isNull(GroupQuery.FAVORITES)
1090 ? false
1091 : cursor.getInt(GroupQuery.FAVORITES) != 0;
1092
Daniel Lehmann9815d7f2012-04-16 18:28:03 -07001093 groupList.add(new GroupMetaData(
Daniel Lehmann72ff4df2012-02-28 20:03:01 -08001094 accountName, accountType, dataSet, groupId, title, defaultGroup,
1095 favorites));
1096 }
1097 } finally {
1098 cursor.close();
1099 }
Daniel Lehmann9815d7f2012-04-16 18:28:03 -07001100 result.mGroups = groupList;
Daniel Lehmann72ff4df2012-02-28 20:03:01 -08001101 }
1102
1103 /**
1104 * Loads all stream items and stream item photos belonging to this contact.
1105 */
1106 private void loadStreamItems(Result result) {
1107 Cursor cursor = getContext().getContentResolver().query(
1108 Contacts.CONTENT_LOOKUP_URI.buildUpon()
1109 .appendPath(result.getLookupKey())
1110 .appendPath(Contacts.StreamItems.CONTENT_DIRECTORY).build(),
1111 null, null, null, null);
1112 LongSparseArray<StreamItemEntry> streamItemsById =
1113 new LongSparseArray<StreamItemEntry>();
1114 ArrayList<StreamItemEntry> streamItems = new ArrayList<StreamItemEntry>();
1115 try {
1116 while (cursor.moveToNext()) {
1117 StreamItemEntry streamItem = new StreamItemEntry(cursor);
1118 streamItemsById.put(streamItem.getId(), streamItem);
1119 streamItems.add(streamItem);
1120 }
1121 } finally {
1122 cursor.close();
1123 }
1124
1125 // Pre-decode all HTMLs
1126 final long start = System.currentTimeMillis();
1127 for (StreamItemEntry streamItem : streamItems) {
1128 streamItem.decodeHtml(getContext());
1129 }
1130 final long end = System.currentTimeMillis();
1131 if (DEBUG) {
1132 Log.d(TAG, "Decoded HTML for " + streamItems.size() + " items, took "
1133 + (end - start) + " ms");
1134 }
1135
1136 // Now retrieve any photo records associated with the stream items.
1137 if (!streamItems.isEmpty()) {
1138 if (result.isUserProfile()) {
1139 // If the stream items we're loading are for the profile, we can't bulk-load the
1140 // stream items with a custom selection.
1141 for (StreamItemEntry entry : streamItems) {
1142 Cursor siCursor = getContext().getContentResolver().query(
1143 Uri.withAppendedPath(
1144 ContentUris.withAppendedId(
1145 StreamItems.CONTENT_URI, entry.getId()),
1146 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY),
1147 null, null, null, null);
Dave Santoroa4400d52011-09-02 16:14:53 -07001148 try {
Daniel Lehmann72ff4df2012-02-28 20:03:01 -08001149 while (siCursor.moveToNext()) {
1150 entry.addPhoto(new StreamItemPhotoEntry(siCursor));
Dave Santoroa4400d52011-09-02 16:14:53 -07001151 }
1152 } finally {
Daniel Lehmann72ff4df2012-02-28 20:03:01 -08001153 siCursor.close();
Dave Santoroa4400d52011-09-02 16:14:53 -07001154 }
Dave Santoro39156002011-07-19 01:18:14 -07001155 }
Daniel Lehmann72ff4df2012-02-28 20:03:01 -08001156 } else {
1157 String[] streamItemIdArr = new String[streamItems.size()];
1158 StringBuilder streamItemPhotoSelection = new StringBuilder();
1159 streamItemPhotoSelection.append(StreamItemPhotos.STREAM_ITEM_ID + " IN (");
1160 for (int i = 0; i < streamItems.size(); i++) {
1161 if (i > 0) {
1162 streamItemPhotoSelection.append(",");
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001163 }
Daniel Lehmann72ff4df2012-02-28 20:03:01 -08001164 streamItemPhotoSelection.append("?");
1165 streamItemIdArr[i] = String.valueOf(streamItems.get(i).getId());
Daniel Lehmann18f104f2010-05-07 15:41:11 -07001166 }
Daniel Lehmann72ff4df2012-02-28 20:03:01 -08001167 streamItemPhotoSelection.append(")");
1168 Cursor sipCursor = getContext().getContentResolver().query(
1169 StreamItems.CONTENT_PHOTO_URI,
1170 null, streamItemPhotoSelection.toString(), streamItemIdArr,
1171 StreamItemPhotos.STREAM_ITEM_ID);
1172 try {
1173 while (sipCursor.moveToNext()) {
1174 long streamItemId = sipCursor.getLong(
1175 sipCursor.getColumnIndex(StreamItemPhotos.STREAM_ITEM_ID));
1176 StreamItemEntry streamItem = streamItemsById.get(streamItemId);
1177 streamItem.addPhoto(new StreamItemPhotoEntry(sipCursor));
1178 }
1179 } finally {
1180 sipCursor.close();
1181 }
1182 }
1183 }
Dmitri Plotnikov7f4f8d12010-11-10 10:22:19 -08001184
Daniel Lehmann72ff4df2012-02-28 20:03:01 -08001185 // Set the sorted stream items on the result.
1186 Collections.sort(streamItems);
Daniel Lehmann9815d7f2012-04-16 18:28:03 -07001187 result.mStreamItems = streamItems;
Daniel Lehmann72ff4df2012-02-28 20:03:01 -08001188 }
1189
1190 @Override
1191 public void deliverResult(Result result) {
1192 unregisterObserver();
1193
1194 // The creator isn't interested in any further updates
1195 if (isReset() || result == null) {
1196 return;
1197 }
1198
1199 mContact = result;
1200
1201 if (result.isLoaded()) {
1202 mLookupUri = result.getLookupUri();
1203
1204 if (!result.isDirectoryEntry()) {
1205 Log.i(TAG, "Registering content observer for " + mLookupUri);
1206 if (mObserver == null) {
1207 mObserver = new ForceLoadContentObserver();
1208 }
1209 getContext().getContentResolver().registerContentObserver(
1210 mLookupUri, true, mObserver);
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001211 }
Dmitri Plotnikovc3f2a522010-11-17 18:36:17 -08001212
Daniel Lehmann72ff4df2012-02-28 20:03:01 -08001213 // inform the source of the data that this contact is being looked at
1214 postViewNotificationToSyncAdapter();
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001215 }
Daniel Lehmann72ff4df2012-02-28 20:03:01 -08001216
1217 super.deliverResult(mContact);
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001218 }
1219
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001220 /**
1221 * Posts a message to the contributing sync adapters that have opted-in, notifying them
1222 * that the contact has just been loaded
1223 */
1224 private void postViewNotificationToSyncAdapter() {
1225 Context context = getContext();
1226 for (Entity entity : mContact.getEntities()) {
1227 final ContentValues entityValues = entity.getEntityValues();
Makoto Onukiaba2b832011-08-12 15:44:53 -07001228 final long rawContactId = entityValues.getAsLong(RawContacts.Entity._ID);
1229 if (mNotifiedRawContactIds.contains(rawContactId)) {
1230 continue; // Already notified for this raw contact.
1231 }
1232 mNotifiedRawContactIds.add(rawContactId);
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001233 final String type = entityValues.getAsString(RawContacts.ACCOUNT_TYPE);
1234 final String dataSet = entityValues.getAsString(RawContacts.DATA_SET);
Flavio Lerda59a887e2011-08-14 18:13:17 +01001235 final AccountType accountType = AccountTypeManager.getInstance(context).getAccountType(
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001236 type, dataSet);
1237 final String serviceName = accountType.getViewContactNotifyServiceClassName();
1238 final String resPackageName = accountType.resPackageName;
1239 if (!TextUtils.isEmpty(serviceName) && !TextUtils.isEmpty(resPackageName)) {
Daniel Lehmann3ef27fb2011-08-09 14:31:29 -07001240 final Uri uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
1241 final Intent intent = new Intent();
1242 intent.setClassName(resPackageName, serviceName);
1243 intent.setAction(Intent.ACTION_VIEW);
1244 intent.setDataAndType(uri, RawContacts.CONTENT_ITEM_TYPE);
1245 try {
1246 context.startService(intent);
1247 } catch (Exception e) {
1248 Log.e(TAG, "Error sending message to source-app", e);
1249 }
1250 }
1251 }
1252 }
1253
Daniel Lehmann3a120772010-06-21 16:21:35 -07001254 private void unregisterObserver() {
1255 if (mObserver != null) {
1256 getContext().getContentResolver().unregisterContentObserver(mObserver);
1257 mObserver = null;
1258 }
1259 }
1260
Daniel Lehmann2a45e352012-02-13 15:03:58 -08001261 /**
1262 * Sets whether to load stream items. Will trigger a reload if the value has changed.
1263 * At the moment, this is only used for debugging purposes
1264 */
1265 public void setLoadStreamItems(boolean value) {
1266 if (mLoadStreamItems != value) {
1267 mLoadStreamItems = value;
1268 onContentChanged();
1269 }
1270 }
1271
Daniel Lehmann9815d7f2012-04-16 18:28:03 -07001272 /**
1273 * Fully upgrades this ContactLoader to one with all lists fully loaded. When done, the
1274 * new result will be delivered
1275 */
1276 public void upgradeToFullContact() {
1277 mLoadGroupMetaData = true;
1278 mLoadInvitableAccountTypes = true;
1279 mLoadStreamItems = true;
1280
1281 // Cache the current result, so that we only load the "missing" parts of the contact.
1282 cacheResult();
1283
1284 // Our load parameters have changed, so let's pretend the data has changed. Its the same
1285 // thing, essentially.
1286 onContentChanged();
1287 }
1288
Daniel Lehmann2a45e352012-02-13 15:03:58 -08001289 public boolean getLoadStreamItems() {
1290 return mLoadStreamItems;
1291 }
1292
Dmitri Plotnikov5a30d9a2010-11-23 14:59:50 -08001293 public Uri getLookupUri() {
1294 return mLookupUri;
1295 }
1296
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001297 @Override
Dianne Hackbornc04fc272010-12-20 23:13:10 -08001298 protected void onStartLoading() {
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001299 if (mContact != null) {
Daniel Lehmanncbcc4492010-04-12 18:03:54 -07001300 deliverResult(mContact);
Dmitri Plotnikov97e90c62011-01-03 11:58:13 -08001301 }
1302
1303 if (takeContentChanged() || mContact == null) {
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001304 forceLoad();
1305 }
1306 }
1307
1308 @Override
Daniel Lehmann72ff4df2012-02-28 20:03:01 -08001309 protected void onStopLoading() {
1310 cancelLoad();
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001311 }
1312
1313 @Override
Dianne Hackbornc04fc272010-12-20 23:13:10 -08001314 protected void onReset() {
Daniel Lehmann72ff4df2012-02-28 20:03:01 -08001315 super.onReset();
1316 cancelLoad();
Dianne Hackborn4ef95cc2010-12-16 00:44:33 -08001317 unregisterObserver();
1318 mContact = null;
Dianne Hackborn4ef95cc2010-12-16 00:44:33 -08001319 }
Daniel Lehmann9815d7f2012-04-16 18:28:03 -07001320
1321 /**
1322 * Caches the result, which is useful when we switch from activity to activity, using the same
1323 * contact. If the next load is for a different contact, the cached result will be dropped
1324 */
1325 public void cacheResult() {
1326 sCachedResult = mContact;
1327 }
Daniel Lehmann4cd94412010-04-08 16:44:36 -07001328}