Commit c60f8714 authored by Rayan Kanso's avatar Rayan Kanso Committed by Commit Bot

[Contacts] Use ContactsContract API in Android to get icons.

Grab available icons and pipe down to contacts provider.

Bug: 1020564
Change-Id: Ia81bb00567a937ea3f6e7aa3a992cdaac2e9d16c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1893068Reviewed-by: default avatarFinnur Thorarinsson <finnur@chromium.org>
Reviewed-by: default avatarDavid Trainor <dtrainor@chromium.org>
Auto-Submit: Rayan Kanso <rayankans@chromium.org>
Commit-Queue: Rayan Kanso <rayankans@chromium.org>
Cr-Commit-Position: refs/heads/master@{#716354}
parent 3ada2199
......@@ -272,6 +272,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/compositor/scene_layer/TabStripSceneLayer.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/contacts_picker/CompressContactIconsWorkerTask.java",
"java/src/org/chromium/chrome/browser/contacts_picker/ContactDetails.java",
"java/src/org/chromium/chrome/browser/contacts_picker/ContactView.java",
"java/src/org/chromium/chrome/browser/contacts_picker/ContactViewHolder.java",
......
// Copyright 2019 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.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import org.chromium.base.ThreadUtils;
import org.chromium.base.task.AsyncTask;
import org.chromium.blink.mojom.ContactIconBlob;
import java.io.ByteArrayOutputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
/**
* A worker task to retrieve images for contacts.
*/
public class CompressContactIconsWorkerTask extends AsyncTask<Void> {
private ContentResolver mContentResolver;
private Set<String> mNoIconIds;
private HashMap<String, Bitmap> mBitmaps;
private List<ContactDetails> mSelectedContacts;
private CompressContactIconsCallback mCallback;
public static boolean sDisableForTesting;
/**
* An interface to use to communicate back the results to the client.
*/
public interface CompressContactIconsCallback {
/**
* @param selectedContacts The list of selected contacts with their icons.
*/
void iconsCompressed(List<ContactDetails> selectedContacts);
}
/**
* @param contentResolver The context's content resolver.
* @param bitmapCache The bitmap cache holding the icon bitmaps.
* @param selectedContacts The list of contacts selected by the user.
* @param callback The callback to return the results to.
*/
public CompressContactIconsWorkerTask(ContentResolver contentResolver,
PickerCategoryView.ContactsBitmapCache bitmapCache,
List<ContactDetails> selectedContacts, CompressContactIconsCallback callback) {
mContentResolver = contentResolver;
mNoIconIds = bitmapCache.noIconIds;
mBitmaps = new HashMap<>();
for (ContactDetails contact : selectedContacts) {
mBitmaps.put(contact.getId(), bitmapCache.getBitmap(contact.getId()));
}
mSelectedContacts = selectedContacts;
mCallback = callback;
}
@Override
protected Void doInBackground() {
assert !ThreadUtils.runningOnUiThread();
if (sDisableForTesting) {
return null;
}
for (ContactDetails contact : mSelectedContacts) {
if (mNoIconIds.contains(contact.getId())) {
continue;
}
Bitmap icon = mBitmaps.get(contact.getId());
if (icon == null) {
Drawable drawable = contact.isSelf() ? contact.getSelfIcon() : null;
if (drawable != null && drawable instanceof BitmapDrawable) {
icon = ((BitmapDrawable) drawable).getBitmap();
} else if (!contact.isSelf()) {
icon = new FetchIconWorkerTask(contact.getId(), mContentResolver, null)
.doInBackground();
}
}
if (icon == null) {
continue;
}
ByteArrayOutputStream stream = new ByteArrayOutputStream();
icon.compress(Bitmap.CompressFormat.PNG, 100, stream);
ContactIconBlob blob = new ContactIconBlob();
blob.data = stream.toByteArray();
blob.mimeType = "image/png";
contact.setIcon(blob);
}
return null;
}
/**
* Communicates the results back to the client. Called on the UI thread.
* @param result Unused Void variable.
*/
@Override
protected void onPostExecute(Void result) {
assert ThreadUtils.runningOnUiThread();
if (isCancelled()) return;
mCallback.iconsCompressed(mSelectedContacts);
}
}
......@@ -9,6 +9,7 @@ import android.graphics.drawable.Drawable;
import androidx.annotation.Nullable;
import org.chromium.blink.mojom.ContactIconBlob;
import org.chromium.chrome.R;
import org.chromium.payments.mojom.PaymentAddress;
......@@ -53,6 +54,9 @@ public class ContactDetails implements Comparable<ContactDetails> {
// The list of addresses registered for this contact.
private final List<PaymentAddress> mAddresses;
// The list of icons registered for this contact.
private final List<ContactIconBlob> mIcons;
// Keeps track of whether this is the contact detail for the owner of the device.
private boolean mIsSelf;
......@@ -75,6 +79,7 @@ public class ContactDetails implements Comparable<ContactDetails> {
mEmails = emails != null ? emails : new ArrayList<String>();
mPhoneNumbers = phoneNumbers != null ? phoneNumbers : new ArrayList<String>();
mAddresses = addresses != null ? addresses : new ArrayList<PaymentAddress>();
mIcons = new ArrayList<>();
mId = id;
}
......@@ -95,6 +100,10 @@ public class ContactDetails implements Comparable<ContactDetails> {
return mAddresses;
}
public List<ContactIconBlob> getIcons() {
return mIcons;
}
public String getDisplayName() {
return mDisplayName;
}
......@@ -103,6 +112,11 @@ public class ContactDetails implements Comparable<ContactDetails> {
return mId;
}
public void setIcon(ContactIconBlob icon) {
assert mIcons.isEmpty();
mIcons.add(icon);
}
/**
* Marks whether object is representing the owner of the device.
* @param value True if this is the contact details for the owner. False otherwise.
......
......@@ -91,14 +91,13 @@ public class ContactViewHolder
@Override
public void iconRetrieved(Bitmap icon, String contactId) {
if (icon == null) return;
if (!contactId.equals(mContact.getId())) return;
if (mCategoryView.getIconCache().getBitmap(contactId) == null) {
mCategoryView.getIconCache().putBitmap(contactId, icon);
}
mItemView.setIconBitmap(icon);
if (icon != null && contactId.equals(mContact.getId())) {
mItemView.setIconBitmap(icon);
}
}
/** Sets the icon to use when testing. */
......
......@@ -7,6 +7,7 @@ package org.chromium.chrome.browser.contacts_picker;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
......@@ -18,6 +19,7 @@ import androidx.annotation.VisibleForTesting;
import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.task.AsyncTask;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.GlobalDiscardableReferencePool;
......@@ -45,7 +47,8 @@ import java.util.Set;
public class PickerCategoryView extends OptimizedFrameLayout
implements View.OnClickListener, RecyclerView.RecyclerListener,
SelectionDelegate.SelectionObserver<ContactDetails>,
SelectableListToolbar.SearchDelegate, TopView.SelectAllToggleCallback {
SelectableListToolbar.SearchDelegate, TopView.SelectAllToggleCallback,
CompressContactIconsWorkerTask.CompressContactIconsCallback {
// These values are written to logs. New enum values can be added, but existing
// enums must never be renumbered or deleted and reused.
private static final int ACTION_CANCEL = 0;
......@@ -91,7 +94,7 @@ public class PickerCategoryView extends OptimizedFrameLayout
private SelectionDelegate<ContactDetails> mSelectionDelegate;
// A cache for contact images, lazily created.
private BitmapCache mBitmapCache;
private ContactsBitmapCache mBitmapCache;
// The search icon.
private ImageView mSearchButton;
......@@ -105,9 +108,6 @@ public class PickerCategoryView extends OptimizedFrameLayout
// Whether the picker is in multi-selection mode.
private boolean mMultiSelectionAllowed;
// Whether the contacts data returned includes icons.
public final boolean includeIcons;
// Whether the contacts data returned includes names.
public final boolean includeNames;
......@@ -120,6 +120,9 @@ public class PickerCategoryView extends OptimizedFrameLayout
// Whether the contacts data returned includes addresses.
public final boolean includeAddresses;
// Whether the contacts data returned includes icons.
public final boolean includeIcons;
/**
* @param multiSelectionAllowed Whether the contacts picker should allow multiple items to be
* selected.
......@@ -133,11 +136,11 @@ public class PickerCategoryView extends OptimizedFrameLayout
mActivity = (ChromeActivity) context;
mMultiSelectionAllowed = multiSelectionAllowed;
includeIcons = shouldIncludeIcons;
includeNames = shouldIncludeNames;
includeEmails = shouldIncludeEmails;
includeTel = shouldIncludeTel;
includeAddresses = shouldIncludeAddresses;
includeIcons = shouldIncludeIcons;
mSelectionDelegate = new SelectionDelegate<ContactDetails>();
if (!multiSelectionAllowed) mSelectionDelegate.setSingleSelectionMode();
......@@ -177,12 +180,7 @@ public class PickerCategoryView extends OptimizedFrameLayout
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setLayoutManager(mLayoutManager);
// Each image (on a Pixel 2 phone) is about 30-40K. Calculate a proportional amount of the
// available memory, but cap it at 5MB.
final long maxMemory = ConversionUtils.bytesToKilobytes(Runtime.getRuntime().maxMemory());
int iconCacheSizeKb = (int) (maxMemory / 8); // 1/8th of the available memory.
mBitmapCache = new BitmapCache(GlobalDiscardableReferencePool.getReferencePool(),
Math.min(iconCacheSizeKb, 5 * ConversionUtils.BYTES_PER_MEGABYTE));
mBitmapCache = new ContactsBitmapCache();
}
/**
......@@ -296,7 +294,7 @@ public class PickerCategoryView extends OptimizedFrameLayout
public void onClick(View view) {
int id = view.getId();
if (id == R.id.done) {
notifyContactsSelected();
prepareContactsSelected();
} else if (id == R.id.search) {
onStartSearch();
} else {
......@@ -314,7 +312,7 @@ public class PickerCategoryView extends OptimizedFrameLayout
return mIconGenerator;
}
BitmapCache getIconCache() {
ContactsBitmapCache getIconCache() {
return mBitmapCache;
}
......@@ -330,6 +328,29 @@ public class PickerCategoryView extends OptimizedFrameLayout
return mMultiSelectionAllowed;
}
/**
* Formats the selected contacts before notifying the listeners.
*/
private void prepareContactsSelected() {
List<ContactDetails> selectedContacts = mSelectionDelegate.getSelectedItemsAsList();
Collections.sort(selectedContacts);
if (includeIcons && PickerAdapter.includesIcons()) {
// Fetch missing icons and compress them first.
new CompressContactIconsWorkerTask(
mActivity.getContentResolver(), mBitmapCache, selectedContacts, this)
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
return;
}
notifyContactsSelected(selectedContacts);
}
@Override
public void iconsCompressed(List<ContactDetails> selectedContacts) {
notifyContactsSelected(selectedContacts);
}
/**
* @param isIncluded Whether the property was requested by the API.
* @param isEnabled Whether the property was allowed to be shared by the user.
......@@ -355,12 +376,8 @@ public class PickerCategoryView extends OptimizedFrameLayout
/**
* Notifies any listeners that one or more contacts have been selected.
*/
private void notifyContactsSelected() {
List<ContactDetails> selectedContacts = mSelectionDelegate.getSelectedItemsAsList();
Collections.sort(selectedContacts);
List<ContactsPickerListener.Contact> contacts =
new ArrayList<ContactsPickerListener.Contact>();
private void notifyContactsSelected(List<ContactDetails> selectedContacts) {
List<ContactsPickerListener.Contact> contacts = new ArrayList<>();
for (ContactDetails contactDetails : selectedContacts) {
contacts.add(new ContactsPickerListener.Contact(
......@@ -370,10 +387,12 @@ public class PickerCategoryView extends OptimizedFrameLayout
contactDetails.getEmails()),
getContactPropertyValues(includeTel, PickerAdapter.includesTelephones(),
contactDetails.getPhoneNumbers()),
// TODO(crbug.com/1016870): Check the address chip when added.
getContactPropertyValues(
includeAddresses, true, contactDetails.getAddresses())));
getContactPropertyValues(includeAddresses, PickerAdapter.includesAddresses(),
contactDetails.getAddresses()),
getContactPropertyValues(includeIcons, PickerAdapter.includesIcons(),
contactDetails.getIcons())));
}
executeAction(ContactsPickerListener.ContactsPickerAction.CONTACTS_SELECTED, contacts,
ACTION_CONTACTS_SELECTED);
}
......@@ -440,4 +459,35 @@ public class PickerCategoryView extends OptimizedFrameLayout
public TopView getTopViewForTesting() {
return mTopView;
}
// A wrapper around BitmapCache to keep track of contacts that don't have an icon.
protected static class ContactsBitmapCache {
public BitmapCache bitmapCache;
public Set<String> noIconIds;
public ContactsBitmapCache() {
// Each image (on a Pixel 2 phone) is about 30-40K. Calculate a proportional amount of
// the available memory, but cap it at 5MB.
final long maxMemory =
ConversionUtils.bytesToKilobytes(Runtime.getRuntime().maxMemory());
int iconCacheSizeKb = (int) (maxMemory / 8); // 1/8th of the available memory.
bitmapCache = new BitmapCache(GlobalDiscardableReferencePool.getReferencePool(),
Math.min(iconCacheSizeKb, 5 * ConversionUtils.BYTES_PER_MEGABYTE));
noIconIds = new HashSet<>();
}
public Bitmap getBitmap(String id) {
return bitmapCache.getBitmap(id);
}
public void putBitmap(String id, Bitmap icon) {
if (icon == null) {
noIconIds.add(id);
} else {
bitmapCache.putBitmap(id, icon);
noIconIds.remove(id);
}
}
}
}
......@@ -257,10 +257,10 @@ public class ProcessInitializationHandler {
@Override
public void showContactsPicker(Context context, ContactsPickerListener listener,
boolean allowMultiple, boolean includeNames, boolean includeEmails,
boolean includeTel, boolean includeAddresses, String formattedOrigin) {
boolean includeTel, boolean includeAddresses, boolean includeIcons,
String formattedOrigin) {
mDialog = new ContactsPickerDialog(context, listener, allowMultiple, includeNames,
includeEmails, includeTel, includeAddresses, /*includeIcons=*/false,
formattedOrigin);
includeEmails, includeTel, includeAddresses, includeIcons, formattedOrigin);
mDialog.getWindow().getAttributes().windowAnimations =
R.style.PickerDialogAnimation;
mDialog.show();
......
......@@ -7,6 +7,7 @@ package org.chromium.chrome.browser.contacts_picker;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.LargeTest;
import android.support.v7.widget.RecyclerView;
......@@ -22,6 +23,7 @@ import org.junit.runner.RunWith;
import org.chromium.base.test.util.CallbackHelper;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.blink.mojom.ContactIconBlob;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.ChromeFeatureList;
......@@ -85,6 +87,9 @@ public class ContactsPickerDialogTest
// The list of currently selected contacts (built piecemeal).
private List<ContactDetails> mCurrentContactSelection;
// An icon to use as contact avatar during testing.
private Bitmap mIcon;
// A callback that fires when something is selected in the dialog.
public final CallbackHelper onSelectionCallback = new CallbackHelper();
......@@ -94,10 +99,12 @@ public class ContactsPickerDialogTest
@Before
public void setUp() throws Exception {
mActivityTestRule.startMainActivityOnBlankPage();
Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
mIcon = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(mIcon);
canvas.drawColor(Color.BLUE);
ContactViewHolder.setIconForTesting(bitmap);
ContactViewHolder.setIconForTesting(mIcon);
// Disable the async task since it tries to access contact icons which would fail in tests.
CompressContactIconsWorkerTask.sDisableForTesting = true;
}
// ContactsPickerDialog.ContactsPickerListener:
......@@ -308,6 +315,10 @@ public class ContactsPickerDialogTest
address.recipient = "";
address.phone = "";
ContactIconBlob icon = new ContactIconBlob();
icon.data = new byte[] {0x42};
icon.mimeType = "image/test";
mTestContacts.add(new ContactDetails("0", "Contact 0", Arrays.asList("0@example.com"),
Arrays.asList("555-1234"), Arrays.asList(address)));
mTestContacts.add(new ContactDetails("1", "Contact 1", /*emails=*/null,
......@@ -320,10 +331,12 @@ public class ContactsPickerDialogTest
/*phoneNumbers=*/null, /*addresses=*/null));
mTestContacts.add(new ContactDetails("5", "Contact 5", /*emails=*/null,
/*phoneNumbers=*/null, /*addresses=*/null));
if (ownerEmail != null) {
// Note: The dialog will move Contact 6 (owner) to the top of the list.
ContactDetails owner = new ContactDetails("6", "Contact 6",
Arrays.asList("owner@example.com"), /*phoneNumbers=*/null, /*addresses=*/null);
owner.setSelfIcon(new BitmapDrawable(mIcon));
ContactDetails owner2 = new ContactDetails("7", "Contact 7",
Arrays.asList("owner@example.com"), /*phoneNumbers=*/null, /*addresses=*/null);
mTestContacts.add(owner);
......@@ -638,6 +651,28 @@ public class ContactsPickerDialogTest
Assert.assertEquals(16, mLastPropertiesRequested);
}
@Test
@LargeTest
public void testSelfIconCompressed() throws Throwable {
CompressContactIconsWorkerTask.sDisableForTesting = false;
setTestContacts(/* ownerEmail = */ "owner@example.com");
createDialog(/* multiselect = */ false);
Assert.assertTrue(mDialog.isShowing());
int expectedSelectionCount = 1;
clickView(0, expectedSelectionCount, /* expectSelection = */ true);
clickDone();
Assert.assertEquals(ContactsPickerAction.CONTACTS_SELECTED, mLastActionRecorded);
Assert.assertEquals(1, mLastSelectedContacts.size());
ContactIconBlob icon =
ContactIconBlob.deserialize(mLastSelectedContacts.get(0).serializedIcons.get(0));
Assert.assertTrue(icon.data.length > 0);
Assert.assertEquals("image/png", icon.mimeType);
}
@Test
@LargeTest
public void testSelectAll() throws Throwable {
......
......@@ -81,7 +81,7 @@ void ContactsProviderAndroid::Select(bool multiple,
JNIEnv* env = base::android::AttachCurrentThread();
Java_ContactsDialogHost_showDialog(
env, dialog_, multiple, include_names, include_emails, include_tel,
include_addresses,
include_addresses, include_icons,
base::android::ConvertUTF16ToJavaString(env, formatted_origin_));
}
......@@ -90,7 +90,8 @@ void ContactsProviderAndroid::AddContact(
const base::android::JavaParamRef<jobjectArray>& names_java,
const base::android::JavaParamRef<jobjectArray>& emails_java,
const base::android::JavaParamRef<jobjectArray>& tel_java,
const base::android::JavaParamRef<jobjectArray>& addresses_java) {
const base::android::JavaParamRef<jobjectArray>& addresses_java,
const base::android::JavaParamRef<jobjectArray>& icons_java) {
DCHECK(callback_);
base::Optional<std::vector<std::string>> names;
......@@ -134,6 +135,22 @@ void ContactsProviderAndroid::AddContact(
}
base::Optional<std::vector<blink::mojom::ContactIconBlobPtr>> icons;
if (icons_java) {
std::vector<blink::mojom::ContactIconBlobPtr> icons_vector;
for (const base::android::JavaRef<jbyteArray>& j_icon :
icons_java.ReadElements<jbyteArray>()) {
blink::mojom::ContactIconBlobPtr icon;
if (!blink::mojom::ContactIconBlob::Deserialize(
static_cast<jbyte*>(env->GetDirectBufferAddress(j_icon.obj())),
env->GetDirectBufferCapacity(j_icon.obj()), &icon)) {
continue;
}
icons_vector.push_back(std::move(icon));
}
icons = std::move(icons_vector);
}
blink::mojom::ContactInfoPtr contact = blink::mojom::ContactInfo::New(
std::move(names), std::move(emails), std::move(tel), std::move(addresses),
......
......@@ -38,7 +38,8 @@ class ContactsProviderAndroid : public ContactsProvider {
const base::android::JavaParamRef<jobjectArray>& names_java,
const base::android::JavaParamRef<jobjectArray>& emails_java,
const base::android::JavaParamRef<jobjectArray>& tel_java,
const base::android::JavaParamRef<jobjectArray>& addresses_java);
const base::android::JavaParamRef<jobjectArray>& addresses_java,
const base::android::JavaParamRef<jobjectArray>& icons_java);
// Signals the end of adding contacts to the list. The contact list is
// returned to the web page, the other params are logged via UKM.
......
......@@ -45,7 +45,8 @@ public class ContactsDialogHost implements ContactsPickerListener {
@CalledByNative
private void showDialog(boolean multiple, boolean includeNames, boolean includeEmails,
boolean includeTel, boolean includeAddresses, String formattedOrigin) {
boolean includeTel, boolean includeAddresses, boolean includeIcons,
String formattedOrigin) {
if (mWindowAndroid.getActivity().get() == null) {
ContactsDialogHostJni.get().endWithPermissionDenied(mNativeContactsProviderAndroid);
return;
......@@ -53,7 +54,7 @@ public class ContactsDialogHost implements ContactsPickerListener {
if (mWindowAndroid.hasPermission(Manifest.permission.READ_CONTACTS)) {
if (!UiUtils.showContactsPicker(mWindowAndroid.getActivity().get(), this, multiple,
includeNames, includeEmails, includeTel, includeAddresses,
includeNames, includeEmails, includeTel, includeAddresses, includeIcons,
formattedOrigin)) {
ContactsDialogHostJni.get().endWithPermissionDenied(mNativeContactsProviderAndroid);
}
......@@ -72,7 +73,7 @@ public class ContactsDialogHost implements ContactsPickerListener {
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if (!UiUtils.showContactsPicker(mWindowAndroid.getActivity().get(), this,
multiple, includeNames, includeEmails, includeTel,
includeAddresses, formattedOrigin)) {
includeAddresses, includeIcons, formattedOrigin)) {
ContactsDialogHostJni.get().endWithPermissionDenied(
mNativeContactsProviderAndroid);
}
......@@ -109,7 +110,10 @@ public class ContactsDialogHost implements ContactsPickerListener {
contact.serializedAddresses != null
? contact.serializedAddresses.toArray(
new ByteBuffer[contact.serializedAddresses.size()])
: null);
: null,
contact.serializedIcons != null ? contact.serializedIcons.toArray(
new ByteBuffer[contact.serializedIcons.size()])
: null);
}
ContactsDialogHostJni.get().endContactsList(
mNativeContactsProviderAndroid, percentageShared, propertiesRequested);
......@@ -124,7 +128,7 @@ public class ContactsDialogHost implements ContactsPickerListener {
@NativeMethods
interface Natives {
void addContact(long nativeContactsProviderAndroid, String[] names, String[] emails,
String[] tel, ByteBuffer[] addresses);
String[] tel, ByteBuffer[] addresses, ByteBuffer[] icons);
void endContactsList(
long nativeContactsProviderAndroid, int percentageShared, int propertiesRequested);
void endWithPermissionDenied(long nativeContactsProviderAndroid);
......
......@@ -220,6 +220,7 @@ android_library("ui_utils_java") {
"//base:base_java",
"//components/payments/mojom:mojom_java",
"//third_party/android_deps:android_support_v7_appcompat_java",
"//third_party/blink/public/mojom:mojom_platform_java",
]
}
......
......@@ -6,6 +6,7 @@ package org.chromium.ui;
import androidx.annotation.IntDef;
import org.chromium.blink.mojom.ContactIconBlob;
import org.chromium.payments.mojom.PaymentAddress;
import java.lang.annotation.Retention;
......@@ -26,9 +27,11 @@ public interface ContactsPickerListener {
public final List<String> emails;
public final List<String> tel;
public final List<ByteBuffer> serializedAddresses;
public final List<ByteBuffer> serializedIcons;
public Contact(List<String> contactNames, List<String> contactEmails,
List<String> contactTel, List<PaymentAddress> contactAddresses) {
List<String> contactTel, List<PaymentAddress> contactAddresses,
List<ContactIconBlob> contactIcons) {
names = contactNames;
emails = contactEmails;
tel = contactTel;
......@@ -41,6 +44,15 @@ public interface ContactsPickerListener {
} else {
serializedAddresses = null;
}
if (contactIcons != null) {
serializedIcons = new ArrayList<ByteBuffer>();
for (ContactIconBlob icon : contactIcons) {
serializedIcons.add(icon.serialize());
}
} else {
serializedIcons = null;
}
}
}
......
......@@ -99,12 +99,14 @@ public class UiUtils {
* @param includeEmails Whether to include emails of the contacts shared.
* @param includeTel Whether to include telephone numbers of the contacts shared.
* @param includeAddresses Whether to include addresses of the contacts shared.
* @param includeIcons Whether to include addresses of the contacts shared.
* @param formattedOrigin The origin the data will be shared with, formatted for display
* with the scheme omitted.
*/
void showContactsPicker(Context context, ContactsPickerListener listener,
boolean allowMultiple, boolean includeNames, boolean includeEmails,
boolean includeTel, boolean includeAddresses, String formattedOrigin);
boolean includeTel, boolean includeAddresses, boolean includeIcons,
String formattedOrigin);
/**
* Called when the contacts picker dialog has been dismissed.
......@@ -158,14 +160,15 @@ public class UiUtils {
* @param includeEmails Whether to include emails in the contact data returned.
* @param includeTel Whether to include telephone numbers in the contact data returned.
* @param includeAddresses Whether to include addresses of the contacts shared.
* @param includeIcons Whether to include icons of the contacts shared.
* @param formattedOrigin The origin the data will be shared with.
*/
public static boolean showContactsPicker(Context context, ContactsPickerListener listener,
boolean allowMultiple, boolean includeNames, boolean includeEmails, boolean includeTel,
boolean includeAddresses, String formattedOrigin) {
boolean includeAddresses, boolean includeIcons, String formattedOrigin) {
if (sContactsPickerDelegate == null) return false;
sContactsPickerDelegate.showContactsPicker(context, listener, allowMultiple, includeNames,
includeEmails, includeTel, includeAddresses, formattedOrigin);
includeEmails, includeTel, includeAddresses, includeIcons, formattedOrigin);
return true;
}
......
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