chromium/components/browser_ui/contacts_picker/android/java/src/org/chromium/components/browser_ui/contacts_picker/ContactView.java

// Copyright 2018 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.components.browser_ui.contacts_picker;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.core.graphics.drawable.RoundedBitmapDrawable;
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;

import org.chromium.components.browser_ui.widget.selectable_list.SelectableItemView;
import org.chromium.components.browser_ui.widget.selectable_list.SelectionDelegate;
import org.chromium.ui.modaldialog.ModalDialogManager;
import org.chromium.ui.modaldialog.ModalDialogProperties;
import org.chromium.ui.modelutil.PropertyModel;

import java.util.List;

/** A container class for a view showing a contact in the Contacts Picker. */
public class ContactView extends SelectableItemView<ContactDetails> {
    // Our context.
    private Context mContext;

    // Our parent category.
    private PickerCategoryView mCategoryView;

    // Our selection delegate.
    private SelectionDelegate<ContactDetails> mSelectionDelegate;

    // The details of the contact shown.
    private ContactDetails mContactDetails;

    // The display name of the contact.
    private TextView mDisplayName;

    // The contact details for the contact.
    private TextView mAddress;
    private TextView mAddressOverflowCount;
    private TextView mEmail;
    private TextView mEmailOverflowCount;
    private TextView mPhoneNumber;
    private TextView mPhoneNumberOverflowCount;

    // The UI that indicates this is the owner of the device.
    private ImageView mStar;

    // The dialog manager to use to show contact details.
    private ModalDialogManager mManager;

    // The property model listing the contents of the contact details dialog.
    private PropertyModel mModel;

    /** Constructor for inflating from XML. */
    public ContactView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mContext = context;

        setSelectionOnLongClick(false);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();

        mDisplayName = findViewById(R.id.title);
        mAddress = findViewById(R.id.address);
        mAddressOverflowCount = findViewById(R.id.address_overflow_count);
        mEmail = findViewById(R.id.email);
        mEmailOverflowCount = findViewById(R.id.email_overflow_count);
        mPhoneNumber = findViewById(R.id.telephone_number);
        mPhoneNumberOverflowCount = findViewById(R.id.telephone_number_overflow_count);
        mStar = findViewById(R.id.star);

