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 {
onContactLoaded(ContactData contact)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
loadInBackground()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
deliverResult(ContactData result)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 
ContactLoader(Context context, Uri lookupUri)153     public ContactLoader(Context context, Uri lookupUri) {
154         super(context);
155         mLookupUri = lookupUri;
156     }
157 
158     @Override
startLoading()159     public void startLoading() {
160         if (mContact != null) {
161             deliverResult(mContact);
162         } else {
163             forceLoad();
164         }
165     }
166 
167     @Override
stopLoading()168     public void stopLoading() {
169         mContact = null;
170         if (mObserver != null) {
171             getContext().getContentResolver().unregisterContentObserver(mObserver);
172         }
173     }
174 
175     @Override
destroy()176     public void destroy() {
177         mContact = null;
178         mDestroyed = true;
179     }
180 }
181