• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.android.contacts.editor;
2 
3 import android.content.AsyncTaskLoader;
4 import android.content.ContentResolver;
5 import android.content.Context;
6 import android.database.Cursor;
7 import android.net.Uri;
8 import android.os.Parcel;
9 import android.os.Parcelable;
10 import android.provider.ContactsContract;
11 import android.provider.ContactsContract.Contacts;
12 import android.provider.ContactsContract.Data;
13 import android.provider.ContactsContract.Profile;
14 import android.provider.ContactsContract.RawContacts;
15 
16 import com.android.contacts.model.AccountTypeManager;
17 import com.android.contacts.model.account.AccountType;
18 
19 import java.util.ArrayList;
20 import java.util.HashMap;
21 import java.util.Map;
22 
23 /**
24  * Loader for the pick a raw contact to edit activity. Loads all raw contact metadata for the
25  * given Contact {@link Uri}.
26  */
27 public class PickRawContactLoader extends
28         AsyncTaskLoader<PickRawContactLoader.RawContactsMetadata> {
29     private Uri mContactUri;
30     private RawContactsMetadata mCachedResult;
31 
32     private static final String[] RAW_CONTACT_PROJECTION = new String[] {
33             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 
41     private static final String RAW_CONTACT_SELECTION = RawContacts.CONTACT_ID + "=?";
42 
43     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;
49 
50     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 
PickRawContactLoader(Context context, Uri contactUri)55     public PickRawContactLoader(Context context, Uri contactUri) {
56         super(context);
57         mContactUri = ensureIsContactUri(contactUri);
58     }
59 
60     @Override
loadInBackground()61     public RawContactsMetadata loadInBackground() {
62         final ContentResolver resolver = getContext().getContentResolver();
63         // Get the id of the contact we're looking at.
64         final Cursor contactCursor = resolver.query(
65                 mContactUri, new String[] {Contacts._ID, Contacts.IS_USER_PROFILE}, null,
66                 null, null);
67 
68         if (contactCursor == null) {
69             return null;
70         }
71 
72         if (contactCursor.getCount() < 1) {
73             contactCursor.close();
74             return null;
75         }
76 
77         final RawContactsMetadata result = new RawContactsMetadata();
78         try {
79             contactCursor.moveToFirst();
80             result.contactId = contactCursor.getLong(/* Contacts._ID */ 0);
81             result.isUserProfile = contactCursor.getInt(/* Contacts.IS_USER_PROFILE */ 1) == 1;
82         } finally {
83             contactCursor.close();
84         }
85 
86         // Load RawContact data
87         final Uri rawContactUri;
88         if (result.isUserProfile) {
89             rawContactUri = ContactsContract.Profile.CONTENT_RAW_CONTACTS_URI;
90         } else {
91             rawContactUri = RawContacts.CONTENT_URI;
92         }
93 
94         final Cursor rawContactCursor = resolver.query(
95                 rawContactUri, RAW_CONTACT_PROJECTION, RAW_CONTACT_SELECTION,
96                 new String[] {Long.toString(result.contactId)}, null);
97 
98         if (rawContactCursor == null) {
99             return null;
100         }
101 
102         if (rawContactCursor.getCount() < 1) {
103             rawContactCursor.close();
104             return null;
105         }
106 
107         rawContactCursor.moveToPosition(-1);
108         final StringBuilder photoSelection = new StringBuilder(PHOTO_SELECTION_PREFIX);
109         final Map<Long, RawContact> rawContactMap = new HashMap<>();
110         try {
111             while (rawContactCursor.moveToNext()) {
112                 RawContact rawContact = new RawContact();
113                 rawContact.id = rawContactCursor.getLong(RAW_CONTACT_ID);
114                 photoSelection.append(rawContact.id).append(',');
115                 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);
121                 rawContactMap.put(rawContact.id, rawContact);
122             }
123         } finally {
124             rawContactCursor.close();
125         }
126 
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         }
153         return result;
154     }
155 
156     @Override
deliverResult(RawContactsMetadata data)157     public void deliverResult(RawContactsMetadata data) {
158         mCachedResult = data;
159         if (isStarted()) {
160             super.deliverResult(data);
161         }
162     }
163 
164     @Override
onStartLoading()165     protected void onStartLoading() {
166         super.onStartLoading();
167         if (mCachedResult == null) {
168             forceLoad();
169         } else {
170             deliverResult(mCachedResult);
171         }
172     }
173 
174     /**
175      * Ensures that this is a valid contact URI. If invalid, then an exception is
176      * thrown. Otherwise, the original URI is returned.
177      */
ensureIsContactUri(final Uri uri)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     }
187 
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 
202         public long contactId;
203         public boolean isUserProfile;
204         public boolean showReadOnly = false;
205         public ArrayList<RawContact> rawContacts = new ArrayList<>();
206 
RawContactsMetadata()207         public RawContactsMetadata() {}
208 
RawContactsMetadata(Parcel in)209         private RawContactsMetadata(Parcel in) {
210             contactId = in.readLong();
211             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          */
trimReadOnly(AccountTypeManager accountManager)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          */
getIndexOfFirstWritableAccount(AccountTypeManager accountManager)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
describeContents()247         public int describeContents() {
248             return 0;
249         }
250 
251         @Override
writeToParcel(Parcel dest, int flags)252         public void writeToParcel(Parcel dest, int flags) {
253             dest.writeLong(contactId);
254             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;
275         public long photoId;
276         public String displayName;
277         public String displayNameAlt;
278         public String accountName;
279         public String accountType;
280         public String accountDataSet;
281 
RawContact()282         public RawContact() {}
283 
RawContact(Parcel in)284         private RawContact(Parcel in) {
285             id = in.readLong();
286             photoId = in.readLong();
287             displayName = in.readString();
288             displayNameAlt = in.readString();
289             accountName = in.readString();
290             accountType = in.readString();
291             accountDataSet = in.readString();
292         }
293 
294         @Override
describeContents()295         public int describeContents() {
296             return 0;
297         }
298 
299         @Override
writeToParcel(Parcel dest, int flags)300         public void writeToParcel(Parcel dest, int flags) {
301             dest.writeLong(id);
302             dest.writeLong(photoId);
303             dest.writeString(displayName);
304             dest.writeString(displayNameAlt);
305             dest.writeString(accountName);
306             dest.writeString(accountType);
307             dest.writeString(accountDataSet);
308         }
309     }
310 }
311