        mAddressOverflowCount.setOnClickListener(this);
        mEmailOverflowCount.setOnClickListener(this);
        mPhoneNumberOverflowCount.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        int id = view.getId();
        if (id == R.id.address_overflow_count
                || id == R.id.email_overflow_count
                || id == R.id.telephone_number_overflow_count) {
            onLongClick(this);
        } else {
            super.onClick(view);
        }
    }

    @Override
    public void handleNonSelectionClick() {
        // Selection is handled in onClick for the parent class.
        assert false;
    }

    @Override
    public boolean onLongClick(View view) {
        mManager = mCategoryView.getModalDialogManager();
        ModalDialogProperties.Controller controller =
                new ModalDialogProperties.Controller() {
                    @Override
                    public void onClick(PropertyModel model, int buttonType) {
                        mManager.dismissDialog(model, buttonType);
                        mModel = null;
                        mManager = null;
                    }

                    @Override
                    public void onDismiss(PropertyModel model, int dismissalCause) {}
                };
        mModel =
                new PropertyModel.Builder(ModalDialogProperties.ALL_KEYS)
                        .with(ModalDialogProperties.CONTROLLER, controller)
                        .with(ModalDialogProperties.TITLE, mContactDetails.getDisplayName())
                        .with(
                                ModalDialogProperties.MESSAGE_PARAGRAPH_1,
                                mContactDetails.getContactDetailsAsString(
                                        PickerAdapter.includesAddresses(),
                                        PickerAdapter.includesEmails(),
                                        PickerAdapter.includesTelephones()))
                        .with(
                                ModalDialogProperties.POSITIVE_BUTTON_TEXT,
                                mContext.getResources(),
                                R.string.close)
                        .build();
        mModel.set(ModalDialogProperties.TITLE_ICON, getStartIconDrawable());
        mManager.showDialog(mModel, ModalDialogManager.ModalDialogType.APP);
        return true;
    }

    @Override
    public void onSelectionStateChange(List<ContactDetails> selectedItems) {
        // If the user cancels the dialog before this object has initialized, the SelectionDelegate
        // will try to notify us that all selections have been cleared. However, we don't need to
        // process that message.
        if (mContactDetails == null) return;

        // When SelectAll or Undo is used, the underlying UI must be updated
        // to reflect the changes.
        boolean selected = selectedItems.contains(mContactDetails);
        boolean checked = super.isChecked();
        if (selected != checked) super.toggle();
    }

    /**
     * Sets the {@link PickerCategoryView} for this ContactView.
     *
     * @param categoryView The category view showing the images. Used to access common functionality
     *     and sizes and retrieve the {@link SelectionDelegate}.
     */
    public void setCategoryView(PickerCategoryView categoryView) {
        mCategoryView = categoryView;
        mSelectionDelegate = mCategoryView.getSelectionDelegate();
        setSelectionDelegate(mSelectionDelegate);
    }

    private void updateTextViewVisibilityAndContent(TextView view, String text) {
        view.setVisibility(TextUtils.isEmpty(text) ? View.GONE : View.VISIBLE);
        view.setText(text);
    }

    /**
     * Completes the initialization of the ContactView. Must be called before the {@link
     * ContactView} can respond to click events.
     *
     * @param contactDetails The details about the contact represented by this ContactView.
     * @param icon The icon to show for the contact (or null if not loaded yet).
     */
    public void initialize(ContactDetails contactDetails, Bitmap icon) {
        resetTile();

        mContactDetails = contactDetails;
        setItem(contactDetails);

        String displayName = contactDetails.getDisplayName();
        mDisplayName.setText(displayName);

        ContactDetails.AbbreviatedContactDetails details =
                contactDetails.getAbbreviatedContactDetails(
                        /* includeAddresses= */ PickerAdapter.includesAddresses(),
                        /* includeEmails= */ PickerAdapter.includesEmails(),
                        /* includeTels= */ PickerAdapter.includesTelephones(),
                        mContext.getResources());

        updateTextViewVisibilityAndContent(mAddress, details.primaryAddress);
        updateTextViewVisibilityAndContent(mAddressOverflowCount, details.overflowAddressCount);
        updateTextViewVisibilityAndContent(mEmail, details.primaryEmail);
        updateTextViewVisibilityAndContent(mEmailOverflowCount, details.overflowEmailCount);
        updateTextViewVisibilityAndContent(mPhoneNumber, details.primaryTelephoneNumber);
        updateTextViewVisibilityAndContent(
                mPhoneNumberOverflowCount, details.overflowTelephoneNumberCount);

        if (contactDetails.isSelf()) mStar.setVisibility(View.VISIBLE);

        if (icon == null || !PickerAdapter.includesIcons()) {
            icon =
                    mCategoryView
                            .getIconGenerator()
                            .generateIconForText(contactDetails.getDisplayNameAbbreviation());
            setStartIconDrawable(new BitmapDrawable(getResources(), icon));
        } else {
            setIconBitmap(icon);
        }
    }

    /**
     * Sets the icon to display for the contact and fade it into view.
     *
     * @param icon The icon to display.
     */
    public void setIconBitmap(Bitmap icon) {
        Resources resources = mContext.getResources();
        RoundedBitmapDrawable drawable = RoundedBitmapDrawableFactory.create(resources, icon);
        drawable.setCircular(true);
        setStartIconDrawable(drawable);
    }

    /**
     * Resets the view to its starting state, which is necessary when the view is about to be
     * re-used.
     */
    private void resetTile() {
        setStartIconDrawable(null);
        mDisplayName.setText("");
        mAddress.setText("");
        mAddressOverflowCount.setText("");
        mEmail.setText("");
        mEmailOverflowCount.setText("");
        mPhoneNumber.setText("");
        mPhoneNumberOverflowCount.setText("");
        mStar.setVisibility(View.GONE);
    }
}