chromium/chrome/browser/ui/android/fast_checkout/internal/java/src/org/chromium/chrome/browser/ui/fast_checkout/FastCheckoutSheetContent.java

// Copyright 2022 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.ui.fast_checkout;

import static org.chromium.chrome.browser.ui.fast_checkout.FastCheckoutProperties.ScreenType.AUTOFILL_PROFILE_SCREEN;
import static org.chromium.chrome.browser.ui.fast_checkout.FastCheckoutProperties.ScreenType.CREDIT_CARD_SCREEN;
import static org.chromium.chrome.browser.ui.fast_checkout.FastCheckoutProperties.ScreenType.HOME_SCREEN;

import android.view.View;
import android.view.View.MeasureSpec;
import android.view.ViewGroup;

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

import org.chromium.chrome.browser.util.ChromeAccessibilityUtil;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
import org.chromium.ui.UiUtils;
import org.chromium.ui.base.LocalizationUtils;

/** The {@link BottomSheetContent} for Fast Checkout. */
public class FastCheckoutSheetContent implements BottomSheetContent {
    private static final float MAX_VISIBLE_WHOLE_ADDRESSES = 2.5f;
    private static final float MAX_VISIBLE_WHOLE_CREDIT_CARDS = 3.5f;

    private final FastCheckoutSheetState mState;
    private final View mContentView;

    /**
     * Constructs a FastCheckoutSheetContent which creates, modifies, and shows the bottom sheet.
     */
    FastCheckoutSheetContent(FastCheckoutSheetState state, View contentView) {
        mState = state;
        mContentView = contentView;

        // Apply RTL layout changes for tests.
        int layoutDirection =
                LocalizationUtils.isLayoutRtl()
                        ? View.LAYOUT_DIRECTION_RTL
                        : View.LAYOUT_DIRECTION_LTR;
        mContentView.setLayoutDirection(layoutDirection);
    }

    @Override
    public View getContentView() {
        return mContentView;
    }

    @Nullable
    @Override
    public View getToolbarView() {
        return null;
    }

    @Override
    public int getVerticalScrollOffset() {
        if (isAutofillProfileScreen() || isCreditCardScreen()) {
            RecyclerView recyclerView =
                    getContentView().findViewById(R.id.fast_checkout_detail_screen_recycler_view);
            return recyclerView.computeVerticalScrollOffset();
        }

        return 0;
    }

    @Override
    public void destroy() {}

    @Override
    public int getPriority() {
        return ContentPriority.HIGH;
    }

    @Override
    public boolean swipeToDismissEnabled() {
        return false;
    }

    @Override
    public boolean skipHalfStateOnScrollingDown() {
        return false;
    }

    @Override
    public int getPeekHeight() {
        return HeightMode.DISABLED;
    }

    @Override
    public float getHalfHeightRatio() {
        if (shouldWrapContent()) {
            return HeightMode.DISABLED;
        }
        return Math.min(getDesiredDetailSheetHeight(), mState.getContainerHeight())
                / (float) mState.getContainerHeight();
    }

    @Override
    public float getFullHeightRatio() {
        if (shouldWrapContent()) {
            return HeightMode.WRAP_CONTENT;
        }
        // This would ideally also be `WRAP_CONTENT` but that disables half height mode.
        // `mBottomSheetController.getContainerHeight()` is the height of the bottom sheet's
        // container, i.e. the screen.
        return Math.min(getBottomSheetHeight(), mState.getContainerHeight())
                / (float) mState.getContainerHeight();
    }

    @Override
    public int getSheetContentDescriptionStringId() {
        return R.string.fast_checkout_content_description;
    }

    @Override
    public int getSheetClosedAccessibilityStringId() {
        return R.string.fast_checkout_sheet_closed;
    }

    @Override
    public int getSheetHalfHeightAccessibilityStringId() {
        return R.string.fast_checkout_content_description;
    }

    @Override
    public int getSheetFullHeightAccessibilityStringId() {
        return R.string.fast_checkout_content_description;
    }

    private boolean isHomeScreen() {
        return mState.getCurrentScreen() == HOME_SCREEN;
    }

    private boolean isAutofillProfileScreen() {
        return mState.getCurrentScreen() == AUTOFILL_PROFILE_SCREEN;
    }

    private boolean isCreditCardScreen() {
        return mState.getCurrentScreen() == CREDIT_CARD_SCREEN;
    }

    private float getBottomSheetHeight() {
        ViewGroup parent = (ViewGroup) getContentView().getParent();
        getContentView()
                .measure(
                        MeasureSpec.makeMeasureSpec(parent.getWidth(), MeasureSpec.EXACTLY),
                        MeasureSpec.makeMeasureSpec(parent.getHeight(), MeasureSpec.AT_MOST));
        return getContentView().getMeasuredHeight();
    }

    private boolean shouldWrapContent() {
        // Always got to FULL state in accessibility mode or when an external keyboard is connected
        // to allow scrolling the RecyclerView with e.g. side swipes or the Tab key.
        if (ChromeAccessibilityUtil.get().isAccessibilityEnabled()
                || UiUtils.isHardwareKeyboardAttached()) {
            return true;
        }
        // If there are 1 or 2 Autofill profiles, it shows all items fully. For 3+ suggestions, it
        // shows the first 2.5 suggestions to encourage scrolling.
        boolean shouldWrapAutofillProfiles =
                isAutofillProfileScreen()
                        && mState.getNumOfAutofillProfiles() < MAX_VISIBLE_WHOLE_ADDRESSES;
        // If there are less than 4 credit cards, it shows all items fully. For 4+ suggestions, it
        // shows the first 3.5 suggestions to encourage scrolling.
        boolean shouldWrapCreditCards =
                isCreditCardScreen()
                        && mState.getNumOfCreditCards() < MAX_VISIBLE_WHOLE_CREDIT_CARDS;
        return isHomeScreen() || shouldWrapAutofillProfiles || shouldWrapCreditCards;
    }

    private int getDesiredDetailSheetHeight() {
        // TODO(crbug.com/40228235): Investigate measuring heights dynamically instead of using
        // hard-coded values.
        int height = getDimensionPixelSize(R.dimen.fast_checkout_detail_sheet_header_height);
        if (isAutofillProfileScreen()) {
            height +=
                    Math.round(
                            MAX_VISIBLE_WHOLE_ADDRESSES
                                    * getDimensionPixelSize(
                                            R.dimen
                                                    .fast_checkout_detail_sheet_height_single_address));
        } else {
            height +=
                    Math.round(
                            MAX_VISIBLE_WHOLE_CREDIT_CARDS
                                    * getDimensionPixelSize(
                                            R.dimen
                                                    .fast_checkout_detail_sheet_height_single_credit_card));
        }
        return height;
    }

    private int getDimensionPixelSize(int id) {
        return mContentView.getContext().getResources().getDimensionPixelSize(id);
    }
}