1863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton
2863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamiltonpackage com.android.loaderapp.model;
3863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton
4863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamiltonimport com.android.loaderapp.util.DataStatus;
5863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamiltonimport com.google.android.collect.Lists;
6863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamiltonimport com.google.android.collect.Maps;
7863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton
8f6ffbae39b62c54c0a96914beaacfca213658a4fJeff Hamiltonimport android.content.AsyncTaskLoader;
9863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamiltonimport android.content.ContentResolver;
10863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamiltonimport android.content.ContentUris;
11863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamiltonimport android.content.Context;
12863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamiltonimport android.content.Entity;
13863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamiltonimport android.content.EntityIterator;
14f6ffbae39b62c54c0a96914beaacfca213658a4fJeff Hamiltonimport android.content.Loader.ForceLoadContentObserver;
15863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamiltonimport android.database.Cursor;
16863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamiltonimport android.net.Uri;
17863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamiltonimport android.os.AsyncTask;
18863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamiltonimport android.provider.ContactsContract.Contacts;
19863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamiltonimport android.provider.ContactsContract.Data;
20863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamiltonimport android.provider.ContactsContract.DisplayNameSources;
21863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamiltonimport android.provider.ContactsContract.RawContacts;
22863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamiltonimport android.provider.ContactsContract.RawContactsEntity;
23863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamiltonimport android.provider.ContactsContract.StatusUpdates;
24863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton
25863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamiltonimport java.util.ArrayList;
26863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamiltonimport java.util.HashMap;
27863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton
28863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton/**
29863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton * Loads a single Contact and all it constituent RawContacts.
30863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton */
3103d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamiltonpublic class ContactLoader extends AsyncTaskLoader<ContactLoader.ContactData> {
32863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton    Uri mLookupUri;
33863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton    ContactData mContact;
34863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton    ForceLoadContentObserver mObserver;
35863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton    boolean mDestroyed;
36863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton
37863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton    public interface Callbacks {
38863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton        public void onContactLoaded(ContactData contact);
39863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton    }
40863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton
41863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton    public static final class ContactData {
42863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton        public Uri uri;
43863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton        public ArrayList<Entity> entities;
44863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton        public HashMap<Long, DataStatus> statuses;
45863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton        public long nameRawContactId = -1;
46863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton        public int displayNameSource = DisplayNameSources.UNDEFINED;
47863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton    }
48863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton
49863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton    interface StatusQuery {
50863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton        final String[] PROJECTION = new String[] {
51863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton                Data._ID, Data.STATUS, Data.STATUS_RES_PACKAGE, Data.STATUS_ICON,
52863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton                Data.STATUS_LABEL, Data.STATUS_TIMESTAMP, Data.PRESENCE,
53863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton        };
54863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton
55863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton        final int _ID = 0;
56863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton    }
57863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton
5803d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton    @Override
5903d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton    public ContactData loadInBackground() {
6003d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton        ContentResolver resolver = getContext().getContentResolver();
6103d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton        ContactData result = new ContactData();
6203d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton
6303d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton        // Undo the lookup URI
6403d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton        Uri contactUri = null;
6503d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton        if (mLookupUri != null) {
6603d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton            mLookupUri = Contacts.getLookupUri(resolver, mLookupUri);
67863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton            if (mLookupUri != null) {
6803d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton                contactUri = Contacts.lookupContact(resolver, mLookupUri);
69863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton            }
7003d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton        }
71863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton
7203d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton        if (contactUri == null) {
7303d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton            return null;
7403d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton        }
7503d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton        result.uri = contactUri;
7603d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton
7703d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton        // Read available social rows
7803d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton        final Uri dataUri = Uri.withAppendedPath(contactUri, Contacts.Data.CONTENT_DIRECTORY);
7903d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton        Cursor cursor = resolver.query(dataUri, StatusQuery.PROJECTION, StatusUpdates.PRESENCE
8003d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton                + " IS NOT NULL OR " + StatusUpdates.STATUS + " IS NOT NULL", null, null);
8103d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton
8203d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton        if (cursor != null) {
8303d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton            try {
8403d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton                HashMap<Long, DataStatus> statuses = Maps.newHashMap();
8503d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton
8603d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton                // Walk found statuses, creating internal row for each
8703d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton                while (cursor.moveToNext()) {
8803d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton                    final DataStatus status = new DataStatus(cursor);
8903d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton                    final long dataId = cursor.getLong(StatusQuery._ID);
9003d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton                    statuses.put(dataId, status);
91863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton                }
9203d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton                result.statuses = statuses;
9303d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton            } finally {
9403d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton                cursor.close();
95863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton            }
9603d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton        }
97863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton
9803d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton        // Read out the info about the display name
9903d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton        cursor = resolver.query(dataUri, new String[] {
10003d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton                Contacts.NAME_RAW_CONTACT_ID, Contacts.DISPLAY_NAME_SOURCE
10103d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton        }, null, null, null);
10203d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton        if (cursor != null) {
10303d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton            try {
10403d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton                if (cursor.moveToFirst()) {
10503d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton                    result.nameRawContactId = cursor.getLong(cursor
10603d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton                            .getColumnIndex(Contacts.NAME_RAW_CONTACT_ID));
10703d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton                    result.displayNameSource = cursor.getInt(cursor
10803d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton                            .getColumnIndex(Contacts.DISPLAY_NAME_SOURCE));
109863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton                }
11003d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton            } finally {
11103d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton                cursor.close();
112863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton            }
11303d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton        }
114863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton
11503d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton        // Read the constituent raw contacts
11603d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton        final long contactId = ContentUris.parseId(contactUri);
11703d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton        cursor = resolver.query(RawContactsEntity.CONTENT_URI, null, RawContacts.CONTACT_ID
11803d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton                + "=" + contactId, null, null);
11903d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton        if (cursor != null) {
12003d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton            ArrayList<Entity> entities = Lists.newArrayList();
12103d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton            EntityIterator iterator = RawContacts.newEntityIterator(cursor);
12203d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton            try {
12303d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton                while (iterator.hasNext()) {
12403d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton                    Entity entity = iterator.next();
12503d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton                    entities.add(entity);
126863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton                }
12703d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton            } finally {
12803d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton                iterator.close();
129863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton            }
13003d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton            result.entities = entities;
131863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton        }
132863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton
13303d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton        return result;
13403d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton    }
135863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton
13603d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton    @Override
13703d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton    public void deliverResult(ContactData result) {
13803d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton        // The creator isn't interested in any further updates
13903d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton        if (mDestroyed) {
14003d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton            return;
14103d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton        }
14203d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton
14303d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton        mContact = result;
14403d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton        if (result != null) {
14503d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton            if (mObserver == null) {
14603d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton                mObserver = new ForceLoadContentObserver();
147863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton            }
14803d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton            getContext().getContentResolver().registerContentObserver(mLookupUri, true, mObserver);
14903d1db253c9e7fa1f83cf467cc25a6a28ba0bdc1Jeff Hamilton            super.deliverResult(result);
150863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton        }
151863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton    }
152863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton
153863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton    public ContactLoader(Context context, Uri lookupUri) {
154863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton        super(context);
155863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton        mLookupUri = lookupUri;
156863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton    }
157863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton
158863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton    @Override
159863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton    public void startLoading() {
160863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton        if (mContact != null) {
161863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton            deliverResult(mContact);
162863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton        } else {
163863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton            forceLoad();
164863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton        }
165863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton    }
166863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton
167863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton    @Override
168863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton    public void stopLoading() {
169863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton        mContact = null;
170863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton        if (mObserver != null) {
1715ae21732ecead724915713cb26e2f79ef54fc5d8Jeff Hamilton            getContext().getContentResolver().unregisterContentObserver(mObserver);
172863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton        }
173863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton    }
174863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton
175863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton    @Override
176863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton    public void destroy() {
177863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton        mContact = null;
178863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton        mDestroyed = true;
179863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton    }
180863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton}
181