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