blob: e43d02b0742d5285a62dd606a1c188aa15773181 [file] [log] [blame]
Gary Maia6c80b32016-09-30 16:34:55 -07001package com.android.contacts.editor;
2
Gary Maia1721802016-11-01 17:55:31 -07003import android.content.AsyncTaskLoader;
4import android.content.ContentResolver;
Gary Maia6c80b32016-09-30 16:34:55 -07005import android.content.Context;
Gary Maia6c80b32016-09-30 16:34:55 -07006import android.database.Cursor;
7import android.net.Uri;
Gary Maia1721802016-11-01 17:55:31 -07008import android.os.Parcel;
9import android.os.Parcelable;
Gary Maiba10be22016-10-12 17:41:15 -070010import android.provider.ContactsContract;
Gary Maia6c80b32016-09-30 16:34:55 -070011import android.provider.ContactsContract.Contacts;
Gary Mai3a533282016-11-08 19:02:26 +000012import android.provider.ContactsContract.Data;
13import android.provider.ContactsContract.Profile;
Gary Maia6c80b32016-09-30 16:34:55 -070014import android.provider.ContactsContract.RawContacts;
15
Gary Mai69c182a2016-12-05 13:07:03 -080016import com.android.contacts.model.AccountTypeManager;
17import com.android.contacts.model.account.AccountType;
Gary Maia1721802016-11-01 17:55:31 -070018
19import java.util.ArrayList;
Gary Mai3a533282016-11-08 19:02:26 +000020import java.util.HashMap;
21import java.util.Map;
Gary Maia1721802016-11-01 17:55:31 -070022
Gary Maia6c80b32016-09-30 16:34:55 -070023/**
24 * Loader for the pick a raw contact to edit activity. Loads all raw contact metadata for the
25 * given Contact {@link Uri}.
26 */
Gary Maia1721802016-11-01 17:55:31 -070027public class PickRawContactLoader extends
28 AsyncTaskLoader<PickRawContactLoader.RawContactsMetadata> {
Gary Maia6c80b32016-09-30 16:34:55 -070029 private Uri mContactUri;
Gary Maia1721802016-11-01 17:55:31 -070030 private RawContactsMetadata mCachedResult;
Gary Maia6c80b32016-09-30 16:34:55 -070031
Gary Maia1721802016-11-01 17:55:31 -070032 private static final String[] RAW_CONTACT_PROJECTION = new String[] {
Gary Maia6c80b32016-09-30 16:34:55 -070033 RawContacts.ACCOUNT_NAME,
34 RawContacts.ACCOUNT_TYPE,
35 RawContacts.DATA_SET,
36 RawContacts._ID,
37 RawContacts.DISPLAY_NAME_PRIMARY,
38 RawContacts.DISPLAY_NAME_ALTERNATIVE
39 };
40
Gary Maia1721802016-11-01 17:55:31 -070041 private static final String RAW_CONTACT_SELECTION = RawContacts.CONTACT_ID + "=?";
Gary Maia6c80b32016-09-30 16:34:55 -070042
Gary Maia1721802016-11-01 17:55:31 -070043 private static final int ACCOUNT_NAME = 0;
44 private static final int ACCOUNT_TYPE = 1;
45 private static final int DATA_SET = 2;
46 private static final int RAW_CONTACT_ID = 3;
47 private static final int DISPLAY_NAME_PRIMARY = 4;
48 private static final int DISPLAY_NAME_ALTERNATIVE = 5;
Gary Maia6c80b32016-09-30 16:34:55 -070049
Gary Mai3a533282016-11-08 19:02:26 +000050 private static final String PHOTO_SELECTION_PREFIX =
51 ContactsContract.Data.RAW_CONTACT_ID + " IN (";
52 private static final String PHOTO_SELECTION_SUFFIX = ") AND " + ContactsContract.Data.MIMETYPE
53 + "=\"" + ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE + "\"";
54
Gary Maia6c80b32016-09-30 16:34:55 -070055 public PickRawContactLoader(Context context, Uri contactUri) {
Gary Maia1721802016-11-01 17:55:31 -070056 super(context);
57 mContactUri = ensureIsContactUri(contactUri);
Gary Maia6c80b32016-09-30 16:34:55 -070058 }
59
60 @Override
Gary Maia1721802016-11-01 17:55:31 -070061 public RawContactsMetadata loadInBackground() {
62 final ContentResolver resolver = getContext().getContentResolver();
Gary Maia6c80b32016-09-30 16:34:55 -070063 // Get the id of the contact we're looking at.
Gary Maia1721802016-11-01 17:55:31 -070064 final Cursor contactCursor = resolver.query(
Gary Mai3a533282016-11-08 19:02:26 +000065 mContactUri, new String[] {Contacts._ID, Contacts.IS_USER_PROFILE}, null,
Gary Maia6c80b32016-09-30 16:34:55 -070066 null, null);
67
Gary Maia1721802016-11-01 17:55:31 -070068 if (contactCursor == null) {
Gary Maia6c80b32016-09-30 16:34:55 -070069 return null;
70 }
71
Gary Maia1721802016-11-01 17:55:31 -070072 if (contactCursor.getCount() < 1) {
73 contactCursor.close();
Gary Maia6c80b32016-09-30 16:34:55 -070074 return null;
75 }
76
Gary Maia1721802016-11-01 17:55:31 -070077 final RawContactsMetadata result = new RawContactsMetadata();
Gary Maia1721802016-11-01 17:55:31 -070078 try {
79 contactCursor.moveToFirst();
Gary Maib9065dd2016-11-08 10:49:00 -080080 result.contactId = contactCursor.getLong(/* Contacts._ID */ 0);
Gary Maia1721802016-11-01 17:55:31 -070081 result.isUserProfile = contactCursor.getInt(/* Contacts.IS_USER_PROFILE */ 1) == 1;
82 } finally {
83 contactCursor.close();
84 }
Gary Maiba10be22016-10-12 17:41:15 -070085
Gary Maia1721802016-11-01 17:55:31 -070086 // Load RawContact data
87 final Uri rawContactUri;
88 if (result.isUserProfile) {
89 rawContactUri = ContactsContract.Profile.CONTENT_RAW_CONTACTS_URI;
Gary Maiba10be22016-10-12 17:41:15 -070090 } else {
Gary Maia1721802016-11-01 17:55:31 -070091 rawContactUri = RawContacts.CONTENT_URI;
Gary Maiba10be22016-10-12 17:41:15 -070092 }
Gary Maia1721802016-11-01 17:55:31 -070093
94 final Cursor rawContactCursor = resolver.query(
95 rawContactUri, RAW_CONTACT_PROJECTION, RAW_CONTACT_SELECTION,
Gary Maib9065dd2016-11-08 10:49:00 -080096 new String[] {Long.toString(result.contactId)}, null);
Gary Maia1721802016-11-01 17:55:31 -070097
98 if (rawContactCursor == null) {
99 return null;
100 }
101
Gary Maicea6f3d2016-12-09 11:32:31 -0800102 if (rawContactCursor.getCount() < 1) {
103 rawContactCursor.close();
104 return null;
105 }
106
Gary Maia1721802016-11-01 17:55:31 -0700107 rawContactCursor.moveToPosition(-1);
Gary Mai3a533282016-11-08 19:02:26 +0000108 final StringBuilder photoSelection = new StringBuilder(PHOTO_SELECTION_PREFIX);
109 final Map<Long, RawContact> rawContactMap = new HashMap<>();
Gary Maia1721802016-11-01 17:55:31 -0700110 try {
111 while (rawContactCursor.moveToNext()) {
112 RawContact rawContact = new RawContact();
113 rawContact.id = rawContactCursor.getLong(RAW_CONTACT_ID);
Gary Mai3a533282016-11-08 19:02:26 +0000114 photoSelection.append(rawContact.id).append(',');
Gary Maia1721802016-11-01 17:55:31 -0700115 rawContact.displayName = rawContactCursor.getString(DISPLAY_NAME_PRIMARY);
116 rawContact.displayNameAlt = rawContactCursor.getString(DISPLAY_NAME_ALTERNATIVE);
117 rawContact.accountName = rawContactCursor.getString(ACCOUNT_NAME);
118 rawContact.accountType = rawContactCursor.getString(ACCOUNT_TYPE);
119 rawContact.accountDataSet = rawContactCursor.getString(DATA_SET);
120 result.rawContacts.add(rawContact);
Gary Mai3a533282016-11-08 19:02:26 +0000121 rawContactMap.put(rawContact.id, rawContact);
Gary Maia1721802016-11-01 17:55:31 -0700122 }
123 } finally {
124 rawContactCursor.close();
125 }
Gary Mai3a533282016-11-08 19:02:26 +0000126
127 // Remove the last ','
128 if (photoSelection.length() > 0) {
129 photoSelection.deleteCharAt(photoSelection.length() - 1);
130 }
131 photoSelection.append(PHOTO_SELECTION_SUFFIX);
132
133 final Uri dataUri = result.isUserProfile
134 ? Uri.withAppendedPath(Profile.CONTENT_URI, Data.CONTENT_URI.getPath())
135 : Data.CONTENT_URI;
136 final Cursor photoCursor = resolver.query(
137 dataUri,
138 new String[] {Data.RAW_CONTACT_ID, Contacts.Photo._ID},
139 photoSelection.toString(), null, null);
140
141 if (photoCursor != null) {
142 try {
143 photoCursor.moveToPosition(-1);
144 while (photoCursor.moveToNext()) {
145 final long rawContactId = photoCursor.getLong(/* Data.RAW_CONTACT_ID */ 0);
146 rawContactMap.get(rawContactId).photoId =
147 photoCursor.getLong(/* PHOTO._ID */ 1);
148 }
149 } finally {
150 photoCursor.close();
151 }
152 }
Gary Maia1721802016-11-01 17:55:31 -0700153 return result;
Gary Maia6c80b32016-09-30 16:34:55 -0700154 }
155
Gary Maia1721802016-11-01 17:55:31 -0700156 @Override
157 public void deliverResult(RawContactsMetadata data) {
158 mCachedResult = data;
159 if (isStarted()) {
160 super.deliverResult(data);
161 }
162 }
163
164 @Override
165 protected void onStartLoading() {
166 super.onStartLoading();
167 if (mCachedResult == null) {
168 forceLoad();
169 } else {
170 deliverResult(mCachedResult);
171 }
Gary Maiba10be22016-10-12 17:41:15 -0700172 }
173
Gary Maia6c80b32016-09-30 16:34:55 -0700174 /**
175 * Ensures that this is a valid contact URI. If invalid, then an exception is
176 * thrown. Otherwise, the original URI is returned.
177 */
178 private static Uri ensureIsContactUri(final Uri uri) {
179 if (uri == null) {
180 throw new IllegalArgumentException("Uri must not be null");
181 }
182 if (!uri.toString().startsWith(Contacts.CONTENT_URI.toString())) {
183 throw new IllegalArgumentException("Invalid contact Uri: " + uri);
184 }
185 return uri;
186 }
Gary Maia1721802016-11-01 17:55:31 -0700187
188 public static class RawContactsMetadata implements Parcelable {
189 public static final Parcelable.Creator<RawContactsMetadata> CREATOR =
190 new Parcelable.Creator<RawContactsMetadata>() {
191 @Override
192 public RawContactsMetadata createFromParcel(Parcel source) {
193 return new RawContactsMetadata(source);
194 }
195
196 @Override
197 public RawContactsMetadata[] newArray(int size) {
198 return new RawContactsMetadata[size];
199 }
200 };
201
Gary Maib9065dd2016-11-08 10:49:00 -0800202 public long contactId;
Gary Maia1721802016-11-01 17:55:31 -0700203 public boolean isUserProfile;
204 public boolean showReadOnly = false;
205 public ArrayList<RawContact> rawContacts = new ArrayList<>();
206
207 public RawContactsMetadata() {}
208
209 private RawContactsMetadata(Parcel in) {
Gary Maib9065dd2016-11-08 10:49:00 -0800210 contactId = in.readLong();
Gary Maia1721802016-11-01 17:55:31 -0700211 isUserProfile = in.readInt() == 1;
212 showReadOnly = in.readInt() == 1;
213 in.readTypedList(rawContacts, RawContact.CREATOR);
214 }
215
216 /**
217 * Removes all read-only raw contacts.
218 */
219 public void trimReadOnly(AccountTypeManager accountManager) {
220 for (int i = rawContacts.size() - 1; i >= 0 ; i--) {
221 final RawContact rawContact = rawContacts.get(i);
222 final AccountType account = accountManager.getAccountType(
223 rawContact.accountType, rawContact.accountDataSet);
224 if (!account.areContactsWritable()) {
225 rawContacts.remove(i);
226 }
227 }
228 }
229
230 /**
231 * Returns the index of the first writable account in this contact or -1 if none exist.
232 */
233 public int getIndexOfFirstWritableAccount(AccountTypeManager accountManager) {
234 for (int i = 0; i < rawContacts.size(); i++) {
235 final RawContact rawContact = rawContacts.get(i);
236 final AccountType account = accountManager.getAccountType(
237 rawContact.accountType, rawContact.accountDataSet);
238 if (account.areContactsWritable()) {
239 return i;
240 }
241 }
242
243 return -1;
244 }
245
246 @Override
247 public int describeContents() {
248 return 0;
249 }
250
251 @Override
252 public void writeToParcel(Parcel dest, int flags) {
Gary Maib9065dd2016-11-08 10:49:00 -0800253 dest.writeLong(contactId);
Gary Maia1721802016-11-01 17:55:31 -0700254 dest.writeInt(isUserProfile ? 1 : 0);
255 dest.writeInt(showReadOnly ? 1 : 0);
256 dest.writeTypedList(rawContacts);
257 }
258 }
259
260 public static class RawContact implements Parcelable {
261 public static final Parcelable.Creator<RawContact> CREATOR =
262 new Parcelable.Creator<RawContact>() {
263 @Override
264 public RawContact createFromParcel(Parcel source) {
265 return new RawContact(source);
266 }
267
268 @Override
269 public RawContact[] newArray(int size) {
270 return new RawContact[size];
271 }
272 };
273
274 public long id;
Gary Mai3a533282016-11-08 19:02:26 +0000275 public long photoId;
Gary Maia1721802016-11-01 17:55:31 -0700276 public String displayName;
277 public String displayNameAlt;
278 public String accountName;
279 public String accountType;
280 public String accountDataSet;
281
282 public RawContact() {}
283
284 private RawContact(Parcel in) {
285 id = in.readLong();
Gary Mai3a533282016-11-08 19:02:26 +0000286 photoId = in.readLong();
Gary Maia1721802016-11-01 17:55:31 -0700287 displayName = in.readString();
288 displayNameAlt = in.readString();
289 accountName = in.readString();
290 accountType = in.readString();
291 accountDataSet = in.readString();
292 }
293
294 @Override
295 public int describeContents() {
296 return 0;
297 }
298
299 @Override
300 public void writeToParcel(Parcel dest, int flags) {
301 dest.writeLong(id);
Gary Mai3a533282016-11-08 19:02:26 +0000302 dest.writeLong(photoId);
Gary Maia1721802016-11-01 17:55:31 -0700303 dest.writeString(displayName);
304 dest.writeString(displayNameAlt);
305 dest.writeString(accountName);
306 dest.writeString(accountType);
307 dest.writeString(accountDataSet);
308 }
309 }
Gary Maia6c80b32016-09-30 16:34:55 -0700310}