| package com.android.contacts.editor; |
| |
| import android.content.AsyncTaskLoader; |
| import android.content.ContentResolver; |
| import android.content.Context; |
| import android.database.Cursor; |
| import android.net.Uri; |
| import android.os.Parcel; |
| import android.os.Parcelable; |
| import android.provider.ContactsContract; |
| import android.provider.ContactsContract.Contacts; |
| import android.provider.ContactsContract.Data; |
| import android.provider.ContactsContract.Profile; |
| import android.provider.ContactsContract.RawContacts; |
| |
| import com.android.contacts.model.AccountTypeManager; |
| import com.android.contacts.model.account.AccountType; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| /** |
| * Loader for the pick a raw contact to edit activity. Loads all raw contact metadata for the |
| * given Contact {@link Uri}. |
| */ |
| public class PickRawContactLoader extends |
| AsyncTaskLoader<PickRawContactLoader.RawContactsMetadata> { |
| private Uri mContactUri; |
| private RawContactsMetadata mCachedResult; |
| |
| private static final String[] RAW_CONTACT_PROJECTION = new String[] { |
| RawContacts.ACCOUNT_NAME, |
| RawContacts.ACCOUNT_TYPE, |
| RawContacts.DATA_SET, |
| RawContacts._ID, |
| RawContacts.DISPLAY_NAME_PRIMARY, |
| RawContacts.DISPLAY_NAME_ALTERNATIVE |
| }; |
| |
| private static final String RAW_CONTACT_SELECTION = RawContacts.CONTACT_ID + "=?"; |
| |
| private static final int ACCOUNT_NAME = 0; |
| private static final int ACCOUNT_TYPE = 1; |
| private static final int DATA_SET = 2; |
| private static final int RAW_CONTACT_ID = 3; |
| private static final int DISPLAY_NAME_PRIMARY = 4; |
| private static final int DISPLAY_NAME_ALTERNATIVE = 5; |
| |
| private static final String PHOTO_SELECTION_PREFIX = |
| ContactsContract.Data.RAW_CONTACT_ID + " IN ("; |
| private static final String PHOTO_SELECTION_SUFFIX = ") AND " + ContactsContract.Data.MIMETYPE |
| + "=\"" + ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE + "\""; |
| |
| public PickRawContactLoader(Context context, Uri contactUri) { |
| super(context); |
| mContactUri = ensureIsContactUri(contactUri); |
| } |
| |
| @Override |
| public RawContactsMetadata loadInBackground() { |
| final ContentResolver resolver = getContext().getContentResolver(); |
| // Get the id of the contact we're looking at. |
| final Cursor contactCursor = resolver.query( |
| mContactUri, new String[] {Contacts._ID, Contacts.IS_USER_PROFILE}, null, |
| null, null); |
| |
| if (contactCursor == null) { |
| return null; |
| } |
| |
| if (contactCursor.getCount() < 1) { |
| contactCursor.close(); |
| return null; |
| } |
| |
| final RawContactsMetadata result = new RawContactsMetadata(); |
| try { |
| contactCursor.moveToFirst(); |
| result.contactId = contactCursor.getLong(/* Contacts._ID */ 0); |
| result.isUserProfile = contactCursor.getInt(/* Contacts.IS_USER_PROFILE */ 1) == 1; |
| } finally { |
| contactCursor.close(); |
| } |
| |
| // Load RawContact data |
| final Uri rawContactUri; |
| if (result.isUserProfile) { |
| rawContactUri = ContactsContract.Profile.CONTENT_RAW_CONTACTS_URI; |
| } else { |
| rawContactUri = RawContacts.CONTENT_URI; |
| } |
| |
| final Cursor rawContactCursor = resolver.query( |
| rawContactUri, RAW_CONTACT_PROJECTION, RAW_CONTACT_SELECTION, |
| new String[] {Long.toString(result.contactId)}, null); |
| |
| if (rawContactCursor == null) { |
| return null; |
| } |
| |
| if (rawContactCursor.getCount() < 1) { |
| rawContactCursor.close(); |
| return null; |
| } |
| |
| rawContactCursor.moveToPosition(-1); |
| final StringBuilder photoSelection = new StringBuilder(PHOTO_SELECTION_PREFIX); |
| final Map<Long, RawContact> rawContactMap = new HashMap<>(); |
| try { |
| while (rawContactCursor.moveToNext()) { |
| RawContact rawContact = new RawContact(); |
| rawContact.id = rawContactCursor.getLong(RAW_CONTACT_ID); |
| photoSelection.append(rawContact.id).append(','); |
| rawContact.displayName = rawContactCursor.getString(DISPLAY_NAME_PRIMARY); |
| rawContact.displayNameAlt = rawContactCursor.getString(DISPLAY_NAME_ALTERNATIVE); |
| rawContact.accountName = rawContactCursor.getString(ACCOUNT_NAME); |
| rawContact.accountType = rawContactCursor.getString(ACCOUNT_TYPE); |
| rawContact.accountDataSet = rawContactCursor.getString(DATA_SET); |
| result.rawContacts.add(rawContact); |
| rawContactMap.put(rawContact.id, rawContact); |
| } |
| } finally { |
| rawContactCursor.close(); |
| } |
| |
| // Remove the last ',' |
| if (photoSelection.length() > 0) { |
| photoSelection.deleteCharAt(photoSelection.length() - 1); |
| } |
| photoSelection.append(PHOTO_SELECTION_SUFFIX); |
| |
| final Uri dataUri = result.isUserProfile |
| ? Uri.withAppendedPath(Profile.CONTENT_URI, Data.CONTENT_URI.getPath()) |
| : Data.CONTENT_URI; |
| final Cursor photoCursor = resolver.query( |
| dataUri, |
| new String[] {Data.RAW_CONTACT_ID, Contacts.Photo._ID}, |
| photoSelection.toString(), null, null); |
| |
| if (photoCursor != null) { |
| try { |
| photoCursor.moveToPosition(-1); |
| while (photoCursor.moveToNext()) { |
| final long rawContactId = photoCursor.getLong(/* Data.RAW_CONTACT_ID */ 0); |
| rawContactMap.get(rawContactId).photoId = |
| photoCursor.getLong(/* PHOTO._ID */ 1); |
| } |
| } finally { |
| photoCursor.close(); |
| } |
| } |
| return result; |
| } |
| |
| @Override |
| public void deliverResult(RawContactsMetadata data) { |
| mCachedResult = data; |
| if (isStarted()) { |
| super.deliverResult(data); |
| } |
| } |
| |
| @Override |
| protected void onStartLoading() { |
| super.onStartLoading(); |
| if (mCachedResult == null) { |
| forceLoad(); |
| } else { |
| deliverResult(mCachedResult); |
| } |
| } |
| |
| /** |
| * Ensures that this is a valid contact URI. If invalid, then an exception is |
| * thrown. Otherwise, the original URI is returned. |
| */ |
| private static Uri ensureIsContactUri(final Uri uri) { |
| if (uri == null) { |
| throw new IllegalArgumentException("Uri must not be null"); |
| } |
| if (!uri.toString().startsWith(Contacts.CONTENT_URI.toString()) && |
| !uri.toString().equals(Profile.CONTENT_URI.toString())) { |
| throw new IllegalArgumentException("Invalid contact Uri: " + uri); |
| } |
| return uri; |
| } |
| |
| public static class RawContactsMetadata implements Parcelable { |
| public static final Parcelable.Creator<RawContactsMetadata> CREATOR = |
| new Parcelable.Creator<RawContactsMetadata>() { |
| @Override |
| public RawContactsMetadata createFromParcel(Parcel source) { |
| return new RawContactsMetadata(source); |
| } |
| |
| @Override |
| public RawContactsMetadata[] newArray(int size) { |
| return new RawContactsMetadata[size]; |
| } |
| }; |
| |
| public long contactId; |
| public boolean isUserProfile; |
| public boolean showReadOnly = false; |
| public ArrayList<RawContact> rawContacts = new ArrayList<>(); |
| |
| public RawContactsMetadata() {} |
| |
| private RawContactsMetadata(Parcel in) { |
| contactId = in.readLong(); |
| isUserProfile = in.readInt() == 1; |
| showReadOnly = in.readInt() == 1; |
| in.readTypedList(rawContacts, RawContact.CREATOR); |
| } |
| |
| /** |
| * Removes all read-only raw contacts. |
| */ |
| public void trimReadOnly(AccountTypeManager accountManager) { |
| for (int i = rawContacts.size() - 1; i >= 0 ; i--) { |
| final RawContact rawContact = rawContacts.get(i); |
| final AccountType account = accountManager.getAccountType( |
| rawContact.accountType, rawContact.accountDataSet); |
| if (!account.areContactsWritable()) { |
| rawContacts.remove(i); |
| } |
| } |
| } |
| |
| /** |
| * Returns the index of the first writable account in this contact or -1 if none exist. |
| */ |
| public int getIndexOfFirstWritableAccount(AccountTypeManager accountManager) { |
| for (int i = 0; i < rawContacts.size(); i++) { |
| final RawContact rawContact = rawContacts.get(i); |
| final AccountType account = accountManager.getAccountType( |
| rawContact.accountType, rawContact.accountDataSet); |
| if (account.areContactsWritable()) { |
| return i; |
| } |
| } |
| |
| return -1; |
| } |
| |
| @Override |
| public int describeContents() { |
| return 0; |
| } |
| |
| @Override |
| public void writeToParcel(Parcel dest, int flags) { |
| dest.writeLong(contactId); |
| dest.writeInt(isUserProfile ? 1 : 0); |
| dest.writeInt(showReadOnly ? 1 : 0); |
| dest.writeTypedList(rawContacts); |
| } |
| } |
| |
| public static class RawContact implements Parcelable { |
| public static final Parcelable.Creator<RawContact> CREATOR = |
| new Parcelable.Creator<RawContact>() { |
| @Override |
| public RawContact createFromParcel(Parcel source) { |
| return new RawContact(source); |
| } |
| |
| @Override |
| public RawContact[] newArray(int size) { |
| return new RawContact[size]; |
| } |
| }; |
| |
| public long id; |
| public long photoId; |
| public String displayName; |
| public String displayNameAlt; |
| public String accountName; |
| public String accountType; |
| public String accountDataSet; |
| |
| public RawContact() {} |
| |
| private RawContact(Parcel in) { |
| id = in.readLong(); |
| photoId = in.readLong(); |
| displayName = in.readString(); |
| displayNameAlt = in.readString(); |
| accountName = in.readString(); |
| accountType = in.readString(); |
| accountDataSet = in.readString(); |
| } |
| |
| @Override |
| public int describeContents() { |
| return 0; |
| } |
| |
| @Override |
| public void writeToParcel(Parcel dest, int flags) { |
| dest.writeLong(id); |
| dest.writeLong(photoId); |
| dest.writeString(displayName); |
| dest.writeString(displayNameAlt); |
| dest.writeString(accountName); |
| dest.writeString(accountType); |
| dest.writeString(accountDataSet); |
| } |
| } |
| } |