blob: 7a8ca252debaa134d50b2b450634761819784c72 [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// 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_assistant.trigger_scripts;
import android.content.Context;
import androidx.annotation.NonNull;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.NativeMethods;
import org.chromium.chrome.browser.ActivityTabProvider;
import org.chromium.chrome.browser.autofill_assistant.AssistantCoordinator;
import org.chromium.chrome.browser.autofill_assistant.AutofillAssistantClient;
import org.chromium.chrome.browser.autofill_assistant.AutofillAssistantPreferencesUtil;
import org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiController;
import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantChip;
import org.chromium.chrome.browser.autofill_assistant.header.AssistantHeaderModel;
import org.chromium.chrome.browser.autofill_assistant.metrics.LiteScriptFinishedState;
import org.chromium.chrome.browser.feedback.HelpAndFeedbackLauncherImpl;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabUtils;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
import org.chromium.content_public.browser.WebContents;
import org.chromium.ui.KeyboardVisibilityDelegate;
import org.chromium.ui.base.ActivityKeyboardVisibilityDelegate;
import org.chromium.ui.base.ApplicationViewportInsetSupplier;
import java.util.List;
import java.util.Map;
/**
* Communicates with the native {@code TriggerScriptBridgeAndroid} to show and hide trigger
* scripts.
*/
@JNINamespace("autofill_assistant")
public class AssistantTriggerScriptBridge {
private AssistantTriggerScript mTriggerScript;
private long mNativeBridge;
private Delegate mDelegate;
private Context mContext;
private WebContents mWebContents;
private ActivityKeyboardVisibilityDelegate mKeyboardVisibilityDelegate;
private KeyboardVisibilityDelegate.KeyboardVisibilityListener mKeyboardVisibilityListener;
private ActivityTabProvider.ActivityTabTabObserver mActivityTabObserver;
/** Interface for delegates of the {@code start} method. */
public interface Delegate {
void onTriggerScriptFinished(@LiteScriptFinishedState int finishedState);
}
public AssistantTriggerScriptBridge() {}
/**
* Starts the trigger script for {@code initialUrl} and reports the finished state to {@code
* delegate}.
*/
public void start(BottomSheetController bottomSheetController, Context context,
ActivityKeyboardVisibilityDelegate keyboardVisibilityDelegate,
ApplicationViewportInsetSupplier bottomInsetProvider,
ActivityTabProvider activityTabProvider, @NonNull WebContents webContents,
@NonNull String initialUrl, Map<String, String> scriptParameters, String experimentIds,
Delegate delegate) {
mDelegate = delegate;
mContext = context;
mWebContents = webContents;
mKeyboardVisibilityDelegate = keyboardVisibilityDelegate;
mTriggerScript = new AssistantTriggerScript(context, new AssistantTriggerScript.Delegate() {
@Override
public void onTriggerScriptAction(int action) {
safeNativeOnTriggerScriptAction(action);
}
@Override
public void onBottomSheetClosedWithSwipe() {
safeNativeOnBottomSheetClosedWithSwipe();
}
@Override
public boolean onBackButtonPressed() {
return safeNativeOnBackButtonPressed();
}
@Override
public void onFeedbackButtonClicked() {
HelpAndFeedbackLauncherImpl.getInstance().showFeedback(
TabUtils.getActivity(TabUtils.fromWebContents(webContents)),
AutofillAssistantUiController.getProfile(),
webContents.getVisibleUrl().getSpec(),
AssistantCoordinator.FEEDBACK_CATEGORY_TAG);
}
}, webContents, bottomSheetController, bottomInsetProvider);
if (mKeyboardVisibilityListener != null) {
mKeyboardVisibilityDelegate.removeKeyboardVisibilityListener(
mKeyboardVisibilityListener);
}
mKeyboardVisibilityListener = this::safeNativeOnKeyboardVisibilityChanged;
mKeyboardVisibilityDelegate.addKeyboardVisibilityListener(mKeyboardVisibilityListener);
mActivityTabObserver =
new ActivityTabProvider.ActivityTabTabObserver(activityTabProvider, true) {
@Override
public void onInteractabilityChanged(Tab tab, boolean isInteractable) {
safeNativeOnTabInteractabilityChanged(isInteractable);
}
};
// Request the client to start the trigger script. Native will then bind itself to this java
// instance via setNativePtr.
AutofillAssistantClient.fromWebContents(webContents)
.startTriggerScript(this, initialUrl, scriptParameters, experimentIds);
}
/**
* Re-creates the header and returns the new header model. Must be called before every
* invocation of {@code showTriggerScript}. It is not possible to persist headers across
* multiple shown trigger scripts.
*/
@CalledByNative
private AssistantHeaderModel createHeaderAndGetModel() {
return mTriggerScript.createHeaderAndGetModel();
}
@CalledByNative
private Context getContext() {
return mContext;
}
/** Returns whether the user has seen a trigger script before or not. */
@CalledByNative
private static boolean isFirstTimeTriggerScriptUser() {
return AutofillAssistantPreferencesUtil.isAutofillAssistantFirstTimeLiteScriptUser();
}
/**
* Used by native to update and show the UI. The header should be created and updated using
* {@code createHeaderAndGetModel} prior to calling this function.
* @return true if the trigger script was displayed, else false.
*/
@CalledByNative
private boolean showTriggerScript(String[] cancelPopupMenuItems, int[] cancelPopupMenuActions,
List<AssistantChip> leftAlignedChips, int[] leftAlignedChipsActions,
List<AssistantChip> rightAlignedChips, int[] rightAlignedChipsActions,
boolean resizeVisualViewport, boolean scrollToHide) {
// Trigger scripts currently do not support switching activities (such as CCT->tab).
// TODO(b/171776026): Re-inject dependencies on activity change to support CCT->tab.
if (TabUtils.getActivity(TabUtils.fromWebContents(mWebContents)) != mContext) {
return false;
}
// NOTE: the cancel popup menu must be set before the chips are bound.
mTriggerScript.setCancelPopupMenu(cancelPopupMenuItems, cancelPopupMenuActions);
mTriggerScript.setLeftAlignedChips(leftAlignedChips, leftAlignedChipsActions);
mTriggerScript.setRightAlignedChips(rightAlignedChips, rightAlignedChipsActions);
boolean shown = mTriggerScript.show(resizeVisualViewport, scrollToHide);
// A trigger script was displayed, users are no longer considered first-time users.
if (shown) {
AutofillAssistantPreferencesUtil.setAutofillAssistantReturningLiteScriptUser();
}
return shown;
}
@CalledByNative
private void hideTriggerScript() {
mTriggerScript.hide();
}
@CalledByNative
private void onTriggerScriptFinished(@LiteScriptFinishedState int state) {
if (state == LiteScriptFinishedState.LITE_SCRIPT_PROMPT_FAILED_CANCEL_FOREVER) {
AutofillAssistantPreferencesUtil.setProactiveHelpSwitch(false);
}
mDelegate.onTriggerScriptFinished(state);
}
@CalledByNative
private static boolean isProactiveHelpEnabled() {
return AutofillAssistantPreferencesUtil.isProactiveHelpOn();
}
@CalledByNative
private void setNativePtr(long nativePtr) {
mNativeBridge = nativePtr;
}
@CalledByNative
private void clearNativePtr() {
mNativeBridge = 0;
mTriggerScript.destroy();
mKeyboardVisibilityDelegate.removeKeyboardVisibilityListener(mKeyboardVisibilityListener);
mActivityTabObserver.destroy();
}
private void safeNativeOnTriggerScriptAction(int action) {
if (mNativeBridge != 0) {
AssistantTriggerScriptBridgeJni.get().onTriggerScriptAction(
mNativeBridge, AssistantTriggerScriptBridge.this, action);
}
}
private void safeNativeOnBottomSheetClosedWithSwipe() {
if (mNativeBridge != 0) {
AssistantTriggerScriptBridgeJni.get().onBottomSheetClosedWithSwipe(
mNativeBridge, AssistantTriggerScriptBridge.this);
}
}
private boolean safeNativeOnBackButtonPressed() {
if (mNativeBridge != 0) {
return AssistantTriggerScriptBridgeJni.get().onBackButtonPressed(
mNativeBridge, AssistantTriggerScriptBridge.this);
}
return false;
}
private void safeNativeOnKeyboardVisibilityChanged(boolean visible) {
if (mNativeBridge != 0) {
AssistantTriggerScriptBridgeJni.get().onKeyboardVisibilityChanged(
mNativeBridge, AssistantTriggerScriptBridge.this, visible);
}
}
private void safeNativeOnTabInteractabilityChanged(boolean interactable) {
if (mNativeBridge != 0) {
AssistantTriggerScriptBridgeJni.get().onTabInteractabilityChanged(
mNativeBridge, AssistantTriggerScriptBridge.this, interactable);
}
}
@NativeMethods
interface Natives {
void onTriggerScriptAction(long nativeTriggerScriptBridgeAndroid,
AssistantTriggerScriptBridge caller, int action);
void onBottomSheetClosedWithSwipe(
long nativeTriggerScriptBridgeAndroid, AssistantTriggerScriptBridge caller);
boolean onBackButtonPressed(
long nativeTriggerScriptBridgeAndroid, AssistantTriggerScriptBridge caller);
void onKeyboardVisibilityChanged(long nativeTriggerScriptBridgeAndroid,
AssistantTriggerScriptBridge caller, boolean visible);
void onTabInteractabilityChanged(long nativeTriggerScriptBridgeAndroid,
AssistantTriggerScriptBridge caller, boolean interactable);
}
}