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.content.AsyncTaskLoader;
9import android.content.ContentResolver;
10import android.content.ContentUris;
11import android.content.Context;
12import android.content.Entity;
13import android.content.EntityIterator;
14import android.content.Loader.ForceLoadContentObserver;
15import android.database.Cursor;
16import android.net.Uri;
17import android.os.AsyncTask;
18import android.provider.ContactsContract.Contacts;
19import android.provider.ContactsContract.Data;
20import android.provider.ContactsContract.DisplayNameSources;
21import android.provider.ContactsContract.RawContacts;
22import android.provider.ContactsContract.RawContactsEntity;
23import android.provider.ContactsContract.StatusUpdates;
24
25import java.util.ArrayList;
26import java.util.HashMap;
27
28/**
29 * Loads a single Contact and all it constituent RawContacts.
30 */
31public 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