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 @@ ...@@ -5,25 +5,22 @@
package org.chromium.chrome.browser.contacts_picker; package org.chromium.chrome.browser.contacts_picker;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.database.Cursor;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.net.Uri;
import android.provider.ContactsContract;
import android.support.v7.widget.RecyclerView.Adapter; import android.support.v7.widget.RecyclerView.Adapter;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.ViewGroup; import android.view.ViewGroup;
import org.chromium.base.task.AsyncTask;
import org.chromium.chrome.R; import org.chromium.chrome.R;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
/** /**
* A data adapter for the Contacts Picker. * 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. // The category view to use to show the contacts.
private PickerCategoryView mCategoryView; private PickerCategoryView mCategoryView;
...@@ -33,14 +30,12 @@ public class PickerAdapter extends Adapter<ContactViewHolder> { ...@@ -33,14 +30,12 @@ public class PickerAdapter extends Adapter<ContactViewHolder> {
// The full list of all registered contacts on the device. // The full list of all registered contacts on the device.
private ArrayList<ContactDetails> mContactDetails; 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. // A list of search result indices into the larger data set.
private ArrayList<Integer> mSearchResults; private ArrayList<Integer> mSearchResults;
private static final String[] PROJECTION = {
ContactsContract.Contacts._ID, ContactsContract.Contacts.LOOKUP_KEY,
ContactsContract.Contacts.DISPLAY_NAME_PRIMARY,
};
/** /**
* The PickerAdapter constructor. * The PickerAdapter constructor.
* @param categoryView The category view to use to show the contacts. * @param categoryView The category view to use to show the contacts.
...@@ -49,7 +44,9 @@ public class PickerAdapter extends Adapter<ContactViewHolder> { ...@@ -49,7 +44,9 @@ public class PickerAdapter extends Adapter<ContactViewHolder> {
public PickerAdapter(PickerCategoryView categoryView, ContentResolver contentResolver) { public PickerAdapter(PickerCategoryView categoryView, ContentResolver contentResolver) {
mCategoryView = categoryView; mCategoryView = categoryView;
mContentResolver = contentResolver; 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> { ...@@ -82,37 +79,15 @@ public class PickerAdapter extends Adapter<ContactViewHolder> {
* @return The contact list as an array. * @return The contact list as an array.
*/ */
public ArrayList<ContactDetails> getAllContacts() { public ArrayList<ContactDetails> getAllContacts() {
if (mContactDetails != null) return mContactDetails; return mContactDetails;
}
Map<String, ArrayList<String>> emailMap =
getDetails(ContactsContract.CommonDataKinds.Email.CONTENT_URI, // ContactFetcherWorkerTask.ContactsRetrievedCallback:
ContactsContract.CommonDataKinds.Email.CONTACT_ID,
ContactsContract.CommonDataKinds.Email.DATA, @Override
ContactsContract.CommonDataKinds.Email.CONTACT_ID + " ASC, " public void contactsRetrieved(ArrayList<ContactDetails> contacts) {
+ ContactsContract.CommonDataKinds.Email.DATA + " ASC"); mContactDetails = contacts;
notifyDataSetChanged();
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;
} }
// RecyclerView.Adapter: // RecyclerView.Adapter:
...@@ -138,46 +113,6 @@ public class PickerAdapter extends Adapter<ContactViewHolder> { ...@@ -138,46 +113,6 @@ public class PickerAdapter extends Adapter<ContactViewHolder> {
holder.setContactDetails(contact); 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() { private Bitmap getPhoto() {
return null; return null;
} }
...@@ -185,7 +120,7 @@ public class PickerAdapter extends Adapter<ContactViewHolder> { ...@@ -185,7 +120,7 @@ public class PickerAdapter extends Adapter<ContactViewHolder> {
@Override @Override
public int getItemCount() { public int getItemCount() {
if (mSearchResults != null) return mSearchResults.size(); if (mSearchResults != null) return mSearchResults.size();
if (mContactDetails == null) return 0;
return mContactDetails.size(); return mContactDetails.size();
} }
} }
...@@ -268,6 +268,7 @@ chrome_java_sources = [ ...@@ -268,6 +268,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/compositor/scene_layer/ToolbarSceneLayer.java", "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/consent_auditor/ConsentAuditorBridge.java",
"java/src/org/chromium/chrome/browser/contacts_picker/ContactDetails.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/ContactsPickerDialog.java",
"java/src/org/chromium/chrome/browser/contacts_picker/ContactsPickerToolbar.java", "java/src/org/chromium/chrome/browser/contacts_picker/ContactsPickerToolbar.java",
"java/src/org/chromium/chrome/browser/contacts_picker/ContactView.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