chromium/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_component/AccessorySheetCoordinator.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.chrome.browser.keyboard_accessory.sheet_component;

import static org.chromium.chrome.browser.keyboard_accessory.sheet_component.AccessorySheetProperties.VISIBLE;

import android.content.Context;

import androidx.annotation.Nullable;
import androidx.annotation.Px;
import androidx.annotation.VisibleForTesting;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;

import org.chromium.base.TraceEvent;
import org.chromium.chrome.browser.keyboard_accessory.AccessorySheetVisualStateProvider;
import org.chromium.chrome.browser.keyboard_accessory.R;
import org.chromium.chrome.browser.keyboard_accessory.data.KeyboardAccessoryData;
import org.chromium.ui.AsyncViewProvider;
import org.chromium.ui.AsyncViewStub;
import org.chromium.ui.ViewProvider;
import org.chromium.ui.modelutil.LazyConstructionPropertyMcp;
import org.chromium.ui.modelutil.ListModel;
import org.chromium.ui.modelutil.ListModelChangeProcessor;
import org.chromium.ui.modelutil.PropertyModel;

/**
 * Creates and owns all elements which are part of the accessory sheet component. It's part of the
 * controller but will mainly forward events (like showing the sheet) and handle communication with
 * the ManualFillingCoordinator (e.g. add a tab to trigger the sheet). to the {@link
 * AccessorySheetMediator}.
 */
public class AccessorySheetCoordinator implements AccessorySheetVisualStateProvider {
    private final AccessorySheetMediator mMediator;

    /**
     * Describes the events that are emitted when an accessory sheet is closed / changed. A class
     * implementing this interface takes the responsibility control the sheet, i.e.
     * ManualFillingCoordinator.
     */
    public interface SheetVisibilityDelegate {
        /**
         * Is triggered when a tab in the accessory was selected and the sheet needs to change.
         * @param sheetIndex The index of the selected sheet in the sheet openers / tab bar.
         */
        void onChangeAccessorySheet(int sheetIndex);

        /** Called when the sheet needs to be hidden. */
        void onCloseAccessorySheet();
    }

    /**
     * Creates the sheet component by instantiating Model, View and Controller before wiring these
     * parts up.
     *
     * @param sheetStub A {@link AsyncViewStub} for the accessory sheet layout.
     */
    public AccessorySheetCoordinator(
            AsyncViewStub sheetStub, SheetVisibilityDelegate sheetVisibilityDelegate) {
        this(
                sheetStub.getContext(),
                AsyncViewProvider.of(sheetStub, R.id.keyboard_accessory_sheet_container),
                sheetVisibilityDelegate);
    }

    /**
     * Constructor that allows to mock the {@link AsyncViewProvider}.
     *
     * @param context The {@link Context} for accessing color resources.
     * @param viewProvider A provider for the accessory.
     */
    @VisibleForTesting
    AccessorySheetCoordinator(
            Context context,
            ViewProvider<AccessorySheetView> viewProvider,
            SheetVisibilityDelegate sheetVisibilityDelegate) {
        PropertyModel model = AccessorySheetProperties.defaultPropertyModel().build();

        LazyConstructionPropertyMcp.create(
                model, VISIBLE, viewProvider, AccessorySheetViewBinder::bind);

        AccessorySheetMetricsRecorder.registerAccessorySheetModelMetricsObserver(model);
        mMediator = new AccessorySheetMediator(context, model, sheetVisibilityDelegate);
    }

    /**
     * Creates the {@link PagerAdapter} for the newly inflated {@link ViewPager}. The created
     * adapter observes the given model for item changes and updates the view pager.
     *
     * @param tabList The list of tabs to be displayed.
     * @param viewPager The newly inflated {@link ViewPager}.
     * @return A fully initialized {@link PagerAdapter}.
     */
    static PagerAdapter createTabViewAdapter(
            ListModel<KeyboardAccessoryData.Tab> tabList, ViewPager viewPager) {
        AccessoryPagerAdapter adapter = new AccessoryPagerAdapter(tabList);
        tabList.addObserver(new ListModelChangeProcessor<>(tabList, viewPager, adapter));
        return adapter;
    }

    public void setTabs(KeyboardAccessoryData.Tab[] tabs) {
        mMediator.setTabs(tabs);
    }

    public RecyclerView.OnScrollListener getScrollListener() {
        return mMediator.getScrollListener();
    }

    /**
     * Returns a {@link KeyboardAccessoryData.Tab} object that is used to display this bottom sheet.
     * @return Returns a {@link KeyboardAccessoryData.Tab}.
     */
    @Nullable
    public KeyboardAccessoryData.Tab getTab() {
        return mMediator.getTab();
    }

    /**
     * Sets the height of the accessory sheet (i.e. adapts to keyboard heights).
     * @param height The height of the sheet in pixels.
     */
    public void setHeight(@Px int height) {
        mMediator.setHeight(height);
    }

    /**
     * Gets the height of the accessory sheet (even if not visible).
     * @return The height of the sheet in pixels.
     */
    public @Px int getHeight() {
        return mMediator.getHeight();
    }

    /** Shows the Accessory Sheet. */
    public void show() {
        TraceEvent.begin("AccessorySheetCoordinator#show");
        mMediator.show();
        TraceEvent.end("AccessorySheetCoordinator#show");
    }

    /** Hides the Accessory Sheet. */
    public void hide() {
        mMediator.hide();
    }

    /**
     * Returns whether the accessory sheet is currently visible.
     * @return True, if the accessory sheet is visible.
     */
    public boolean isShown() {
        return mMediator.isShown();
    }

    /**
     * Calling this function changes the active tab to the tab at the given |position|.
     * @param position The index of the tab (starting with 0) that should be set active.
     */
    public void setActiveTab(int position) {
        mMediator.setActiveTab(position);
    }

    public void setOnPageChangeListener(ViewPager.OnPageChangeListener onPageChangeListener) {
        mMediator.setOnPageChangeListener(onPageChangeListener);
    }

    @Override
    public void addObserver(Observer observer) {
        mMediator.addObserver(observer);
    }

    @Override
    public void removeObserver(Observer observer) {
        mMediator.removeObserver(observer);
    }

    AccessorySheetMediator getMediatorForTesting() {
        return mMediator;
    }
}