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                 !uri.toString().equals(Profile.CONTENT_URI.toString())) {
    184             throw new IllegalArgumentException("Invalid contact Uri: " + uri);
    185         }
    186         return uri;
    187     }
    188 
    189     public static class RawContactsMetadata implements Parcelable {
    190         public static final Parcelable.Creator<RawContactsMetadata> CREATOR =
    191                 new Parcelable.Creator<RawContactsMetadata>() {
    192                     @Override
    193                     public RawContactsMetadata createFromParcel(Parcel source) {
    194                         return new RawContactsMetadata(source);
    195                     }
    196 
    197                     @Override
    198                     public RawContactsMetadata[] newArray(int size) {
    199                         return new RawContactsMetadata[size];
    200                     }
    201                 };
    202 
    203         public long contactId;
    204         public boolean isUserProfile;
    205         public boolean showReadOnly = false;
    206         public ArrayList<RawContact> rawContacts = new ArrayList<>();
    207 
    208         public RawContactsMetadata() {}
    209 
    210         private RawContactsMetadata(Parcel in) {
    211             contactId = in.readLong();
    212             isUserProfile = in.readInt() == 1;
    213             showReadOnly = in.readInt() == 1;
    214             in.readTypedList(rawContacts, RawContact.CREATOR);
    215         }
    216 
    217         /**
    218          * Removes all read-only raw contacts.
    219          */
    220         public void trimReadOnly(AccountTypeManager accountManager) {
    221             for (int i = rawContacts.size() - 1; i >= 0 ; i--) {
    222                 final RawContact rawContact = rawContacts.get(i);
    223                 final AccountType account = accountManager.getAccountType(
    224                         rawContact.accountType, rawContact.accountDataSet);
    225                 if (!account.areContactsWritable()) {
    226                     rawContacts.remove(i);
    227                 }
    228             }
    229         }
    230 
    231         /**
    232          * Returns the index of the first writable account in this contact or -1 if none exist.
    233          */
    234         public int getIndexOfFirstWritableAccount(AccountTypeManager accountManager) {
    235             for (int i = 0; i < rawContacts.size(); i++) {
    236                 final RawContact rawContact = rawContacts.get(i);
    237                 final AccountType account = accountManager.getAccountType(
    238                         rawContact.accountType, rawContact.accountDataSet);
    239                 if (account.areContactsWritable()) {
    240                     return i;
    241                 }
    242             }
    243 
    244             return -1;
    245         }
    246 
    247         @Override
    248         public int describeContents() {
    249             return 0;
    250         }
    251 
    252         @Override
    253         public void writeToParcel(Parcel dest, int flags) {
    254             dest.writeLong(contactId);
    255             dest.writeInt(isUserProfile ? 1 : 0);
    256             dest.writeInt(showReadOnly ? 1 : 0);
    257             dest.writeTypedList(rawContacts);
    258         }
    259     }
    260 
    261     public static class RawContact implements Parcelable {
    262         public static final Parcelable.Creator<RawContact> CREATOR =
    263                 new Parcelable.Creator<RawContact>() {
    264                     @Override
    265                     public RawContact createFromParcel(Parcel source) {
    266                         return new RawContact(source);
    267                     }
    268 
    269                     @Override
    270                     public RawContact[] newArray(int size) {
    271                         return new RawContact[size];
    272                     }
    273                 };
    274 
    275         public long id;
    276         public long photoId;
    277         public String displayName;
    278         public String displayNameAlt;
    279         public String accountName;
    280         public String accountType;
    281         public String accountDataSet;
    282 
    283         public RawContact() {}
    284 
    285         private RawContact(Parcel in) {
    286             id = in.readLong();
    287             photoId = in.readLong();
    288             displayName = in.readString();
    289             displayNameAlt = in.readString();
    290             accountName = in.readString();
    291             accountType = in.readString();
    292             accountDataSet = in.readString();
    293         }
    294 
    295         @Override
    296         public int describeContents() {
    297             return 0;
    298         }
    299 
    300         @Override
    301         public void writeToParcel(Parcel dest, int flags) {
    302             dest.writeLong(id);
    303             dest.writeLong(photoId);
    304             dest.writeString(displayName);
    305             dest.writeString(displayNameAlt);
    306             dest.writeString(accountName);
    307             dest.writeString(accountType);
    308             dest.writeString(accountDataSet);
    309         }
    310     }
    311 }
    312