Commit 92739742 authored by Evan Stade's avatar Evan Stade Committed by Commit Bot

WebLayer contacts picker: Split Chrome logic out of PickerAdapter

PickerAdapter is split into a base PickerAdapter and a
ChromePickerAdapter. Inheritance is used instead of a delegate object
because ChromePickerAdapter overrides some Adapter methods.

The code that uses the signin component is left in ChromePickerAdapter,
even though it's componentized, as WebLayer doesn't currently use this
component. This code may also be componentized later, but the plan is
for WebLayer to initially skip the special handling for the owner
contact.

After this change, all of the contacts_picker package save
ChromePickerAdapter can be moved to //components.

Bug: 1016938
Change-Id: Iba554c2064e1e302a05455b8875b409a14c3b199
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2324762Reviewed-by: default avatarTed Choc <tedchoc@chromium.org>
Reviewed-by: default avatarFinnur Thorarinsson <finnur@chromium.org>
Commit-Queue: Evan Stade <estade@chromium.org>
Cr-Commit-Position: refs/heads/master@{#799722}
parent 7763a092
......@@ -344,6 +344,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/compositor/scene_layer/TabListSceneLayer.java",
"java/src/org/chromium/chrome/browser/compositor/scene_layer/TabStripSceneLayer.java",
"java/src/org/chromium/chrome/browser/consent_auditor/ConsentAuditorBridge.java",
"java/src/org/chromium/chrome/browser/contacts_picker/ChromePickerAdapter.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",
......
// Copyright 2020 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.accounts.Account;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import androidx.recyclerview.widget.RecyclerView;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.signin.DisplayableProfileData;
import org.chromium.chrome.browser.signin.IdentityServicesProvider;
import org.chromium.chrome.browser.signin.ProfileDataCache;
import org.chromium.components.signin.AccountManagerFacade;
import org.chromium.components.signin.AccountManagerFacadeProvider;
import org.chromium.components.signin.base.CoreAccountInfo;
import org.chromium.components.signin.identitymanager.ConsentLevel;
import org.chromium.components.signin.identitymanager.IdentityManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* A {@link PickerAdapter} with special behavior tailored for Chrome.
*
* Owner email is looked up in the {@link ProfileDataCache}, or, failing that, via the {@link
* AccountManagerFacade}.
*/
public class ChromePickerAdapter extends PickerAdapter implements ProfileDataCache.Observer {
// The profile data cache to consult when figuring out the signed in user.
private ProfileDataCache mProfileDataCache;
// Whether an observer for ProfileDataCache has been registered.
private boolean mObserving;
// Whether owner info is being fetched asynchronously.
private boolean mWaitingOnOwnerInfo;
public ChromePickerAdapter() {}
// Adapter:
@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
addProfileDataObserver();
}
@Override
public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
super.onDetachedFromRecyclerView(recyclerView);
removeProfileDataObserver();
}
// ProfileDataCache.Observer:
@Override
public void onProfileDataUpdated(String accountId) {
if (!mWaitingOnOwnerInfo || !TextUtils.equals(accountId, getOwnerEmail())) {
return;
}
// Now that we've received an update for the right accountId, we can stop listening and
// update our records.
mWaitingOnOwnerInfo = false;
removeProfileDataObserver();
// TODO(finnur): crbug.com/1021477 - Maintain an member instance of this.
DisplayableProfileData profileData =
mProfileDataCache.getProfileDataOrDefault(getOwnerEmail());
ContactDetails contact = getAllContacts().get(0);
Drawable icon = profileData.getImage();
contact.setSelfIcon(icon);
update();
}
private void addProfileDataObserver() {
if (!mObserving) {
mObserving = true;
mProfileDataCache.addObserver(this);
}
}
private void removeProfileDataObserver() {
if (mObserving) {
mObserving = false;
mProfileDataCache.removeObserver(this);
}
}
// PickerAdapter:
@Override
public void init(PickerCategoryView categoryView, Context context, String formattedOrigin) {
mProfileDataCache = new ProfileDataCache(context,
context.getResources().getDimensionPixelSize(R.dimen.contact_picker_icon_size));
super.init(categoryView, context, formattedOrigin);
}
/**
* Returns the email for the currently signed-in user. If that is not available, return the
* first Google account associated with this phone instead.
*/
@Override
protected String findOwnerEmail() {
CoreAccountInfo coreAccountInfo = getCoreAccountInfo();
if (coreAccountInfo != null) {
return coreAccountInfo.getEmail();
}
AccountManagerFacade manager = AccountManagerFacadeProvider.getInstance();
List<Account> accounts = manager.tryGetGoogleAccounts();
if (accounts.size() > 0) {
return accounts.get(0).name;
}
return null;
}
@Override
protected void addOwnerInfoToContacts(ArrayList<ContactDetails> contacts) {
// Processing was not complete, finish the rest asynchronously. Flow continues in
// onProfileDataUpdated.
mWaitingOnOwnerInfo = true;
addProfileDataObserver();
mProfileDataCache.update(Collections.singletonList(getOwnerEmail()));
contacts.add(0, constructOwnerInfo(getOwnerEmail()));
}
/**
* Constructs a {@link ContactDetails} record for the currently signed in user. Name is obtained
* via the {@link DisplayableProfileData}, if available, or (alternatively) using the signed in
* information.
* @param ownerEmail The email for the currently signed in user.
* @return The contact info for the currently signed in user.
*/
@SuppressLint("HardwareIds")
private ContactDetails constructOwnerInfo(String ownerEmail) {
DisplayableProfileData profileData = mProfileDataCache.getProfileDataOrDefault(ownerEmail);
String name = profileData.getFullNameOrEmail();
if (TextUtils.isEmpty(name) || TextUtils.equals(name, ownerEmail)) {
name = CoreAccountInfo.getEmailFrom(getCoreAccountInfo());
}
ContactDetails contact = new ContactDetails(ContactDetails.SELF_CONTACT_ID, name,
Collections.singletonList(ownerEmail), /*phoneNumbers=*/null, /*addresses=*/null);
Drawable icon = profileData.getImage();
contact.setIsSelf(true);
contact.setSelfIcon(icon);
return contact;
}
private CoreAccountInfo getCoreAccountInfo() {
// Since this is read-only operation to obtain email address, always using regular profile
// for both regular and off-the-record profile is safe.
IdentityManager identityManager = IdentityServicesProvider.get().getIdentityManager(
Profile.getLastUsedRegularProfile());
return identityManager.getPrimaryAccountInfo(ConsentLevel.SYNC);
}
}
......@@ -24,6 +24,7 @@ public class ContactsPickerDialog
/**
* The ContactsPickerDialog constructor.
* @param windowAndroid The window associated with the main Activity.
* @param adapter An uninitialized {@link PickerAdapter} for this dialog.
* @param listener The listener object that gets notified when an action is taken.
* @param allowMultiple Whether the contacts picker should allow multiple items to be selected.
* @param includeNames Whether the contacts data returned should include names.
......@@ -34,14 +35,14 @@ public class ContactsPickerDialog
* @param formattedOrigin The origin the data will be shared with, formatted for display with
* the scheme omitted.
*/
public ContactsPickerDialog(WindowAndroid windowAndroid, ContactsPickerListener listener,
boolean allowMultiple, boolean includeNames, boolean includeEmails, boolean includeTel,
boolean includeAddresses, boolean includeIcons, String formattedOrigin,
VrModeProvider vrModeProvider) {
public ContactsPickerDialog(WindowAndroid windowAndroid, PickerAdapter adapter,
ContactsPickerListener listener, boolean allowMultiple, boolean includeNames,
boolean includeEmails, boolean includeTel, boolean includeAddresses,
boolean includeIcons, String formattedOrigin, VrModeProvider vrModeProvider) {
super(windowAndroid.getContext().get(), R.style.Theme_Chromium_Fullscreen);
// Initialize the main content view.
mCategoryView = new PickerCategoryView(windowAndroid, allowMultiple, includeNames,
mCategoryView = new PickerCategoryView(windowAndroid, adapter, allowMultiple, includeNames,
includeEmails, includeTel, includeAddresses, includeIcons, formattedOrigin, this,
vrModeProvider);
mCategoryView.initialize(this, listener);
......
......@@ -126,14 +126,27 @@ public class PickerCategoryView extends OptimizedFrameLayout
public final boolean includeIcons;
/**
* @param windowAndroid The Activity window the Contacts Picker is associated with.
* @param adapter An uninitialized PickerAdapter for this dialog, which may contain
* embedder-specific behaviors. The PickerCategoryView will initialized it.
* @param multiSelectionAllowed Whether the contacts picker should allow multiple items to be
* selected.
* selected.
* @param shouldIncludeNames Whether to allow sharing of names of contacts.
* @param shouldIncludeEmails Whether to allow sharing of contact emails.
* @param shouldIncludeTel Whether to allow sharing of contact telephone numbers.
* @param shouldIncludeAddresses Whether to allow sharing of contact (physical) addresses.
* @param shouldIncludeIcons Whether to allow sharing of contact icons.
* @param formattedOrigin The origin receiving the contact details, formatted for display in the
* UI.
* @param delegate A delegate listening for events from the toolbar.
* @param vrModeProvider Provides accessors for VR mode state.
*/
@SuppressWarnings("unchecked") // mSelectableListLayout
public PickerCategoryView(WindowAndroid windowAndroid, boolean multiSelectionAllowed,
boolean shouldIncludeNames, boolean shouldIncludeEmails, boolean shouldIncludeTel,
boolean shouldIncludeAddresses, boolean shouldIncludeIcons, String formattedOrigin,
ContactsPickerToolbar.ContactsToolbarDelegate delegate, VrModeProvider vrModeProvider) {
public PickerCategoryView(WindowAndroid windowAndroid, PickerAdapter adapter,
boolean multiSelectionAllowed, boolean shouldIncludeNames, boolean shouldIncludeEmails,
boolean shouldIncludeTel, boolean shouldIncludeAddresses, boolean shouldIncludeIcons,
String formattedOrigin, ContactsPickerToolbar.ContactsToolbarDelegate delegate,
VrModeProvider vrModeProvider) {
super(windowAndroid.getContext().get(), null);
mWindowAndroid = windowAndroid;
......@@ -162,7 +175,8 @@ public class PickerCategoryView extends OptimizedFrameLayout
R.string.contacts_picker_no_contacts_found,
R.string.contacts_picker_no_contacts_found);
mPickerAdapter = new PickerAdapter(this, context, formattedOrigin);
mPickerAdapter = adapter;
mPickerAdapter.init(this, context, formattedOrigin);
mRecyclerView = mSelectableListLayout.initializeRecyclerView(mPickerAdapter);
int titleId = multiSelectionAllowed ? R.string.contacts_picker_select_contacts
: R.string.contacts_picker_select_contact;
......
......@@ -38,6 +38,7 @@ import org.chromium.chrome.browser.DeferredStartupHandler;
import org.chromium.chrome.browser.DevToolsServer;
import org.chromium.chrome.browser.banners.AppBannerManager;
import org.chromium.chrome.browser.bookmarkswidget.BookmarkWidgetProvider;
import org.chromium.chrome.browser.contacts_picker.ChromePickerAdapter;
import org.chromium.chrome.browser.contacts_picker.ContactsPickerDialog;
import org.chromium.chrome.browser.content_capture.ContentCaptureHistoryDeletionObserver;
import org.chromium.chrome.browser.crash.CrashUploadCountStore;
......@@ -248,9 +249,10 @@ public class ProcessInitializationHandler {
String formattedOrigin) {
// TODO(crbug.com/1117536): remove this cast.
ChromeActivity activity = (ChromeActivity) context;
mDialog = new ContactsPickerDialog(activity.getWindowAndroid(), listener,
allowMultiple, includeNames, includeEmails, includeTel, includeAddresses,
includeIcons, formattedOrigin, new VrModeProviderImpl());
mDialog = new ContactsPickerDialog(activity.getWindowAndroid(),
new ChromePickerAdapter(), listener, allowMultiple, includeNames,
includeEmails, includeTel, includeAddresses, includeIcons, formattedOrigin,
new VrModeProviderImpl());
mDialog.getWindow().getAttributes().windowAnimations =
R.style.PickerDialogAnimation;
mDialog.show();
......
......@@ -170,9 +170,10 @@ public class ContactsPickerDialogTest
public ContactsPickerDialog call() {
final ContactsPickerDialog dialog = new ContactsPickerDialog(
mActivityTestRule.getActivity().getWindowAndroid(),
ContactsPickerDialogTest.this, multiselect, includeNames,
includeEmails, includeTel, includeAddresses, includeIcons,
"example.com", new VrModeProvider() {
new ChromePickerAdapter(), ContactsPickerDialogTest.this,
multiselect, includeNames, includeEmails, includeTel,
includeAddresses, includeIcons, "example.com",
new VrModeProvider() {
@Override
public boolean isInVr() {
return false;
......
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