chromium/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/ChromePickerAdapter.java

// Copyright 2020 The Chromium Authors
// 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.annotation.SuppressLint;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;

import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;

import org.chromium.chrome.R;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.signin.services.DisplayableProfileData;
import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
import org.chromium.chrome.browser.signin.services.ProfileDataCache;
import org.chromium.components.browser_ui.contacts_picker.ContactDetails;
import org.chromium.components.browser_ui.contacts_picker.PickerAdapter;
import org.chromium.components.signin.AccountManagerFacadeProvider;
import org.chromium.components.signin.AccountUtils;
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;

/**
 * A {@link PickerAdapter} with special behavior tailored for Chrome.
 *
 * <p>Owner email is looked up in the {@link ProfileDataCache}, or, failing that, via the {@link
 * AccountManagerFacade}.
 */
public class ChromePickerAdapter extends PickerAdapter implements ProfileDataCache.Observer {
    private final Profile mProfile;

    // 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(Context context, Profile profile) {
        mProfile = profile;
        mProfileDataCache =
                ProfileDataCache.createWithoutBadge(context, R.dimen.contact_picker_icon_size);
    }

    // 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 accountEmail) {
        if (!mWaitingOnOwnerInfo || !TextUtils.equals(accountEmail, 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:

    /**
     * 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();
        }
        final @Nullable CoreAccountInfo defaultCoreAccountInfo =
                AccountUtils.getDefaultCoreAccountInfoIfFulfilled(
                        AccountManagerFacadeProvider.getInstance().getCoreAccountInfos());
        return defaultCoreAccountInfo != null ? defaultCoreAccountInfo.getEmail() : null;
    }

    @Override
    protected void addOwnerInfoToContacts(ArrayList<ContactDetails> contacts) {
        // Processing was not complete, finish the rest asynchronously. Flow continues in
        // onProfileDataUpdated.
        mWaitingOnOwnerInfo = true;
        addProfileDataObserver();
        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(mProfile.getOriginalProfile());
        return identityManager.getPrimaryAccountInfo(ConsentLevel.SIGNIN);
    }
}