chromium/chrome/android/features/keyboard_accessory/public/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingComponent.java

// Copyright 2019 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;

import android.content.Context;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.Px;

import org.chromium.base.supplier.ObservableSupplier;
import org.chromium.base.supplier.Supplier;
import org.chromium.chrome.browser.back_press.BackPressManager;
import org.chromium.chrome.browser.keyboard_accessory.data.KeyboardAccessoryData;
import org.chromium.chrome.browser.keyboard_accessory.data.PropertyProvider;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.ui.edge_to_edge.EdgeToEdgeController;
import org.chromium.components.autofill.AutofillDelegate;
import org.chromium.components.autofill.AutofillSuggestion;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
import org.chromium.components.browser_ui.widget.gesture.BackPressHandler;
import org.chromium.content_public.browser.WebContents;
import org.chromium.ui.AsyncViewStub;
import org.chromium.ui.DropdownPopupWindow;
import org.chromium.ui.base.WindowAndroid;

import java.util.List;
import java.util.function.BooleanSupplier;

/** This component handles the new, non-popup filling UI. */
public interface ManualFillingComponent extends BackPressHandler {
    /**
     * Observers are added with {@link #addObserver} and removed with {@link #removeObserver}. They
     * are notified when the {@link ManualFillingComponent} is destroyed.
     */
    interface Observer {
        /** Called if the ManualFillingComponent is destroyed. */
        void onDestroy();
    }

    /**
     * Since the ManualFillingComponent is considered part of the keyboard when using the regular
     * {@link org.chromium.ui.KeyboardVisibilityDelegate}, it needs direct access to the system
     * keyboard (but still reuse a form of {@link org.chromium.ui.KeyboardVisibilityDelegate}).
     * The "soft keyboard" describes the system's onscreen keyboard that covers part of the device
     * screen. It is hidden by default if the phone uses any physical keyboard.
     */
    interface SoftKeyboardDelegate {
        /**
         * Hide only Android's soft keyboard. Keeps eventual keyboard replacements and extensions
         * untouched.
         * @param view A focused {@link View}.
         * @return True if the keyboard was visible before this call.
         */
        boolean hideSoftKeyboardOnly(View view);

        /**
         * Returns whether Android soft keyboard is showing and ignores all extensions/replacements.
         * @param context A {@link Context} instance.
         * @param view    A {@link View}.
         * @return Returns true if Android's soft keyboard is visible. Ignores
         *         extensions/replacements.
         */
        boolean isSoftKeyboardShowing(Context context, View view);

        /**
         * Requests Android's soft keyboard.
         * @param contentView A {@link ViewGroup} used as target for the keyboard.
         */
        void showSoftKeyboard(ViewGroup contentView);

        /**
         * Returns the height of the bare soft keyboard (excluding extensions like accessories).
         * @param rootView A root {@link View} that allows size estimation based on display size.
         * @return The soft keyboard size in pixels.
         */
        @Px
        int calculateSoftKeyboardHeight(View rootView);
    }

    /** A delegate that can be used to request updates for accessory sheets. */
    interface UpdateAccessorySheetDelegate {
        /**
         * Requests a timely update to the accessory sheet of the given {@param sheetType}. If any
         * sheet can be constructed, the native side will push it, even if it was pushed before.
         * @param sheetType The {@link AccessoryTabType} of the sheet that should be updated.
         */
        void requestSheet(@AccessoryTabType int sheetType);
    }

    /**
     * Initializes the manual filling component. Calls to this class are NoOps until this method is
     * called.
     *
     * @param windowAndroid The window needed to listen to the keyboard and to connect to activity.
     * @param profile The {@link Profile} associated with the data.
     * @param sheetController A {@link BottomSheetController} to show the UI in.
     * @param isContextualSearchOpened Whether contextual search panel is opened.
     * @param keyboardDelegate A {@link SoftKeyboardDelegate} to control only the system keyboard.
     * @param backPressManager A {@link BackPressManager} to register {@link BackPressHandler}.
     * @param edgeToEdgeControllerSupplier A {@link Supplier<EdgeToEdgeController>}.
     * @param barStub The {@link AsyncViewStub} used to inflate the keyboard accessory bar.
     */
    void initialize(
            WindowAndroid windowAndroid,
            Profile profile,
            BottomSheetController sheetController,
            BooleanSupplier isContextualSearchOpened,
            SoftKeyboardDelegate keyboardDelegate,
            BackPressManager backPressManager,
            Supplier<EdgeToEdgeController> edgeToEdgeControllerSupplier,
            AsyncViewStub sheetStub,
            AsyncViewStub barStub);

    /** Cleans up the manual UI by destroying the accessory bar and its bottom sheet. */
    void destroy();

    /**
     * Handles tapping on the Android back button.
     * @return Whether tapping the back button dismissed the accessory sheet or not.
     */
    boolean onBackPressed();

    /** Ensures that keyboard accessory and keyboard are hidden and reset. */
    void dismiss();

