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