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; ...@@ -7,12 +7,14 @@ package org.chromium.chrome.browser.autofill_assistant;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.Nullable; 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.CalledByNative;
import org.chromium.base.annotations.JNINamespace; import org.chromium.base.annotations.JNINamespace;
import org.chromium.chrome.browser.autofill.PersonalDataManager; import org.chromium.chrome.browser.autofill.PersonalDataManager;
import org.chromium.chrome.browser.customtabs.CustomTabActivity; import org.chromium.chrome.browser.customtabs.CustomTabActivity;
import org.chromium.chrome.browser.payments.AutofillAssistantPaymentRequest; 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.EmptyTabObserver;
import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tabmodel.EmptyTabModelObserver; import org.chromium.chrome.browser.tabmodel.EmptyTabModelObserver;
...@@ -47,7 +49,7 @@ public class AutofillAssistantUiController implements AutofillAssistantUiDelegat ...@@ -47,7 +49,7 @@ public class AutofillAssistantUiController implements AutofillAssistantUiDelegat
private final WebContents mWebContents; private final WebContents mWebContents;
private final long mUiControllerAndroid; private final long mUiControllerAndroid;
private final AutofillAssistantUiDelegate mUiDelegate; private final UiDelegateHolder mUiDelegateHolder;
private AutofillAssistantPaymentRequest mAutofillAssistantPaymentRequest; private AutofillAssistantPaymentRequest mAutofillAssistantPaymentRequest;
...@@ -70,7 +72,7 @@ public class AutofillAssistantUiController implements AutofillAssistantUiDelegat ...@@ -70,7 +72,7 @@ public class AutofillAssistantUiController implements AutofillAssistantUiDelegat
public AutofillAssistantUiController(CustomTabActivity activity) { public AutofillAssistantUiController(CustomTabActivity activity) {
// Set mUiDelegate before nativeInit, as it can be accessed through native methods from // Set mUiDelegate before nativeInit, as it can be accessed through native methods from
// nativeInit already. // nativeInit already.
mUiDelegate = new AutofillAssistantUiDelegate(activity, this); mUiDelegateHolder = new UiDelegateHolder(new AutofillAssistantUiDelegate(activity, this));
Map<String, String> parameters = extractParameters(activity.getInitialIntent().getExtras()); Map<String, String> parameters = extractParameters(activity.getInitialIntent().getExtras());
parameters.remove(PARAMETER_ENABLED); parameters.remove(PARAMETER_ENABLED);
...@@ -109,8 +111,30 @@ public class AutofillAssistantUiController implements AutofillAssistantUiDelegat ...@@ -109,8 +111,30 @@ public class AutofillAssistantUiController implements AutofillAssistantUiDelegat
@Override @Override
public void onDismiss() { 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); nativeDestroy(mUiControllerAndroid);
} }
});
});
}
@Override @Override
public void onScriptSelected(String scriptPath) { public void onScriptSelected(String scriptPath) {
...@@ -150,22 +174,22 @@ public class AutofillAssistantUiController implements AutofillAssistantUiDelegat ...@@ -150,22 +174,22 @@ public class AutofillAssistantUiController implements AutofillAssistantUiDelegat
@CalledByNative @CalledByNative
private void onShowStatusMessage(String message) { private void onShowStatusMessage(String message) {
mUiDelegate.showStatusMessage(message); mUiDelegateHolder.performUiOperation(uiDelegate -> uiDelegate.showStatusMessage(message));
} }
@CalledByNative @CalledByNative
private void onShowOverlay() { private void onShowOverlay() {
mUiDelegate.showOverlay(); mUiDelegateHolder.performUiOperation(AutofillAssistantUiDelegate::showOverlay);
} }
@CalledByNative @CalledByNative
private void onHideOverlay() { private void onHideOverlay() {
mUiDelegate.hideOverlay(); mUiDelegateHolder.performUiOperation(AutofillAssistantUiDelegate::hideOverlay);
} }
@CalledByNative @CalledByNative
private void onShutdown() { private void onShutdown() {
mUiDelegate.shutdown(); nativeDestroy(mUiControllerAndroid);
} }
@CalledByNative @CalledByNative
...@@ -181,19 +205,24 @@ public class AutofillAssistantUiController implements AutofillAssistantUiDelegat ...@@ -181,19 +205,24 @@ public class AutofillAssistantUiController implements AutofillAssistantUiDelegat
scriptHandles.add(new AutofillAssistantUiDelegate.ScriptHandle( scriptHandles.add(new AutofillAssistantUiDelegate.ScriptHandle(
scriptNames[i], scriptPaths[i], scriptsHighlightFlags[i])); scriptNames[i], scriptPaths[i], scriptsHighlightFlags[i]));
} }
mUiDelegate.updateScripts(scriptHandles);
mUiDelegateHolder.performUiOperation(uiDelegate -> uiDelegate.updateScripts(scriptHandles));
} }
@CalledByNative @CalledByNative
private void onChooseAddress() { private void onChooseAddress() {
mUiDelegate.showProfiles(PersonalDataManager.getInstance().getProfilesToSuggest( // TODO(crbug.com/806868): Remove this method once all scripts use payment request.
true /* includeNameInLabel */)); mUiDelegateHolder.performUiOperation(uiDelegate
-> uiDelegate.showProfiles(PersonalDataManager.getInstance().getProfilesToSuggest(
true /* includeNameInLabel */)));
} }
@CalledByNative @CalledByNative
private void onChooseCard() { private void onChooseCard() {
mUiDelegate.showCards(PersonalDataManager.getInstance().getCreditCardsToSuggest( // TODO(crbug.com/806868): Remove this method once all scripts use payment request.
true /* includeServerCards */)); mUiDelegateHolder.performUiOperation(uiDelegate
-> uiDelegate.showCards(PersonalDataManager.getInstance().getCreditCardsToSuggest(
true /* includeServerCards */)));
} }
@CalledByNative @CalledByNative
...@@ -207,14 +236,11 @@ public class AutofillAssistantUiController implements AutofillAssistantUiDelegat ...@@ -207,14 +236,11 @@ public class AutofillAssistantUiController implements AutofillAssistantUiDelegat
paymentOtions.shippingType = shippingType; paymentOtions.shippingType = shippingType;
mAutofillAssistantPaymentRequest = mAutofillAssistantPaymentRequest =
new AutofillAssistantPaymentRequest(mWebContents, paymentOtions); new AutofillAssistantPaymentRequest(mWebContents, paymentOtions);
mAutofillAssistantPaymentRequest.show(
new Callback<AutofillAssistantPaymentRequest.SelectedPaymentInformation>() { mUiDelegateHolder.performUiOperation(
@Override uiDelegate -> mAutofillAssistantPaymentRequest.show(selectedPaymentInformation -> {
public void onResult(AutofillAssistantPaymentRequest.SelectedPaymentInformation
selectedPaymentInformation) {
nativeOnGetPaymentInformation(mUiControllerAndroid, nativeOnGetPaymentInformation(mUiControllerAndroid,
selectedPaymentInformation.succeed, selectedPaymentInformation.succeed, selectedPaymentInformation.cardGuid,
selectedPaymentInformation.cardGuid,
selectedPaymentInformation.cardIssuerNetwork, selectedPaymentInformation.cardIssuerNetwork,
selectedPaymentInformation.addressGuid, selectedPaymentInformation.addressGuid,
selectedPaymentInformation.payerName, selectedPaymentInformation.payerName,
...@@ -222,38 +248,92 @@ public class AutofillAssistantUiController implements AutofillAssistantUiDelegat ...@@ -222,38 +248,92 @@ public class AutofillAssistantUiController implements AutofillAssistantUiDelegat
selectedPaymentInformation.payerEmail); selectedPaymentInformation.payerEmail);
mAutofillAssistantPaymentRequest.close(); mAutofillAssistantPaymentRequest.close();
mAutofillAssistantPaymentRequest = null; mAutofillAssistantPaymentRequest = null;
} }));
});
} }
@CalledByNative @CalledByNative
private void onHideDetails() { private void onHideDetails() {
mUiDelegate.hideDetails(); mUiDelegateHolder.performUiOperation(AutofillAssistantUiDelegate::hideDetails);
} }
@CalledByNative @CalledByNative
private void onShowDetails(String title, String url, String description, int year, int month, private void onShowDetails(String title, String url, String description, int year, int month,
int day, int hour, int minute, int second) { 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) { if (year > 0 && month > 0 && day > 0 && hour >= 0 && minute >= 0 && second >= 0) {
Calendar calendar = Calendar.getInstance(); Calendar calendar = Calendar.getInstance();
// Month in Java Date is 0-based, but the one we receive from the server is 1-based. // 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); calendar.set(year, month - 1, day, hour, minute, second);
date = calendar.getTime(); date = calendar.getTime();
} else {
date = null;
} }
mUiDelegate.showDetails( mUiDelegateHolder.performUiOperation(uiDelegate
new AutofillAssistantUiDelegate.Details(title, url, date, description)); -> uiDelegate.showDetails(
new AutofillAssistantUiDelegate.Details(title, url, date, description)));
} }
@CalledByNative @CalledByNative
private void onShowProgressBar(int progress, String message) { private void onShowProgressBar(int progress, String message) {
mUiDelegate.showProgressBar(progress, message); mUiDelegateHolder.performUiOperation(
uiDelegate -> uiDelegate.showProgressBar(progress, message));
} }
@CalledByNative @CalledByNative
private void onHideProgressBar() { 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. // native methods.
......
...@@ -29,6 +29,8 @@ import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile; ...@@ -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.autofill.PersonalDataManager.CreditCard;
import org.chromium.chrome.browser.help.HelpAndFeedback; import org.chromium.chrome.browser.help.HelpAndFeedback;
import org.chromium.chrome.browser.profiles.Profile; 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 org.chromium.chrome.browser.widget.MaterialProgressBar;
import java.io.InputStream; import java.io.InputStream;
...@@ -192,7 +194,8 @@ class AutofillAssistantUiDelegate { ...@@ -192,7 +194,8 @@ class AutofillAssistantUiDelegate {
mOverlay = mFullContainer.findViewById(R.id.overlay); mOverlay = mFullContainer.findViewById(R.id.overlay);
mOverlay.setOnClickListener(unusedView -> mClient.onClickOverlay()); mOverlay.setOnClickListener(unusedView -> mClient.onClickOverlay());
mBottomBar = mFullContainer.findViewById(R.id.bottombar); 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) mBottomBar.findViewById(R.id.feedback_button)
.setOnClickListener(unusedView .setOnClickListener(unusedView
-> HelpAndFeedback.getInstance(mActivity).showFeedback(mActivity, -> HelpAndFeedback.getInstance(mActivity).showFeedback(mActivity,
...@@ -227,7 +230,7 @@ class AutofillAssistantUiDelegate { ...@@ -227,7 +230,7 @@ class AutofillAssistantUiDelegate {
} }
private void ensureFullContainerIsShown() { private void ensureFullContainerIsShown() {
if (!mFullContainer.isShown()) mFullContainer.setVisibility(View.VISIBLE); if (!mFullContainer.isShown()) show();
} }
/** /**
...@@ -288,6 +291,27 @@ class AutofillAssistantUiDelegate { ...@@ -288,6 +291,27 @@ class AutofillAssistantUiDelegate {
return chipView; 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. */ /** Called to show overlay. */
public void showOverlay() { public void showOverlay() {
mOverlay.setVisibility(View.VISIBLE); mOverlay.setVisibility(View.VISIBLE);
...@@ -367,14 +391,6 @@ class AutofillAssistantUiDelegate { ...@@ -367,14 +391,6 @@ class AutofillAssistantUiDelegate {
mProgressBar.setVisibility(View.INVISIBLE); 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. * Show profiles in the bar.
* *
......
...@@ -76,6 +76,7 @@ public class Snackbar { ...@@ -76,6 +76,7 @@ public class Snackbar {
public static final int UMA_FEED_NTP_STREAM = 26; public static final int UMA_FEED_NTP_STREAM = 26;
public static final int UMA_WEBAPK_PRIVACY_DISCLOSURE = 27; public static final int UMA_WEBAPK_PRIVACY_DISCLOSURE = 27;
public static final int UMA_TWA_PRIVACY_DISCLOSURE = 28; public static final int UMA_TWA_PRIVACY_DISCLOSURE = 28;
public static final int UMA_AUTOFILL_ASSISTANT_STOP_UNDO = 29;
private SnackbarController mController; private SnackbarController mController;
private CharSequence mText; private CharSequence mText;
......
...@@ -58,7 +58,7 @@ public class SnackbarManager implements OnClickListener, InfoBarContainer.InfoBa ...@@ -58,7 +58,7 @@ public class SnackbarManager implements OnClickListener, InfoBarContainer.InfoBa
default void onDismissNoAction(Object actionData) { } 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; private static final int ACCESSIBILITY_MODE_SNACKBAR_DURATION_MS = 6000;
// Used instead of the constant so tests can override the value. // 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 ...@@ -3921,6 +3921,10 @@ However, you aren’t invisible. Going private doesn’t hide your browsing from
Close other incognito tabs Close other incognito tabs
</message> </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> </messages>
</release> </release>
</grit> </grit>
dccd747756f872e773b6b728c35bd264656efac2
\ No newline at end of file
...@@ -46264,6 +46264,7 @@ Called by update_net_trust_anchors.py.--> ...@@ -46264,6 +46264,7 @@ Called by update_net_trust_anchors.py.-->
<int value="26" label="FEED_NTP_STREAM"/> <int value="26" label="FEED_NTP_STREAM"/>
<int value="27" label="UMA_WEBAPK_PRIVACY_DISCLOSURE"/> <int value="27" label="UMA_WEBAPK_PRIVACY_DISCLOSURE"/>
<int value="28" label="UMA_TWA_PRIVACY_DISCLOSURE"/> <int value="28" label="UMA_TWA_PRIVACY_DISCLOSURE"/>
<int value="29" label="UMA_AUTOFILL_ASSISTANT_STOP_UNDO"/>
</enum> </enum>
<enum name="SnippetOpenMethod"> <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