Home | History | Annotate | Download | only in editor
      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 
     55     public PickRawContactLoader(Context context, Uri contactUri) {
     56         super(context);
     57         mContactUri = ensureIsContactUri(contactUri);
     58     }
     59 
     60     @Override
     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
    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         }
    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      */
    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 
    207         public RawContactsMetadata() {}
    208 
    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          */
    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) {
    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 
    282         public RawContact() {}
    283 
    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
    295         public int describeContents() {
    296             return 0;
    297         }
    298 
    299         @Override
    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