chromium/chrome/android/java/src/org/chromium/chrome/browser/autofill/vcn/AutofillVcnEnrollBottomSheetViewBinder.java

// Copyright 2023 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.autofill.vcn;

import android.content.Context;
import android.graphics.Bitmap;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.method.LinkMovementMethod;
import android.text.style.ClickableSpan;
import android.view.View;
import android.widget.TextView;

import org.chromium.chrome.browser.autofill.AutofillUiUtils;
import org.chromium.chrome.browser.autofill.vcn.AutofillVcnEnrollBottomSheetProperties.Description;
import org.chromium.chrome.browser.autofill.vcn.AutofillVcnEnrollBottomSheetProperties.IssuerIcon;
import org.chromium.chrome.browser.autofill.vcn.AutofillVcnEnrollBottomSheetProperties.LegalMessages;
import org.chromium.ui.modelutil.PropertyKey;
import org.chromium.ui.modelutil.PropertyModel;

/** The view-binder of the autofill virtual card enrollment bottom sheet UI. */
/*package*/ class AutofillVcnEnrollBottomSheetViewBinder {
    /**
     * Updates the view based on changes in the model.
     *
     * @param model The updated model to read.
     * @param view The view to update.
     * @param propertyKey The property of the model that has changed.
     */
    static void bind(
            PropertyModel model, AutofillVcnEnrollBottomSheetView view, PropertyKey propertyKey) {
        if (AutofillVcnEnrollBottomSheetProperties.MESSAGE_TEXT == propertyKey) {
            view.mDialogTitle.setText(
                    model.get(AutofillVcnEnrollBottomSheetProperties.MESSAGE_TEXT));

        } else if (AutofillVcnEnrollBottomSheetProperties.DESCRIPTION == propertyKey) {
            view.mVirtualCardDescription.setText(
                    getDescriptionSpan(
                            model.get(AutofillVcnEnrollBottomSheetProperties.DESCRIPTION)));
            view.mVirtualCardDescription.setMovementMethod(LinkMovementMethod.getInstance());

        } else if (AutofillVcnEnrollBottomSheetProperties.CARD_CONTAINER_ACCESSIBILITY_DESCRIPTION
                == propertyKey) {
            view.mCardContainer.setContentDescription(
                    model.get(
                            AutofillVcnEnrollBottomSheetProperties
                                    .CARD_CONTAINER_ACCESSIBILITY_DESCRIPTION));

        } else if (AutofillVcnEnrollBottomSheetProperties.ISSUER_ICON == propertyKey) {
            view.mIssuerIcon.setImageBitmap(
                    scaleBitmap(model.get(AutofillVcnEnrollBottomSheetProperties.ISSUER_ICON)));

        } else if (AutofillVcnEnrollBottomSheetProperties.CARD_LABEL == propertyKey) {
            view.mCardLabel.setText(model.get(AutofillVcnEnrollBottomSheetProperties.CARD_LABEL));

        } else if (AutofillVcnEnrollBottomSheetProperties.CARD_DESCRIPTION == propertyKey) {
            view.mCardDescription.setText(
                    model.get(AutofillVcnEnrollBottomSheetProperties.CARD_DESCRIPTION));

        } else if (AutofillVcnEnrollBottomSheetProperties.GOOGLE_LEGAL_MESSAGES == propertyKey) {
            setLegalMessageOrHideIfEmpty(
                    view.mContentView.getContext(),
                    view.mGoogleLegalMessage,
                    model.get(AutofillVcnEnrollBottomSheetProperties.GOOGLE_LEGAL_MESSAGES));

        } else if (AutofillVcnEnrollBottomSheetProperties.ISSUER_LEGAL_MESSAGES == propertyKey) {
            setLegalMessageOrHideIfEmpty(
                    view.mContentView.getContext(),
                    view.mIssuerLegalMessage,
                    model.get(AutofillVcnEnrollBottomSheetProperties.ISSUER_LEGAL_MESSAGES));

        } else if (AutofillVcnEnrollBottomSheetProperties.ACCEPT_BUTTON_LABEL == propertyKey) {
            view.mAcceptButton.setText(
                    model.get(AutofillVcnEnrollBottomSheetProperties.ACCEPT_BUTTON_LABEL));

        } else if (AutofillVcnEnrollBottomSheetProperties.CANCEL_BUTTON_LABEL == propertyKey) {
            view.mCancelButton.setText(
                    model.get(AutofillVcnEnrollBottomSheetProperties.CANCEL_BUTTON_LABEL));
        } else if (AutofillVcnEnrollBottomSheetProperties.SHOW_LOADING_STATE == propertyKey) {
            if (model.get(AutofillVcnEnrollBottomSheetProperties.SHOW_LOADING_STATE)) {
                view.mAcceptButton.setVisibility(View.GONE);
                view.mCancelButton.setVisibility(View.GONE);
                view.mLoadingView.showLoadingUI(/* skipDelay= */ true);
                view.mLoadingViewContainer.setVisibility(View.VISIBLE);
            } else {
                view.mLoadingViewContainer.setVisibility(View.GONE);
                view.mLoadingView.hideLoadingUI();
                view.mAcceptButton.setVisibility(View.VISIBLE);
                view.mCancelButton.setVisibility(View.VISIBLE);
            }
        } else if (AutofillVcnEnrollBottomSheetProperties.LOADING_DESCRIPTION == propertyKey) {
            view.mLoadingViewContainer.setContentDescription(
                    model.get(AutofillVcnEnrollBottomSheetProperties.LOADING_DESCRIPTION));
        }
    }

    // Returns the virtual card description text with a "learn more" link.
    private static SpannableString getDescriptionSpan(Description description) {
        SpannableString result = new SpannableString(new String());
        if (description == null
                || description.mText == null
                || description.mText.isEmpty()
                || description.mLearnMoreLinkText == null
                || description.mLearnMoreLinkText.isEmpty()
                || description.mLearnMoreLinkUrl == null
                || description.mLearnMoreLinkUrl.isEmpty()
                || description.mLinkOpener == null) {
            return result;
        }

        int learnMoreStart = description.mText.indexOf(description.mLearnMoreLinkText);
        if (learnMoreStart < 0) return result;

        int learnMoreEnd = learnMoreStart + description.mLearnMoreLinkText.length();
        result = new SpannableString(description.mText);
        result.setSpan(
                new ClickableSpan() {
                    @Override
                    public void onClick(View view) {
                        description.mLinkOpener.openLink(
                                description.mLearnMoreLinkUrl, description.mLearnMoreLinkType);
                    }
                },
                learnMoreStart,
                learnMoreEnd,
                Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
        return result;
    }

    // Returns a scaled bitmap.
    private static Bitmap scaleBitmap(IssuerIcon issuerIcon) {
        return issuerIcon != null
                        && issuerIcon.mBitmap != null
                        && issuerIcon.mWidth > 0
                        && issuerIcon.mHeight > 0
                ? Bitmap.createScaledBitmap(
                        issuerIcon.mBitmap,
                        issuerIcon.mWidth,
                        issuerIcon.mHeight,
                        /* filter= */ true)
                : null;
    }

    // Returns the legal message text formatted with links.
    private static SpannableStringBuilder getLegalMessageSpan(
            Context context, LegalMessages legalMessages) {
        return legalMessages != null
                        && legalMessages.mLines != null
                        && !legalMessages.mLines.isEmpty()
                        && legalMessages.mLinkOpener != null
                ? AutofillUiUtils.getSpannableStringForLegalMessageLines(
                        context,
                        legalMessages.mLines,
                        /* underlineLinks= */ true,
                        (String url) -> {
                            legalMessages.mLinkOpener.openLink(url, legalMessages.mLinkType);
                        })
                : new SpannableStringBuilder();
    }

    // Sets the legal message text formatted with links, or hides the text view for empty legal
    // message.
    private static void setLegalMessageOrHideIfEmpty(
            Context context, TextView textView, LegalMessages legalMessages) {
        SpannableStringBuilder stringBuilder = getLegalMessageSpan(context, legalMessages);
        if (stringBuilder.length() > 0) {
            textView.setText(stringBuilder);
            textView.setMovementMethod(LinkMovementMethod.getInstance());
            textView.setVisibility(View.VISIBLE);
        } else {
            textView.setVisibility(View.GONE);
        }
    }
}