ContactLoader.java revision 863e7a55dc45cd1210e4d07e5847f48dfe301876
1 2package com.android.loaderapp.model; 3 4import com.android.loaderapp.util.DataStatus; 5import com.google.android.collect.Lists; 6import com.google.android.collect.Maps; 7 8import android.app.patterns.Loader; 9import android.content.ContentResolver; 10import android.content.ContentUris; 11import android.content.Context; 12import android.content.Entity; 13import android.content.EntityIterator; 14import android.database.Cursor; 15import android.net.Uri; 16import android.os.AsyncTask; 17import android.provider.ContactsContract.Contacts; 18import android.provider.ContactsContract.Data; 19import android.provider.ContactsContract.DisplayNameSources; 20import android.provider.ContactsContract.RawContacts; 21import android.provider.ContactsContract.RawContactsEntity; 22import android.provider.ContactsContract.StatusUpdates; 23 24import java.util.ArrayList; 25import java.util.HashMap; 26 27/** 28 * Loads a single Contact and all it constituent RawContacts. 29 */ 30public class ContactLoader extends Loader<ContactLoader.ContactData> { 31 Context mContext; 32 Uri mLookupUri; 33 ContactData mContact; 34 ForceLoadContentObserver mObserver; 35 boolean mDestroyed; 36 37 public interface Callbacks { 38 public void onContactLoaded(ContactData contact); 39 } 40 41 public static final class ContactData { 42 public Uri uri; 43 public ArrayList<Entity> entities; 44 public HashMap<Long, DataStatus> statuses; 45 public long nameRawContactId = -1; 46 public int displayNameSource = DisplayNameSources.UNDEFINED; 47 } 48 49 interface StatusQuery { 50 final String[] PROJECTION = new String[] { 51 Data._ID, Data.STATUS, Data.STATUS_RES_PACKAGE, Data.STATUS_ICON, 52 Data.STATUS_LABEL, Data.STATUS_TIMESTAMP, Data.PRESENCE, 53 }; 54 55 final int _ID = 0; 56 } 57 58 final class LoadContactTask extends AsyncTask<Void, Void, ContactData> { 59 60 @Override 61 protected ContactData doInBackground(Void... args) { 62 ContentResolver resolver = mContext.getContentResolver(); 63 ContactData result = new ContactData(); 64 65 // Undo the lookup URI 66 Uri contactUri = null; 67 if (mLookupUri != null) { 68 mLookupUri = Contacts.getLookupUri(resolver, mLookupUri); 69 if (mLookupUri != null) { 70 contactUri = Contacts.lookupContact(resolver, mLookupUri); 71 } 72 } 73 74 if (contactUri == null) { 75 return null; 76 } 77 result.uri = contactUri; 78 79 // Read available social rows 80 final Uri dataUri = Uri.withAppendedPath(contactUri, Contacts.Data.CONTENT_DIRECTORY); 81 Cursor cursor = resolver.query(dataUri, StatusQuery.PROJECTION, StatusUpdates.PRESENCE 82 + " IS NOT NULL OR " + StatusUpdates.STATUS + " IS NOT NULL", null, null); 83 84 if (cursor != null) { 85 try { 86 HashMap<Long, DataStatus> statuses = Maps.newHashMap(); 87 88 // Walk found statuses, creating internal row for each 89 while (cursor.moveToNext()) { 90 final DataStatus status = new DataStatus(cursor); 91 final long dataId = cursor.getLong(StatusQuery._ID); 92 statuses.put(dataId, status); 93 } 94 result.statuses = statuses; 95 } finally { 96 cursor.close(); 97 } 98 } 99 100 // Read out the info about the display name 101 cursor = resolver.query(dataUri, new String[] { 102 Contacts.NAME_RAW_CONTACT_ID, Contacts.DISPLAY_NAME_SOURCE 103 }, null, null, null); 104 if (cursor != null) { 105 try { 106 if (cursor.moveToFirst()) { 107 result.nameRawContactId = cursor.getLong(cursor 108 .getColumnIndex(Contacts.NAME_RAW_CONTACT_ID)); 109 result.displayNameSource = cursor.getInt(cursor 110 .getColumnIndex(Contacts.DISPLAY_NAME_SOURCE)); 111 } 112 } finally { 113 cursor.close(); 114 } 115 } 116 117 // Read the constituent raw contacts 118 final long contactId = ContentUris.parseId(contactUri); 119 cursor = resolver.query(RawContactsEntity.CONTENT_URI, null, RawContacts.CONTACT_ID 120 + "=" + contactId, null, null); 121 if (cursor != null) { 122 ArrayList<Entity> entities = Lists.newArrayList(); 123 EntityIterator iterator = RawContacts.newEntityIterator(cursor); 124 try { 125 while (iterator.hasNext()) { 126 Entity entity = iterator.next(); 127 entities.add(entity); 128 } 129 } finally { 130 iterator.close(); 131 } 132 result.entities = entities; 133 } 134 135 return result; 136 } 137 138 @Override 139 protected void onPostExecute(ContactData result) { 140 // The creator isn't interested in any furether updates 141 if (mDestroyed) { 142 return; 143 } 144 145 mContact = result; 146 if (result != null) { 147 if (mObserver == null) { 148 mObserver = new ForceLoadContentObserver(); 149 } 150 mContext.getContentResolver().registerContentObserver(mLookupUri, true, mObserver); 151 deliverResult(result); 152 } 153 } 154 } 155 156 public ContactLoader(Context context, Uri lookupUri) { 157 super(context); 158 mLookupUri = lookupUri; 159 } 160 161 @Override 162 public void startLoading() { 163 if (mContact != null) { 164 deliverResult(mContact); 165 } else { 166 forceLoad(); 167 } 168 } 169 170 @Override 171 public void forceLoad() { 172 new LoadContactTask().execute((Void[])null); 173 } 174 175 @Override 176 public void stopLoading() { 177 mContact = null; 178 if (mObserver != null) { 179 mContext.getContentResolver().unregisterContentObserver(mObserver); 180 } 181 } 182 183 @Override 184 public void destroy() { 185 mContact = null; 186 mDestroyed = true; 187 } 188} 189