    /**
     * Notifies the component that a popup window exists so it can be dismissed if necessary.
     * @param popup A {@link DropdownPopupWindow} that might be dismissed later.
     */
    void notifyPopupAvailable(DropdownPopupWindow popup);

    /**
     * By registering a provider, an empty tab of the given tab type is created. Call
     * {@link PropertyProvider#notifyObservers(Object)} to fill or update the sheet.
     * @param webContents The {@link WebContents} the provided data is meant for.
     * @param sheetType The type of sheet to instantiate and to provide data for.
     * @param sheetDataProvider The {@link PropertyProvider} the tab will get its data from.
     */
    void registerSheetDataProvider(
            WebContents webContents,
            @AccessoryTabType int sheetType,
            PropertyProvider<KeyboardAccessoryData.AccessorySheetData> sheetDataProvider);

    /**
     * Registers an updater delegate which requests new accessory sheets for a given `webContents`.
     * @param webContents The {@link WebContents} the given `delegate` maintains sheets for.
     * @param delegate A {@link UpdateAccessorySheetDelegate} to issue requests for recent sheets.
     */
    void registerSheetUpdateDelegate(
            WebContents webContents, UpdateAccessorySheetDelegate delegate);

    /**
     * Registers a provider, to provide actions for the keyboard accessory bar. Call
     * {@link PropertyProvider#notifyObservers(Object)} to fill or update the actions.
     * @param webContents The {@link WebContents} the provided data is meant for.
     * @param actionProvider The {@link PropertyProvider} providing actions.
     */
    void registerActionProvider(
            WebContents webContents,
            PropertyProvider<KeyboardAccessoryData.Action[]> actionProvider);

    /**
     * Registers a provider, to provide autofill suggestions for the keyboard accessory bar. Call
     * {@link PropertyProvider#notifyObservers(Object)} to fill or update the suggestions.
     *
     * @param autofillProvider The {@link PropertyProvider} providing autofill suggestions.
     * @param delegate The {@link AutofillDelegate} to call for interaction with the suggestions.
     */
    void registerAutofillProvider(
            PropertyProvider<List<AutofillSuggestion>> autofillProvider, AutofillDelegate delegate);

    /**
     * Signals that the accessory has permission to show.
     *
     * @param waitForKeyboard signals if the keyboard is requested.
     */
    void show(boolean waitForKeyboard);

    /**
     * Requests to close the active tab in the keyboard accessory. If there is no active tab, this
     * is a NoOp.
     */
    void closeAccessorySheet();

    /**
     * Opens the keyboard which implicitly dismisses the sheet. Without open sheet, this is a NoOp.
     */
    void swapSheetWithKeyboard();

    /** Hides the sheet until undone with {@link #show()}. */
    void hide();

    /**
     * Commands the accessory to show and set the currently active tab to the given |tabType|.
     * @param tabType the tab that should be selected by default.
     */
    void showAccessorySheetTab(@AccessoryTabType int tabType);

    /** Notifies the component that the activity it's living in was resumed. */
    void onResume();

    /** Notifies the component that the activity it's living in was paused. */
    void onPause();

    /**
     * Returns whether the Keyboard is replaced by an accessory sheet or is about to do so.
     * @return True if an accessory sheet is (being) opened and replacing the keyboard.
     * @param view A {@link View} that is used to find the window root.
     */
    boolean isFillingViewShown(View view);

    /**
     * The filling UI extends or
     * @return A {@link ObservableSupplier<Integer>} providing an inset to shrink the page by.
     */
    ObservableSupplier<Integer> getBottomInsetSupplier();

    /**
     * @param observer An {@link Observer} to add.
     * @return True iff the observer could be added.
     */
    boolean addObserver(Observer observer);

    /**
     * @param observer An {@link Observer} to add.
     * @return True iff the observer could be remove.
     */
    boolean removeObserver(Observer observer);

    /**
     * Show a confimation dialog.
     *
     * @param title A title of the confirmation dialog.
     * @param message The message of the confirmation dialog.
     * @param confirmedCallback A {@link Runnable} to trigger upon confirmation.
     * @param declinedCallback A {@link Runnable} to trigger upon rejection.
     */
    void confirmOperation(
            String title, String message, Runnable confirmedCallback, Runnable declinedCallback);

    /**
     * Returns the amount that the keyboard will be extended by the filling component when shown.
     * i.e. The height of any accessories to be shown on top of the keyboard.
     */
    int getKeyboardExtensionHeight();

    /**
     * Will force the accessory to show when the keyboard is shown. TODO(crbug.com/40879203):
     * Ideally this would live in a test utility like ManualFillingTestHelper.
     */
    void forceShowForTesting();

    /**
     * Returns a supplier for {@link AccessorySheetVisualStateProvider} that can be observed to be
     * notified of changes to the visual state of the accessory sheel.
     */
    ObservableSupplier<AccessorySheetVisualStateProvider> getAccessorySheetVisualStateProvider();
}