blob: ed014c8380f8b48b46efcb7d3b7a36cab7a656c2 [file] [log] [blame]
// Copyright 2018 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;
import android.view.View;
import android.view.ViewGroup;
import org.chromium.base.ThreadUtils;
import org.chromium.chrome.autofill_assistant.R;
import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.autofill_assistant.header.AssistantHeaderModel;
import org.chromium.chrome.browser.help.HelpAndFeedback;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.snackbar.Snackbar;
import org.chromium.chrome.browser.snackbar.SnackbarManager;
/**
* The main coordinator for the Autofill Assistant, responsible for instantiating all other
* sub-components and shutting down the Autofill Assistant.
*/
class AssistantCoordinator implements TouchEventFilterView.Delegate {
interface Delegate {
/**
* Completely stop the Autofill Assistant.
*/
void stop();
/**
* Asks for an update of the touchable area.
*/
void updateTouchableArea();
/**
* Called when interaction within allowed touchable area was detected. The interaction
* could be any gesture.
*/
void onUserInteractionInsideTouchableArea();
}
private static final String FEEDBACK_CATEGORY_TAG =
"com.android.chrome.USER_INITIATED_FEEDBACK_REPORT_AUTOFILL_ASSISTANT";
private static final int SNACKBAR_DELAY_MS = 5_000;
private static final int GRACEFUL_SHUTDOWN_DELAY_MS = 5_000;
private final ChromeActivity mActivity;
private final Delegate mDelegate;
private final AssistantModel mModel;
private final View mAssistantView;
private final AssistantBottomBarCoordinator mBottomBarCoordinator;
private final AssistantKeyboardCoordinator mKeyboardCoordinator;
private final AssistantOverlayCoordinator mOverlayCoordinator;
private boolean mIsShuttingDownGracefully;
AssistantCoordinator(ChromeActivity activity, Delegate delegate) {
mActivity = activity;
mDelegate = delegate;
mModel = new AssistantModel();
// Inflate autofill_assistant_sheet layout and add it to the main coordinator view.
ViewGroup coordinator = activity.findViewById(R.id.coordinator);
mAssistantView = activity.getLayoutInflater()
.inflate(R.layout.autofill_assistant_sheet, coordinator)
.findViewById(R.id.autofill_assistant);
// Instantiate child components.
mBottomBarCoordinator =
new AssistantBottomBarCoordinator(mActivity, mAssistantView, mModel);
mKeyboardCoordinator = new AssistantKeyboardCoordinator(activity);
mOverlayCoordinator = new AssistantOverlayCoordinator(activity, mAssistantView, this);
showAssistantView();
}
/**
* Shut down the Autofill Assistant immediately, without showing a message.
*/
public void shutdownImmediately() {
detachAssistantView();
mOverlayCoordinator.destroy();
mDelegate.stop();
}
/**
* Schedule the shut down of the Autofill Assistant such that any status message currently
* visible will still be shown for a few seconds before shutting down. Optionally replace
* the status message with a generic error message iff {@code showGiveUpMessage} is true.
*/
// TODO(crbug.com/806868): Move this method to native.
public void gracefulShutdown(boolean showGiveUpMessage) {
mIsShuttingDownGracefully = true;
// Make sure bottom bar is expanded.
mBottomBarCoordinator.expand();
// Hide everything except header.
mOverlayCoordinator.hide();
mModel.getDetailsModel().clearDetails();
mBottomBarCoordinator.getPaymentRequestCoordinator().setVisible(false);
mModel.getCarouselModel().clearChips();
if (showGiveUpMessage) {
mModel.getHeaderModel().set(AssistantHeaderModel.STATUS_MESSAGE,
mActivity.getString(R.string.autofill_assistant_give_up));
}
ThreadUtils.postOnUiThreadDelayed(this::shutdownImmediately, GRACEFUL_SHUTDOWN_DELAY_MS);
}
/**
* Shut down the Autofill Assistant and close the current Chrome tab.
*/
public void close() {
shutdownImmediately();
mActivity.finish();
}
/**
* Show the onboarding screen and call {@code onAccept} if the user agreed to proceed, shutdown
* otherwise.
*/
public void showOnboarding(Runnable onAccept) {
// Hide header buttons.
mModel.getHeaderModel().set(AssistantHeaderModel.FEEDBACK_VISIBLE, false);
mModel.getHeaderModel().set(AssistantHeaderModel.CLOSE_VISIBLE, false);
// Show overlay to prevent user from interacting with the page during onboarding.
mOverlayCoordinator.showFullOverlay();
AssistantOnboardingCoordinator.show(mActivity, mBottomBarCoordinator.getView())
.then(accepted -> {
if (!accepted) {
shutdownImmediately();
return;
}
// Show header buttons.
mModel.getHeaderModel().set(AssistantHeaderModel.FEEDBACK_VISIBLE, true);
mModel.getHeaderModel().set(AssistantHeaderModel.CLOSE_VISIBLE, true);
// Hide overlay.
mOverlayCoordinator.hide();
onAccept.run();
});
}
/**
* Get the model representing the current state of the UI.
*/
public AssistantModel getModel() {
return mModel;
}
// Getters to retrieve the sub coordinators.
public AssistantBottomBarCoordinator getBottomBarCoordinator() {
return mBottomBarCoordinator;
}
public AssistantKeyboardCoordinator getKeyboardCoordinator() {
return mKeyboardCoordinator;
}
public AssistantOverlayCoordinator getOverlayCoordinator() {
return mOverlayCoordinator;
}
/**
* Dismiss the assistant view and show a cancellable snackbar alerting the user that the
* Autofill assistant is shutting down.
*/
public void dismissAndShowSnackbar(String message) {
if (mIsShuttingDownGracefully) {
shutdownImmediately();
return;
}
hideAssistantView();
Snackbar snackBar =
Snackbar.make(message,
new SnackbarManager.SnackbarController() {
@Override
public void onAction(Object actionData) {
// Shutdown was cancelled.
showAssistantView();
}
@Override
public void onDismissNoAction(Object actionData) {
shutdownImmediately();
}
},
Snackbar.TYPE_ACTION, Snackbar.UMA_AUTOFILL_ASSISTANT_STOP_UNDO)
.setAction(mActivity.getString(R.string.undo), /* actionData= */ null);
snackBar.setSingleLine(false);
snackBar.setDuration(SNACKBAR_DELAY_MS);
mActivity.getSnackbarManager().showSnackbar(snackBar);
}
private void dismissAndShowSnackbar(int message) {
dismissAndShowSnackbar(mActivity.getString(message));
}
/**
* Show the Chrome feedback form.
*/
public void showFeedback(String debugContext) {
HelpAndFeedback.getInstance(mActivity).showFeedback(mActivity, Profile.getLastUsedProfile(),
mActivity.getActivityTab().getUrl(), FEEDBACK_CATEGORY_TAG,
FeedbackContext.buildContextString(mActivity, debugContext, 4));
}
// Implementation of methods from {@link TouchEventFilterView.Delegate}.
@Override
public void onUnexpectedTaps() {
dismissAndShowSnackbar(R.string.autofill_assistant_maybe_give_up);
}
@Override
public void updateTouchableArea() {
mDelegate.updateTouchableArea();
}
@Override
public void onUserInteractionInsideTouchableArea() {
mDelegate.onUserInteractionInsideTouchableArea();
}
// Private methods.
private void showAssistantView() {
mAssistantView.setVisibility(View.VISIBLE);
mKeyboardCoordinator.enableListenForKeyboardVisibility(true);
mBottomBarCoordinator.expand();
mBottomBarCoordinator.getView().announceForAccessibility(
mActivity.getString(R.string.autofill_assistant_available_accessibility));
}
private void hideAssistantView() {
mAssistantView.setVisibility(View.GONE);
mKeyboardCoordinator.enableListenForKeyboardVisibility(false);
}
private void detachAssistantView() {
mActivity.<ViewGroup>findViewById(R.id.coordinator).removeView(mAssistantView);
}
}