chromium/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/ActionChipsView.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.omnibox.suggestions.base;

import android.content.Context;
import android.view.KeyEvent;

import androidx.annotation.NonNull;
import androidx.annotation.Px;
import androidx.annotation.VisibleForTesting;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import org.chromium.build.annotations.CheckDiscard;
import org.chromium.chrome.browser.omnibox.R;
import org.chromium.chrome.browser.omnibox.styles.OmniboxResourceProvider;
import org.chromium.chrome.browser.omnibox.suggestions.RecyclerViewSelectionController;
import org.chromium.chrome.browser.util.KeyNavigationUtil;
import org.chromium.components.browser_ui.widget.chips.ChipView;

/**
 * Container view for the {@link ChipView}. Chips should be initially horizontally aligned with the
 * Content view and stretch to the end of the encompassing BaseSuggestionView.
 */
public class ActionChipsView extends RecyclerView {
    private @NonNull RecyclerViewSelectionController mSelectionController;

    /**
     * Constructs a new pedal view.
     *
     * @param context The context used to construct the chip view.
     */
    public ActionChipsView(Context context) {
        super(context);

        setItemAnimator(null);
        setId(R.id.omnibox_actions_carousel);
        var layoutManager = new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false);
        setLayoutManager(layoutManager);

        mSelectionController = new RecyclerViewSelectionController(layoutManager);
        mSelectionController.setCycleThroughNoSelection(true);
        addOnChildAttachStateChangeListener(mSelectionController);

        setMinimumHeight(
                getResources()
                        .getDimensionPixelSize(R.dimen.omnibox_action_chips_container_height));
        setPaddingRelative(
                0,
                0,
                0,
                getResources().getDimensionPixelSize(R.dimen.omnibox_suggestion_content_padding));

        final @Px int leadInSpace =
                OmniboxResourceProvider.getSuggestionDecorationIconSizeWidth(context);
        final @Px int elementSpace =
                getResources().getDimensionPixelSize(R.dimen.omnibox_action_chip_spacing);

        addItemDecoration(new SpacingRecyclerViewItemDecoration(leadInSpace, elementSpace));
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (event.getKeyCode() == KeyEvent.KEYCODE_TAB) {
            if (event.isShiftPressed()) {
                return mSelectionController.selectPreviousItem();
            } else {
                return mSelectionController.selectNextItem();
            }
        } else if (KeyNavigationUtil.isEnter(event)) {
            var chip = mSelectionController.getSelectedView();
            if (chip != null) return chip.performClick();
        }

        return superOnKeyDown(keyCode, event);
    }

    /**
     * Proxy calls to super.onKeyDown; call exposed for testing purposes. There is no way to detect
     * calls to super using robolectric.
     */
    @CheckDiscard("Should be inlined except for testing")
    @VisibleForTesting
    public boolean superOnKeyDown(int keyCode, KeyEvent event) {
        return super.onKeyDown(keyCode, event);
    }

    @Override
    public void setSelected(boolean isSelected) {
        mSelectionController.resetSelection();
    }

    @VisibleForTesting(otherwise = VisibleForTesting.NONE)
    void setSelectionControllerForTesting(RecyclerViewSelectionController controller) {
        mSelectionController = controller;
    }

    public void setLeadInSpacing(int spacing) {
        if (getItemDecorationCount() > 0) {
            assert getItemDecorationCount() == 1 : "Expected at most 1 decoration";
            removeItemDecorationAt(0);
        }

        addItemDecoration(
                new SpacingRecyclerViewItemDecoration(
                        spacing,
                        getResources().getDimensionPixelSize(R.dimen.omnibox_action_chip_spacing)));
    }
}