Commit 9d304d7e authored by Jordan Demeulenaere's avatar Jordan Demeulenaere Committed by Commit Bot

[Autofill Assistant] Show cancellable snackbar when stopping AA.

Screenshot: https://screenshot.googleplex.com/kgCWVfNQx5e.png
Issue: b/118246783

Bug: 806868
Change-Id: I20f38afdbcdeb66c14b747aaa7b92f03ebf460a6
Reviewed-on: https://chromium-review.googlesource.com/c/1299159Reviewed-by: default avatarTheresa <twellington@chromium.org>
Reviewed-by: default avatarMathias Carlen <mcarlen@chromium.org>
Commit-Queue: Jordan Demeulenaere <jdemeulenaere@chromium.org>
Cr-Commit-Position: refs/heads/master@{#603104}
parent 8b3c4223
......@@ -7,12 +7,14 @@ package org.chromium.chrome.browser.autofill_assistant;
import android.os.Bundle;
import android.support.annotation.Nullable;
import org.chromium.base.Callback;
import com.google.android.libraries.feed.common.functional.Consumer;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.chrome.browser.autofill.PersonalDataManager;
import org.chromium.chrome.browser.customtabs.CustomTabActivity;
import org.chromium.chrome.browser.payments.AutofillAssistantPaymentRequest;
import org.chromium.chrome.browser.snackbar.SnackbarManager;
import org.chromium.chrome.browser.tab.EmptyTabObserver;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tabmodel.EmptyTabModelObserver;
......@@ -47,7 +49,7 @@ public class AutofillAssistantUiController implements AutofillAssistantUiDelegat
private final WebContents mWebContents;
private final long mUiControllerAndroid;
private final AutofillAssistantUiDelegate mUiDelegate;
private final UiDelegateHolder mUiDelegateHolder;
private AutofillAssistantPaymentRequest mAutofillAssistantPaymentRequest;
......@@ -70,7 +72,7 @@ public class AutofillAssistantUiController implements AutofillAssistantUiDelegat
public AutofillAssistantUiController(CustomTabActivity activity) {
// Set mUiDelegate before nativeInit, as it can be accessed through native methods from
// nativeInit already.
mUiDelegate = new AutofillAssistantUiDelegate(activity, this);
mUiDelegateHolder = new UiDelegateHolder(new AutofillAssistantUiDelegate(activity, this));
Map<String, String> parameters = extractParameters(activity.getInitialIntent().getExtras());
parameters.remove(PARAMETER_ENABLED);
......@@ -109,8 +111,30 @@ public class AutofillAssistantUiController implements AutofillAssistantUiDelegat
@Override
public void onDismiss() {
mUiDelegateHolder.performUiOperation(uiDelegate -> {
uiDelegate.hide();
mUiDelegateHolder.pauseUiOperations();
// Show the UI back when unpaused.
mUiDelegateHolder.performUiOperation(AutofillAssistantUiDelegate::show);
// We show a snackbar with "undo" button for a few seconds, and shutdown only if it is
// not cancelled.
uiDelegate.showAutofillAssistantStoppedSnackbar(
new SnackbarManager.SnackbarController() {
@Override
public void onAction(Object actionData) {
// Shutdown was cancelled.
mUiDelegateHolder.unpauseUiOperations();
}
@Override
public void onDismissNoAction(Object actionData) {
nativeDestroy(mUiControllerAndroid);
}
});
});
}
@Override
public void onScriptSelected(String scriptPath) {
......@@ -150,22 +174,22 @@ public class AutofillAssistantUiController implements AutofillAssistantUiDelegat
@CalledByNative
private void onShowStatusMessage(String message) {
mUiDelegate.showStatusMessage(message);
mUiDelegateHolder.performUiOperation(uiDelegate -> uiDelegate.showStatusMessage(message));
}
@CalledByNative
private void onShowOverlay() {
mUiDelegate.showOverlay();
mUiDelegateHolder.performUiOperation(AutofillAssistantUiDelegate::showOverlay);
}
@CalledByNative
private void onHideOverlay() {
mUiDelegate.hideOverlay();
mUiDelegateHolder.performUiOperation(AutofillAssistantUiDelegate::hideOverlay);
}
@CalledByNative
private void onShutdown() {
mUiDelegate.shutdown();
nativeDestroy(mUiControllerAndroid);
}
@CalledByNative
......@@ -181,19 +205,24 @@ public class AutofillAssistantUiController implements AutofillAssistantUiDelegat
scriptHandles.add(new AutofillAssistantUiDelegate.ScriptHandle(
scriptNames[i], scriptPaths[i], scriptsHighlightFlags[i]));
}
mUiDelegate.updateScripts(scriptHandles);
mUiDelegateHolder.performUiOperation(uiDelegate -> uiDelegate.updateScripts(scriptHandles));
}
@CalledByNative
private void onChooseAddress() {
mUiDelegate.showProfiles(PersonalDataManager.getInstance().getProfilesToSuggest(
true /* includeNameInLabel */));
// TODO(crbug.com/806868): Remove this method once all scripts use payment request.
mUiDelegateHolder.performUiOperation(uiDelegate
-> uiDelegate.showProfiles(PersonalDataManager.getInstance().getProfilesToSuggest(
true /* includeNameInLabel */)));
}
@CalledByNative
private void onChooseCard() {
mUiDelegate.showCards(PersonalDataManager.getInstance().getCreditCardsToSuggest(
true /* includeServerCards */));
// TODO(crbug.com/806868): Remove this method once all scripts use payment request.
mUiDelegateHolder.performUiOperation(uiDelegate
-> uiDelegate.showCards(PersonalDataManager.getInstance().getCreditCardsToSuggest(
true /* includeServerCards */)));
}
@CalledByNative
......@@ -207,14 +236,11 @@ public class AutofillAssistantUiController implements AutofillAssistantUiDelegat
paymentOtions.shippingType = shippingType;
mAutofillAssistantPaymentRequest =
new AutofillAssistantPaymentRequest(mWebContents, paymentOtions);
mAutofillAssistantPaymentRequest.show(
new Callback<AutofillAssistantPaymentRequest.SelectedPaymentInformation>() {
@Override
public void onResult(AutofillAssistantPaymentRequest.SelectedPaymentInformation
selectedPaymentInformation) {
mUiDelegateHolder.performUiOperation(
uiDelegate -> mAutofillAssistantPaymentRequest.show(selectedPaymentInformation -> {
nativeOnGetPaymentInformation(mUiControllerAndroid,
selectedPaymentInformation.succeed,
selectedPaymentInformation.cardGuid,
selectedPaymentInformation.succeed, selectedPaymentInformation.cardGuid,
selectedPaymentInformation.cardIssuerNetwork,
selectedPaymentInformation.addressGuid,
selectedPaymentInformation.payerName,
......@@ -222,38 +248,92 @@ public class AutofillAssistantUiController implements AutofillAssistantUiDelegat
selectedPaymentInformation.payerEmail);
mAutofillAssistantPaymentRequest.close();
mAutofillAssistantPaymentRequest = null;
}
});
}));
}
@CalledByNative
private void onHideDetails() {
mUiDelegate.hideDetails();
mUiDelegateHolder.performUiOperation(AutofillAssistantUiDelegate::hideDetails);
}
@CalledByNative
private void onShowDetails(String title, String url, String description, int year, int month,
int day, int hour, int minute, int second) {
Date date = null;
Date date;
if (year > 0 && month > 0 && day > 0 && hour >= 0 && minute >= 0 && second >= 0) {
Calendar calendar = Calendar.getInstance();
// Month in Java Date is 0-based, but the one we receive from the server is 1-based.
calendar.set(year, month - 1, day, hour, minute, second);
date = calendar.getTime();
} else {
date = null;
}
mUiDelegate.showDetails(
new AutofillAssistantUiDelegate.Details(title, url, date, description));
mUiDelegateHolder.performUiOperation(uiDelegate
-> uiDelegate.showDetails(
new AutofillAssistantUiDelegate.Details(title, url, date, description)));
}
@CalledByNative
private void onShowProgressBar(int progress, String message) {
mUiDelegate.showProgressBar(progress, message);
mUiDelegateHolder.performUiOperation(
uiDelegate -> uiDelegate.showProgressBar(progress, message));
}
@CalledByNative
private void onHideProgressBar() {
mUiDelegate.hideProgressBar();
mUiDelegateHolder.performUiOperation(AutofillAssistantUiDelegate::hideProgressBar);
}
/**
* Class holder for the AutofillAssistantUiDelegate to make sure we don't make UI changes when
* we are in a pause state (i.e. few seconds before stopping completely).
*/
private static class UiDelegateHolder {
private final AutofillAssistantUiDelegate mUiDelegate;
private boolean mShouldQueueUiOperations = false;
private final ArrayList<Consumer<AutofillAssistantUiDelegate>> mPendingUiOperations =
new ArrayList<>();
private UiDelegateHolder(AutofillAssistantUiDelegate uiDelegate) {
mUiDelegate = uiDelegate;
}
/**
* Pause all UI operations such that they can potentially be ran later using {@link
* #unpauseUiOperations()}.
*/
public void pauseUiOperations() {
mShouldQueueUiOperations = true;
}
/**
* Unpause and trigger all UI operations received by {@link #performUiOperation(Consumer)}
* since the last {@link #pauseUiOperations()}.
*/
public void unpauseUiOperations() {
mShouldQueueUiOperations = false;
for (int i = 0; i < mPendingUiOperations.size(); i++) {
mPendingUiOperations.get(i).accept(mUiDelegate);
}
mPendingUiOperations.clear();
}
/**
* Perform a UI operation:
* - directly if we are not in a pause state.
* - later if the shutdown is cancelled.
* - never if Autofill Assistant is shut down.
*/
public void performUiOperation(Consumer<AutofillAssistantUiDelegate> operation) {
if (mShouldQueueUiOperations) {
mPendingUiOperations.add(operation);
return;
}
operation.accept(mUiDelegate);
}
}
// native methods.
......
......@@ -29,6 +29,8 @@ import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
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;
import org.chromium.chrome.browser.widget.MaterialProgressBar;
import java.io.InputStream;
......@@ -192,7 +194,8 @@ class AutofillAssistantUiDelegate {
mOverlay = mFullContainer.findViewById(R.id.overlay);
mOverlay.setOnClickListener(unusedView -> mClient.onClickOverlay());
mBottomBar = mFullContainer.findViewById(R.id.bottombar);
mBottomBar.findViewById(R.id.close_button).setOnClickListener(unusedView -> shutdown());
mBottomBar.findViewById(R.id.close_button)
.setOnClickListener(unusedView -> mClient.onDismiss());
mBottomBar.findViewById(R.id.feedback_button)
.setOnClickListener(unusedView
-> HelpAndFeedback.getInstance(mActivity).showFeedback(mActivity,
......@@ -227,7 +230,7 @@ class AutofillAssistantUiDelegate {
}
private void ensureFullContainerIsShown() {
if (!mFullContainer.isShown()) mFullContainer.setVisibility(View.VISIBLE);
if (!mFullContainer.isShown()) show();
}
/**
......@@ -288,6 +291,27 @@ class AutofillAssistantUiDelegate {
return chipView;
}
public void show() {
mFullContainer.setVisibility(View.VISIBLE);
}
public void hide() {
mFullContainer.setVisibility(View.GONE);
}
public void showAutofillAssistantStoppedSnackbar(
SnackbarManager.SnackbarController controller) {
int durationMs = SnackbarManager.DEFAULT_SNACKBAR_DURATION_MS;
Snackbar snackBar =
Snackbar.make(mActivity.getString(
R.string.autofill_assistant_stopped, durationMs / 1_000),
controller, Snackbar.TYPE_ACTION,
Snackbar.UMA_AUTOFILL_ASSISTANT_STOP_UNDO)
.setAction(mActivity.getString(R.string.undo), /* actionData= */ null);
snackBar.setDuration(durationMs);
mActivity.getSnackbarManager().showSnackbar(snackBar);
}
/** Called to show overlay. */
public void showOverlay() {
mOverlay.setVisibility(View.VISIBLE);
......@@ -367,14 +391,6 @@ class AutofillAssistantUiDelegate {
mProgressBar.setVisibility(View.INVISIBLE);
}
/**
* Shuts down the Autofill Assistant. The UI disappears and any associated state goes away.
*/
public void shutdown() {
mFullContainer.setVisibility(View.GONE);
mClient.onDismiss();
}
/**
* Show profiles in the bar.
*
......
......@@ -76,6 +76,7 @@ public class Snackbar {
public static final int UMA_FEED_NTP_STREAM = 26;
public static final int UMA_WEBAPK_PRIVACY_DISCLOSURE = 27;
public static final int UMA_TWA_PRIVACY_DISCLOSURE = 28;
public static final int UMA_AUTOFILL_ASSISTANT_STOP_UNDO = 29;
private SnackbarController mController;
private CharSequence mText;
......
......@@ -58,7 +58,7 @@ public class SnackbarManager implements OnClickListener, InfoBarContainer.InfoBa
default void onDismissNoAction(Object actionData) { }
}
private static final int DEFAULT_SNACKBAR_DURATION_MS = 3000;
public static final int DEFAULT_SNACKBAR_DURATION_MS = 3000;
private static final int ACCESSIBILITY_MODE_SNACKBAR_DURATION_MS = 6000;
// Used instead of the constant so tests can override the value.
......
......@@ -3921,6 +3921,10 @@ However, you aren’t invisible. Going private doesn’t hide your browsing from
Close other incognito tabs
</message>
<!-- Autofill Assistant -->
<message name="IDS_AUTOFILL_ASSISTANT_STOPPED" desc="Text label that is shown when stopping the Autofill Assistant. DURATION_SECONDS is a number representing a duration in seconds, which is why it is appended with 's'.">
Autofill Assistant will stop in <ph name="DURATION_SECONDS">%1$s<ex>3</ex></ph>s…
</message>
</messages>
</release>
</grit>
dccd747756f872e773b6b728c35bd264656efac2
\ No newline at end of file
......@@ -46264,6 +46264,7 @@ Called by update_net_trust_anchors.py.-->
<int value="26" label="FEED_NTP_STREAM"/>
<int value="27" label="UMA_WEBAPK_PRIVACY_DISCLOSURE"/>
<int value="28" label="UMA_TWA_PRIVACY_DISCLOSURE"/>
<int value="29" label="UMA_AUTOFILL_ASSISTANT_STOP_UNDO"/>
</enum>
<enum name="SnippetOpenMethod">
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment