Commit e360ae59 authored by Finnur Thorarinsson's avatar Finnur Thorarinsson Committed by Commit Bot

Contacts Picker: Move data retrieval off the UI thread.

Bug: 860467
Change-Id: I47a07d4b3a518c1bf109483b80e8951b9fa93aa7
Reviewed-on: https://chromium-review.googlesource.com/1251626
Commit-Queue: Finnur Thorarinsson <finnur@chromium.org>
Reviewed-by: default avatarTheresa <twellington@chromium.org>
Cr-Commit-Position: refs/heads/master@{#595810}
parent 6e62467b
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.contacts_picker;
import android.content.ContentResolver;
import android.database.Cursor;
import android.net.Uri;
import android.provider.ContactsContract;
import org.chromium.base.ThreadUtils;
import org.chromium.base.task.AsyncTask;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
/**
* A worker task to retrieve images for contacts.
*/
class ContactsFetcherWorkerTask extends AsyncTask<ArrayList<ContactDetails>> {
private static final String[] PROJECTION = {
ContactsContract.Contacts._ID, ContactsContract.Contacts.LOOKUP_KEY,
ContactsContract.Contacts.DISPLAY_NAME_PRIMARY,
};
/**
* An interface to use to communicate back the results to the client.
*/
public interface ContactsRetrievedCallback {
/**
* A callback to define to receive the contact details.
* @param contacts The contacts retrieved.
*/
void contactsRetrieved(ArrayList<ContactDetails> contacts);
}
// The content resolver to use for looking up contacts.
private ContentResolver mContentResolver;
// The callback to use to communicate the results.
private ContactsRetrievedCallback mCallback;
/**
* A ContactsFetcherWorkerTask constructor.
* @param callback The callback to use to communicate back the results.
*/
public ContactsFetcherWorkerTask(
ContentResolver contentResolver, ContactsRetrievedCallback callback) {
mContentResolver = contentResolver;
mCallback = callback;
}
/**
* Fetches the details for all contacts (in a background thread).
* @return The icon representing a contact.
*/
@Override
protected ArrayList<ContactDetails> doInBackground() {
assert !ThreadUtils.runningOnUiThread();
if (isCancelled()) return null;
return getAllContacts();
}
/**
* Fetches specific details for contacts.
* @param source The source URI to use for the lookup.
* @param idColumn The name of the id column.
* @param idColumn The name of the data column.
* @param sortOrder The sort order. Data must be sorted by CONTACT_ID but can be additionally
* sorted also.
* @return A map of ids to contact details (as ArrayList).
*/
private Map<String, ArrayList<String>> getDetails(
Uri source, String idColumn, String dataColumn, String sortOrder) {
Map<String, ArrayList<String>> map = new HashMap<String, ArrayList<String>>();
Cursor cursor = mContentResolver.query(source, null, null, null, sortOrder);
ArrayList<String> list = new ArrayList<String>();
String key = "";
String value;
while (cursor.moveToNext()) {
String id = cursor.getString(cursor.getColumnIndex(idColumn));
value = cursor.getString(cursor.getColumnIndex(dataColumn));
if (key.isEmpty()) {
key = id;
list.add(value);
} else {
if (key.equals(id)) {
list.add(value);
} else {
map.put(key, list);
list = new ArrayList<String>();
list.add(value);
key = id;
}
}
}
map.put(key, list);
cursor.close();
return map;
}
/**
* Fetches all known contacts.
* @return The contact list as an array.
*/
public ArrayList<ContactDetails> getAllContacts() {
Map<String, ArrayList<String>> emailMap =
getDetails(ContactsContract.CommonDataKinds.Email.CONTENT_URI,
ContactsContract.CommonDataKinds.Email.CONTACT_ID,
ContactsContract.CommonDataKinds.Email.DATA,
ContactsContract.CommonDataKinds.Email.CONTACT_ID + " ASC, "
+ ContactsContract.CommonDataKinds.Email.DATA + " ASC");
Map<String, ArrayList<String>> phoneMap =
getDetails(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
ContactsContract.CommonDataKinds.Email.DATA,
ContactsContract.CommonDataKinds.Email.CONTACT_ID + " ASC, "
+ ContactsContract.CommonDataKinds.Phone.NUMBER + " ASC");
// A cursor containing the raw contacts data.
Cursor cursor = mContentResolver.query(ContactsContract.Contacts.CONTENT_URI, PROJECTION,
null, null, ContactsContract.Contacts.DISPLAY_NAME_PRIMARY + " ASC");
ArrayList<ContactDetails> contacts = new ArrayList<ContactDetails>(cursor.getCount());
if (!cursor.moveToFirst()) return contacts;
do {
String id = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
String name = cursor.getString(
cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME_PRIMARY));
contacts.add(new ContactDetails(id, name, emailMap.get(id), phoneMap.get(id)));
} while (cursor.moveToNext());
cursor.close();
return contacts;
}
/**
* Communicates the results back to the client. Called on the UI thread.
* @param contacts The contacts retrieved.
*/
@Override
protected void onPostExecute(ArrayList<ContactDetails> contacts) {
assert ThreadUtils.runningOnUiThread();
if (isCancelled()) return;
mCallback.contactsRetrieved(contacts);
}
}
......@@ -5,25 +5,22 @@
package org.chromium.chrome.browser.contacts_picker;
import android.content.ContentResolver;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.Uri;
import android.provider.ContactsContract;
import android.support.v7.widget.RecyclerView.Adapter;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import org.chromium.base.task.AsyncTask;
import org.chromium.chrome.R;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
/**
* A data adapter for the Contacts Picker.
*/
public class PickerAdapter extends Adapter<ContactViewHolder> {
public class PickerAdapter extends Adapter<ContactViewHolder>
implements ContactsFetcherWorkerTask.ContactsRetrievedCallback {
// The category view to use to show the contacts.
private PickerCategoryView mCategoryView;
......@@ -33,14 +30,12 @@ public class PickerAdapter extends Adapter<ContactViewHolder> {
// The full list of all registered contacts on the device.
private ArrayList<ContactDetails> mContactDetails;
// The async worker task to use for fetching the contact details.
private ContactsFetcherWorkerTask mWorkerTask;
// A list of search result indices into the larger data set.
private ArrayList<Integer> mSearchResults;
private static final String[] PROJECTION = {
ContactsContract.Contacts._ID, ContactsContract.Contacts.LOOKUP_KEY,
ContactsContract.Contacts.DISPLAY_NAME_PRIMARY,
};
/**
* The PickerAdapter constructor.
* @param categoryView The category view to use to show the contacts.
......@@ -49,7 +44,9 @@ public class PickerAdapter extends Adapter<ContactViewHolder> {
public PickerAdapter(PickerCategoryView categoryView, ContentResolver contentResolver) {
mCategoryView = categoryView;
mContentResolver = contentResolver;
mContactDetails = getAllContacts();
mWorkerTask = new ContactsFetcherWorkerTask(mContentResolver, this);
mWorkerTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
/**
......@@ -82,37 +79,15 @@ public class PickerAdapter extends Adapter<ContactViewHolder> {
* @return The contact list as an array.
*/
public ArrayList<ContactDetails> getAllContacts() {
if (mContactDetails != null) return mContactDetails;
Map<String, ArrayList<String>> emailMap =
getDetails(ContactsContract.CommonDataKinds.Email.CONTENT_URI,
ContactsContract.CommonDataKinds.Email.CONTACT_ID,
ContactsContract.CommonDataKinds.Email.DATA,
ContactsContract.CommonDataKinds.Email.CONTACT_ID + " ASC, "
+ ContactsContract.CommonDataKinds.Email.DATA + " ASC");
Map<String, ArrayList<String>> phoneMap =
getDetails(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
ContactsContract.CommonDataKinds.Email.DATA,
ContactsContract.CommonDataKinds.Email.CONTACT_ID + " ASC, "
+ ContactsContract.CommonDataKinds.Phone.NUMBER + " ASC");
// A cursor containing the raw contacts data.
Cursor cursor = mContentResolver.query(ContactsContract.Contacts.CONTENT_URI, PROJECTION,
null, null, ContactsContract.Contacts.DISPLAY_NAME_PRIMARY + " ASC");
ArrayList<ContactDetails> contacts = new ArrayList<ContactDetails>(cursor.getCount());
if (!cursor.moveToFirst()) return contacts;
do {
String id = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
String name = cursor.getString(
cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME_PRIMARY));
contacts.add(new ContactDetails(id, name, emailMap.get(id), phoneMap.get(id)));
} while (cursor.moveToNext());
cursor.close();
return contacts;
return mContactDetails;
}
// ContactFetcherWorkerTask.ContactsRetrievedCallback:
@Override
public void contactsRetrieved(ArrayList<ContactDetails> contacts) {
mContactDetails = contacts;
notifyDataSetChanged();
}
// RecyclerView.Adapter:
......@@ -138,46 +113,6 @@ public class PickerAdapter extends Adapter<ContactViewHolder> {
holder.setContactDetails(contact);
}
/**
* Fetches details for a contact.
* @param source The source URI to use for the lookup.
* @param idColumn The name of the id column.
* @param idColumn The name of the data column.
* @param sortOrder The sort order. Data must be sorted by CONTACT_ID but can be additionally
* sorted also.
* @return A map of ids to contact details (as ArrayList).
*/
private Map<String, ArrayList<String>> getDetails(
Uri source, String idColumn, String dataColumn, String sortOrder) {
Map<String, ArrayList<String>> map = new HashMap<String, ArrayList<String>>();
Cursor cursor = mContentResolver.query(source, null, null, null, sortOrder);
ArrayList<String> list = new ArrayList<String>();
String key = "";
String value;
while (cursor.moveToNext()) {
String id = cursor.getString(cursor.getColumnIndex(idColumn));
value = cursor.getString(cursor.getColumnIndex(dataColumn));
if (key.isEmpty()) {
key = id;
list.add(value);
} else {
if (key.equals(id)) {
list.add(value);
} else {
map.put(key, list);
list = new ArrayList<String>();
list.add(value);
key = id;
}
}
}
map.put(key, list);
cursor.close();
return map;
}
private Bitmap getPhoto() {
return null;
}
......@@ -185,7 +120,7 @@ public class PickerAdapter extends Adapter<ContactViewHolder> {
@Override
public int getItemCount() {
if (mSearchResults != null) return mSearchResults.size();
if (mContactDetails == null) return 0;
return mContactDetails.size();
}
}
......@@ -268,6 +268,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/compositor/scene_layer/ToolbarSceneLayer.java",
"java/src/org/chromium/chrome/browser/consent_auditor/ConsentAuditorBridge.java",
"java/src/org/chromium/chrome/browser/contacts_picker/ContactDetails.java",
"java/src/org/chromium/chrome/browser/contacts_picker/ContactsFetcherWorkerTask.java",
"java/src/org/chromium/chrome/browser/contacts_picker/ContactsPickerDialog.java",
"java/src/org/chromium/chrome/browser/contacts_picker/ContactsPickerToolbar.java",
"java/src/org/chromium/chrome/browser/contacts_picker/ContactView.java",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment