Commit 778ad11d authored by Michele Mancina's avatar Michele Mancina Committed by Commit Bot

[Autofill Assistant] Fix tab switching behavior.

This is done by saving the expanded/collapsed state when the tab is switched and restoring it when the tab is reselected.

Bug: b/159308598
Change-Id: Ib2f9c511c96557291251ef4b723bd88e4aa1721c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2362824
Commit-Queue: Michele Mancina <micantox@google.com>
Reviewed-by: default avatarMathias Carlen <mcarlen@chromium.org>
Cr-Commit-Position: refs/heads/master@{#803454}
parent ba80574e
...@@ -83,6 +83,8 @@ android_library("java") { ...@@ -83,6 +83,8 @@ android_library("java") {
"java/src/org/chromium/chrome/browser/autofill_assistant/AbstractListObserver.java", "java/src/org/chromium/chrome/browser/autofill_assistant/AbstractListObserver.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/AssistantAccessibilityUtils.java", "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantAccessibilityUtils.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java", "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarDelegate.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarNativeDelegate.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomSheetContent.java", "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomSheetContent.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java", "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/AssistantDialogButton.java", "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantDialogButton.java",
...@@ -185,6 +187,7 @@ android_library("java") { ...@@ -185,6 +187,7 @@ android_library("java") {
generate_jni("jni_headers") { generate_jni("jni_headers") {
sources = [ sources = [
"java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarNativeDelegate.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/AssistantDialogButton.java", "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantDialogButton.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/AssistantInfoPopup.java", "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantInfoPopup.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/AssistantModel.java", "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantModel.java",
......
...@@ -36,6 +36,8 @@ import org.chromium.chrome.browser.ui.TabObscuringHandler; ...@@ -36,6 +36,8 @@ import org.chromium.chrome.browser.ui.TabObscuringHandler;
import org.chromium.chrome.browser.util.ChromeAccessibilityUtil; import org.chromium.chrome.browser.util.ChromeAccessibilityUtil;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent; import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetController; import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.SheetState;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetObserver;
import org.chromium.components.browser_ui.bottomsheet.EmptyBottomSheetObserver; import org.chromium.components.browser_ui.bottomsheet.EmptyBottomSheetObserver;
import org.chromium.content_public.browser.UiThreadTaskTraits; import org.chromium.content_public.browser.UiThreadTaskTraits;
import org.chromium.content_public.browser.WebContents; import org.chromium.content_public.browser.WebContents;
...@@ -60,6 +62,7 @@ class AssistantBottomBarCoordinator implements AssistantPeekHeightCoordinator.De ...@@ -60,6 +62,7 @@ class AssistantBottomBarCoordinator implements AssistantPeekHeightCoordinator.De
private WebContents mWebContents; private WebContents mWebContents;
private ApplicationViewportInsetSupplier mWindowApplicationInsetSupplier; private ApplicationViewportInsetSupplier mWindowApplicationInsetSupplier;
private final ChromeAccessibilityUtil.Observer mAccessibilityObserver; private final ChromeAccessibilityUtil.Observer mAccessibilityObserver;
private final BottomSheetObserver mBottomSheetObserver;
// Child coordinators. // Child coordinators.
private final AssistantHeaderCoordinator mHeaderCoordinator; private final AssistantHeaderCoordinator mHeaderCoordinator;
...@@ -98,8 +101,7 @@ class AssistantBottomBarCoordinator implements AssistantPeekHeightCoordinator.De ...@@ -98,8 +101,7 @@ class AssistantBottomBarCoordinator implements AssistantPeekHeightCoordinator.De
AssistantBottomBarCoordinator(Activity activity, AssistantModel model, AssistantBottomBarCoordinator(Activity activity, AssistantModel model,
BottomSheetController controller, BottomSheetController controller,
ApplicationViewportInsetSupplier applicationViewportInsetSupplier, ApplicationViewportInsetSupplier applicationViewportInsetSupplier,
TabObscuringHandler tabObscuringHandler, TabObscuringHandler tabObscuringHandler) {
AssistantBottomSheetContent.Delegate bottomSheetDelegate) {
mModel = model; mModel = model;
mBottomSheetController = controller; mBottomSheetController = controller;
mTabObscuringHandler = tabObscuringHandler; mTabObscuringHandler = tabObscuringHandler;
...@@ -114,7 +116,7 @@ class AssistantBottomBarCoordinator implements AssistantPeekHeightCoordinator.De ...@@ -114,7 +116,7 @@ class AssistantBottomBarCoordinator implements AssistantPeekHeightCoordinator.De
if (currentSheetContent instanceof AssistantBottomSheetContent) { if (currentSheetContent instanceof AssistantBottomSheetContent) {
mContent = (AssistantBottomSheetContent) currentSheetContent; mContent = (AssistantBottomSheetContent) currentSheetContent;
} else { } else {
mContent = new AssistantBottomSheetContent(activity, bottomSheetDelegate); mContent = new AssistantBottomSheetContent(activity, model::getBottomBarDelegate);
} }
// Replace or set the content to the actual Autofill Assistant views. // Replace or set the content to the actual Autofill Assistant views.
...@@ -189,9 +191,14 @@ class AssistantBottomBarCoordinator implements AssistantPeekHeightCoordinator.De ...@@ -189,9 +191,14 @@ class AssistantBottomBarCoordinator implements AssistantPeekHeightCoordinator.De
setHorizontalMargins(mInfoBoxCoordinator.getView()); setHorizontalMargins(mInfoBoxCoordinator.getView());
setHorizontalMargins(mDetailsCoordinator.getView()); setHorizontalMargins(mDetailsCoordinator.getView());
controller.addObserver(new EmptyBottomSheetObserver() { mBottomSheetObserver = new EmptyBottomSheetObserver() {
@Override @Override
public void onSheetStateChanged(int newState) { public void onSheetStateChanged(int newState) {
if (newState == SheetState.PEEK || newState == SheetState.HALF
|| newState == SheetState.FULL) {
mModel.setBottomSheetState(newState);
}
// Note: recycler view updates while the bottom sheet is SCROLLING result in a // Note: recycler view updates while the bottom sheet is SCROLLING result in a
// BottomSheet assertion. // BottomSheet assertion.
if (newState != BottomSheetController.SheetState.SCROLLING) { if (newState != BottomSheetController.SheetState.SCROLLING) {
...@@ -210,13 +217,14 @@ class AssistantBottomBarCoordinator implements AssistantPeekHeightCoordinator.De ...@@ -210,13 +217,14 @@ class AssistantBottomBarCoordinator implements AssistantPeekHeightCoordinator.De
public void onSheetOffsetChanged(float heightFraction, float offsetPx) { public void onSheetOffsetChanged(float heightFraction, float offsetPx) {
updateVisualViewportHeight(); updateVisualViewportHeight();
} }
}); };
controller.addObserver(mBottomSheetObserver);
// Show or hide the bottom sheet content when the Autofill Assistant visibility is changed. // Show or hide the bottom sheet content when the Autofill Assistant visibility is changed.
model.addObserver((source, propertyKey) -> { model.addObserver((source, propertyKey) -> {
if (AssistantModel.VISIBLE == propertyKey) { if (AssistantModel.VISIBLE == propertyKey) {
if (model.get(AssistantModel.VISIBLE)) { if (model.get(AssistantModel.VISIBLE)) {
showContentAndExpand(); showContent(/* shouldExpand = */ false, /* animate = */ false);
} else { } else {
hide(); hide();
} }
...@@ -320,6 +328,7 @@ class AssistantBottomBarCoordinator implements AssistantPeekHeightCoordinator.De ...@@ -320,6 +328,7 @@ class AssistantBottomBarCoordinator implements AssistantPeekHeightCoordinator.De
resetVisualViewportHeight(); resetVisualViewportHeight();
mWindowApplicationInsetSupplier.removeSupplier(mInsetSupplier); mWindowApplicationInsetSupplier.removeSupplier(mInsetSupplier);
ChromeAccessibilityUtil.get().removeObserver(mAccessibilityObserver); ChromeAccessibilityUtil.get().removeObserver(mAccessibilityObserver);
mBottomSheetController.removeObserver(mBottomSheetObserver);
if (mObscuringToken != TokenHolder.INVALID_TOKEN) { if (mObscuringToken != TokenHolder.INVALID_TOKEN) {
mTabObscuringHandler.unobscureAllTabs(mObscuringToken); mTabObscuringHandler.unobscureAllTabs(mObscuringToken);
...@@ -334,9 +343,9 @@ class AssistantBottomBarCoordinator implements AssistantPeekHeightCoordinator.De ...@@ -334,9 +343,9 @@ class AssistantBottomBarCoordinator implements AssistantPeekHeightCoordinator.De
} }
/** Request showing the Assistant bottom bar view and expand the sheet. */ /** Request showing the Assistant bottom bar view and expand the sheet. */
public void showContentAndExpand() { public void showContent(boolean shouldExpand, boolean animate) {
BottomSheetUtils.showContentAndExpand( BottomSheetUtils.showContentAndMaybeExpand(
mBottomSheetController, mContent, /* animate= */ true); mBottomSheetController, mContent, shouldExpand, animate);
} }
/** Hide the Assistant bottom bar view. */ /** Hide the Assistant bottom bar view. */
...@@ -373,6 +382,10 @@ class AssistantBottomBarCoordinator implements AssistantPeekHeightCoordinator.De ...@@ -373,6 +382,10 @@ class AssistantBottomBarCoordinator implements AssistantPeekHeightCoordinator.De
mBottomSheetController.collapseSheet(/* animate = */ true); mBottomSheetController.collapseSheet(/* animate = */ true);
} }
void restoreState(@SheetState int state) {
BottomSheetUtils.restoreState(mBottomSheetController, mContent, state);
}
@Override @Override
public void setShowOnlyCarousels(boolean showOnlyCarousels) { public void setShowOnlyCarousels(boolean showOnlyCarousels) {
mScrollableContent.setVisibility(showOnlyCarousels ? View.GONE : View.VISIBLE); mScrollableContent.setVisibility(showOnlyCarousels ? View.GONE : View.VISIBLE);
......
// 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;
/** Common interface for the bottom bar delegate. */
public interface AssistantBottomBarDelegate {
/** The back button has been pressed. */
boolean onBackButtonPressed();
}
// 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;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.NativeMethods;
/** Delegate for the bottom bar which forwards events to a native counterpart. */
@JNINamespace("autofill_assistant")
public class AssistantBottomBarNativeDelegate implements AssistantBottomBarDelegate {
private long mNativeAssistantBottomBarDelegate;
@CalledByNative
private static AssistantBottomBarNativeDelegate create(long nativeAssistantBottomBarDelegate) {
return new AssistantBottomBarNativeDelegate(nativeAssistantBottomBarDelegate);
}
private AssistantBottomBarNativeDelegate(long nativeAssistantBottomBarDelegate) {
mNativeAssistantBottomBarDelegate = nativeAssistantBottomBarDelegate;
}
@Override
public boolean onBackButtonPressed() {
if (mNativeAssistantBottomBarDelegate != 0) {
return AssistantBottomBarNativeDelegateJni.get().onBackButtonClicked(
mNativeAssistantBottomBarDelegate, AssistantBottomBarNativeDelegate.this);
}
return false;
}
@CalledByNative
private void clearNativePtr() {
mNativeAssistantBottomBarDelegate = 0;
}
@NativeMethods
interface Natives {
boolean onBackButtonClicked(
long nativeAssistantBottomBarDelegate, AssistantBottomBarNativeDelegate caller);
}
}
...@@ -12,6 +12,7 @@ import android.widget.ScrollView; ...@@ -12,6 +12,7 @@ import android.widget.ScrollView;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import org.chromium.base.supplier.Supplier;
import org.chromium.chrome.autofill_assistant.R; import org.chromium.chrome.autofill_assistant.R;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent; import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
...@@ -21,24 +22,20 @@ import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent; ...@@ -21,24 +22,20 @@ import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
* practice, this allows to replace the onboarding by the actual Autofill Assistant content). * practice, this allows to replace the onboarding by the actual Autofill Assistant content).
*/ */
class AssistantBottomSheetContent implements BottomSheetContent { class AssistantBottomSheetContent implements BottomSheetContent {
interface Delegate {
boolean onBackButtonPressed();
}
private final View mToolbarView; private final View mToolbarView;
private final SizeListenableLinearLayout mContentView; private final SizeListenableLinearLayout mContentView;
@Nullable @Nullable
private ScrollView mContentScrollableView; private ScrollView mContentScrollableView;
@Nullable private Supplier<AssistantBottomBarDelegate> mBottomBarDelegateSupplier;
private Delegate mDelegate;
public AssistantBottomSheetContent(Context context, @Nullable Delegate delegate) { public AssistantBottomSheetContent(
Context context, Supplier<AssistantBottomBarDelegate> supplier) {
mToolbarView = LayoutInflater.from(context).inflate( mToolbarView = LayoutInflater.from(context).inflate(
R.layout.autofill_assistant_bottom_sheet_toolbar, /* root= */ null); R.layout.autofill_assistant_bottom_sheet_toolbar, /* root= */ null);
mContentView = new SizeListenableLinearLayout(context); mContentView = new SizeListenableLinearLayout(context);
mContentView.setLayoutParams(new ViewGroup.LayoutParams( mContentView.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
mDelegate = delegate; mBottomBarDelegateSupplier = supplier;
} }
public void setContent(View content, ScrollView scrollableView) { public void setContent(View content, ScrollView scrollableView) {
...@@ -133,10 +130,11 @@ class AssistantBottomSheetContent implements BottomSheetContent { ...@@ -133,10 +130,11 @@ class AssistantBottomSheetContent implements BottomSheetContent {
@Override @Override
public boolean handleBackPress() { public boolean handleBackPress() {
if (mDelegate == null) { AssistantBottomBarDelegate bottomBarDelegate = mBottomBarDelegateSupplier.get();
if (bottomBarDelegate == null) {
return false; return false;
} }
return mDelegate.onBackButtonPressed(); return bottomBarDelegate.onBackButtonPressed();
} }
} }
...@@ -12,6 +12,7 @@ import org.chromium.chrome.browser.help.HelpAndFeedback; ...@@ -12,6 +12,7 @@ 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.ui.TabObscuringHandler; import org.chromium.chrome.browser.ui.TabObscuringHandler;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetController; import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.SheetState;
/** /**
* The main coordinator for the Autofill Assistant, responsible for instantiating all other * The main coordinator for the Autofill Assistant, responsible for instantiating all other
...@@ -31,8 +32,7 @@ class AssistantCoordinator { ...@@ -31,8 +32,7 @@ class AssistantCoordinator {
AssistantCoordinator(ChromeActivity activity, BottomSheetController controller, AssistantCoordinator(ChromeActivity activity, BottomSheetController controller,
TabObscuringHandler tabObscuringHandler, TabObscuringHandler tabObscuringHandler,
@Nullable AssistantOverlayCoordinator overlayCoordinator, @Nullable AssistantOverlayCoordinator overlayCoordinator,
AssistantKeyboardCoordinator.Delegate keyboardCoordinatorDelegate, AssistantKeyboardCoordinator.Delegate keyboardCoordinatorDelegate) {
AssistantBottomSheetContent.Delegate bottomSheetDelegate) {
mActivity = activity; mActivity = activity;
if (overlayCoordinator != null) { if (overlayCoordinator != null) {
...@@ -47,13 +47,11 @@ class AssistantCoordinator { ...@@ -47,13 +47,11 @@ class AssistantCoordinator {
mBottomBarCoordinator = new AssistantBottomBarCoordinator(activity, mModel, controller, mBottomBarCoordinator = new AssistantBottomBarCoordinator(activity, mModel, controller,
activity.getWindowAndroid().getApplicationBottomInsetProvider(), activity.getWindowAndroid().getApplicationBottomInsetProvider(),
tabObscuringHandler, bottomSheetDelegate); tabObscuringHandler);
mKeyboardCoordinator = new AssistantKeyboardCoordinator(activity, mKeyboardCoordinator = new AssistantKeyboardCoordinator(activity,
activity.getWindowAndroid().getKeyboardDelegate(), activity.getWindowAndroid().getKeyboardDelegate(),
activity.getCompositorViewHolder(), mModel, keyboardCoordinatorDelegate, activity.getCompositorViewHolder(), mModel, keyboardCoordinatorDelegate,
controller); controller);
mModel.setVisible(true);
} }
/** Detaches and destroys the view. */ /** Detaches and destroys the view. */
...@@ -94,4 +92,10 @@ class AssistantCoordinator { ...@@ -94,4 +92,10 @@ class AssistantCoordinator {
null /* feed context */, null /* feed context */,
FeedbackContext.buildContextString(mActivity, debugContext, 4)); FeedbackContext.buildContextString(mActivity, debugContext, 4));
} }
public void show() {
// Simulates native's initialization.
mModel.setVisible(true);
mBottomBarCoordinator.restoreState(SheetState.HALF);
}
} }
...@@ -25,6 +25,10 @@ class AssistantModel extends PropertyModel { ...@@ -25,6 +25,10 @@ class AssistantModel extends PropertyModel {
static final WritableBooleanPropertyKey ALLOW_SOFT_KEYBOARD = new WritableBooleanPropertyKey(); static final WritableBooleanPropertyKey ALLOW_SOFT_KEYBOARD = new WritableBooleanPropertyKey();
static final WritableBooleanPropertyKey ALLOW_TALKBACK_ON_WEBSITE = static final WritableBooleanPropertyKey ALLOW_TALKBACK_ON_WEBSITE =
new WritableBooleanPropertyKey(); new WritableBooleanPropertyKey();
static final WritableObjectPropertyKey<AssistantBottomBarDelegate> BOTTOM_BAR_DELEGATE =
new WritableObjectPropertyKey<>();
static final WritableIntPropertyKey BOTTOM_SHEET_STATE = new WritableIntPropertyKey();
static final WritableFloatPropertyKey TALKBACK_SHEET_SIZE_FRACTION = static final WritableFloatPropertyKey TALKBACK_SHEET_SIZE_FRACTION =
new WritableFloatPropertyKey(); new WritableFloatPropertyKey();
static final WritableBooleanPropertyKey VISIBLE = new WritableBooleanPropertyKey(); static final WritableBooleanPropertyKey VISIBLE = new WritableBooleanPropertyKey();
...@@ -48,8 +52,8 @@ class AssistantModel extends PropertyModel { ...@@ -48,8 +52,8 @@ class AssistantModel extends PropertyModel {
} }
AssistantModel(AssistantOverlayModel overlayModel) { AssistantModel(AssistantOverlayModel overlayModel) {
super(ALLOW_SOFT_KEYBOARD, VISIBLE, WEB_CONTENTS, ALLOW_TALKBACK_ON_WEBSITE, super(ALLOW_SOFT_KEYBOARD, ALLOW_TALKBACK_ON_WEBSITE, BOTTOM_BAR_DELEGATE,
TALKBACK_SHEET_SIZE_FRACTION); BOTTOM_SHEET_STATE, TALKBACK_SHEET_SIZE_FRACTION, VISIBLE, WEB_CONTENTS);
mOverlayModel = overlayModel; mOverlayModel = overlayModel;
} }
...@@ -92,6 +96,18 @@ class AssistantModel extends PropertyModel { ...@@ -92,6 +96,18 @@ class AssistantModel extends PropertyModel {
return mGenericUiModel; return mGenericUiModel;
} }
public AssistantBottomBarDelegate getBottomBarDelegate() {
return get(BOTTOM_BAR_DELEGATE);
}
public int getBottomSheetState() {
return get(BOTTOM_SHEET_STATE);
}
public void setBottomSheetState(int state) {
set(BOTTOM_SHEET_STATE, state);
}
@CalledByNative @CalledByNative
private void setAllowSoftKeyboard(boolean allowed) { private void setAllowSoftKeyboard(boolean allowed) {
set(ALLOW_SOFT_KEYBOARD, allowed); set(ALLOW_SOFT_KEYBOARD, allowed);
...@@ -102,6 +118,11 @@ class AssistantModel extends PropertyModel { ...@@ -102,6 +118,11 @@ class AssistantModel extends PropertyModel {
set(ALLOW_TALKBACK_ON_WEBSITE, allowed); set(ALLOW_TALKBACK_ON_WEBSITE, allowed);
} }
@CalledByNative
private void setBottomBarDelegate(AssistantBottomBarDelegate delegate) {
set(BOTTOM_BAR_DELEGATE, delegate);
}
@CalledByNative @CalledByNative
private void setTalkbackSheetSizeFraction(float fraction) { private void setTalkbackSheetSizeFraction(float fraction) {
set(TALKBACK_SHEET_SIZE_FRACTION, fraction); set(TALKBACK_SHEET_SIZE_FRACTION, fraction);
......
...@@ -98,14 +98,19 @@ class AssistantOnboardingCoordinator { ...@@ -98,14 +98,19 @@ class AssistantOnboardingCoordinator {
mContext, mBrowserControls, mCompositorViewHolder, mScrimCoordinator, overlayModel); mContext, mBrowserControls, mCompositorViewHolder, mScrimCoordinator, overlayModel);
overlayModel.set(AssistantOverlayModel.STATE, AssistantOverlayState.FULL); overlayModel.set(AssistantOverlayModel.STATE, AssistantOverlayState.FULL);
mContent = new AssistantBottomSheetContent(mContext, () -> { mContent =
onUserAction( new AssistantBottomSheetContent(mContext, () -> new AssistantBottomBarDelegate() {
/* accept= */ false, callback, OnBoarding.OB_NO_ANSWER, @Override
DropOutReason.ONBOARDING_BACK_BUTTON_CLICKED); public boolean onBackButtonPressed() {
return true; onUserAction(
}); /* accept= */ false, callback, OnBoarding.OB_NO_ANSWER,
DropOutReason.ONBOARDING_BACK_BUTTON_CLICKED);
return true;
}
});
initContent(callback); initContent(callback);
BottomSheetUtils.showContentAndExpand(mController, mContent, mAnimate); BottomSheetUtils.showContentAndMaybeExpand(
mController, mContent, /* shouldExpand = */ true, mAnimate);
} }
/** /**
......
...@@ -10,9 +10,11 @@ import android.view.View; ...@@ -10,9 +10,11 @@ import android.view.View;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
import org.chromium.base.Callback; import org.chromium.base.Callback;
import org.chromium.base.task.PostTask;
import org.chromium.chrome.autofill_assistant.R; import org.chromium.chrome.autofill_assistant.R;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetController; import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
import org.chromium.components.browser_ui.bottomsheet.EmptyBottomSheetObserver; import org.chromium.components.browser_ui.bottomsheet.EmptyBottomSheetObserver;
import org.chromium.content_public.browser.UiThreadTaskTraits;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
...@@ -104,12 +106,21 @@ class AssistantPeekHeightCoordinator { ...@@ -104,12 +106,21 @@ class AssistantPeekHeightCoordinator {
private void onHeaderHeightChanged(int height) { private void onHeaderHeightChanged(int height) {
mHeaderHeight = height; mHeaderHeight = height;
updateToolbarPadding(); maybeUpdateToolBarPadding();
} }
private void onActionsHeightChanged(int height) { private void onActionsHeightChanged(int height) {
mActionsHeight = height; mActionsHeight = height;
updateToolbarPadding(); maybeUpdateToolBarPadding();
}
private void maybeUpdateToolBarPadding() {
if (mPeekMode != PeekMode.UNDEFINED) {
// TODO(b/164389932): Investigate proper fix for HANDLE_HEADER peek state not working as
// expected when switching from CCT to browser. Without the postTask, it shows in the
// HEADER mode with no pull handle displayed.
PostTask.postTask(UiThreadTaskTraits.DEFAULT, this::updateToolbarPadding);
}
} }
private void maybeShowOnlyCarousels() { private void maybeShowOnlyCarousels() {
......
...@@ -25,10 +25,14 @@ import org.chromium.chrome.browser.tab.Tab; ...@@ -25,10 +25,14 @@ import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.ui.TabObscuringHandler; import org.chromium.chrome.browser.ui.TabObscuringHandler;
import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager.SnackbarController; import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager.SnackbarController;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetController; import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.SheetState;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetControllerProvider; import org.chromium.components.browser_ui.bottomsheet.BottomSheetControllerProvider;
import org.chromium.content_public.browser.UiThreadTaskTraits; import org.chromium.content_public.browser.UiThreadTaskTraits;
import org.chromium.content_public.browser.WebContents; import org.chromium.content_public.browser.WebContents;
import org.chromium.ui.base.WindowAndroid; import org.chromium.ui.base.WindowAndroid;
import org.chromium.ui.modelutil.PropertyKey;
import org.chromium.ui.modelutil.PropertyObservable;
import org.chromium.ui.modelutil.PropertyObservable.PropertyObserver;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
...@@ -128,12 +132,25 @@ public class AutofillAssistantUiController { ...@@ -128,12 +132,25 @@ public class AutofillAssistantUiController {
mActivity = activity; mActivity = activity;
mCoordinator = new AssistantCoordinator(activity, controller, tabObscuringHandler, mCoordinator = new AssistantCoordinator(activity, controller, tabObscuringHandler,
onboardingCoordinator == null ? null : onboardingCoordinator.transferControls(), onboardingCoordinator == null ? null : onboardingCoordinator.transferControls(),
this::safeNativeOnKeyboardVisibilityChanged, this::safeNativeOnBackButtonClicked); this::safeNativeOnKeyboardVisibilityChanged);
mActivityTabObserver = mActivityTabObserver =
new ActivityTabProvider.ActivityTabTabObserver(activity.getActivityTabProvider()) { new ActivityTabProvider.ActivityTabTabObserver(
activity.getActivityTabProvider(), /* shouldTrigger = */ true) {
@Override @Override
protected void onObservingDifferentTab(Tab tab, boolean hint) { protected void onObservingDifferentTab(Tab tab, boolean hint) {
if (mWebContents == null) return; if (mWebContents == null) {
if (!hint) {
// This particular scenario would happen only if we're switching
// from a tab with no Autofill Assistant running to a tab with AA
// running with no tab switching hinting (i.e. a first notification
// with |hint| set to true).
// In this case the native side is not yet fully initialized, so we
// need to wait for the web contents to be set from native before
// notifying native that the tab was selected.
setWebContentObserver(tab);
}
return;
}
if (!allowTabSwitching) { if (!allowTabSwitching) {
if (tab == null || tab.getWebContents() != mWebContents) { if (tab == null || tab.getWebContents() != mWebContents) {
...@@ -149,6 +166,7 @@ public class AutofillAssistantUiController { ...@@ -149,6 +166,7 @@ public class AutofillAssistantUiController {
dismissSnackbar(); dismissSnackbar();
if (tab == null) { if (tab == null) {
safeOnTabSwitched(getModel().getBottomSheetState());
// A null tab indicates that there's no selected tab; Most likely, we're // A null tab indicates that there's no selected tab; Most likely, we're
// in the process of selecting a new tab. Hide the UI for possible reuse // in the process of selecting a new tab. Hide the UI for possible reuse
// later. // later.
...@@ -156,15 +174,23 @@ public class AutofillAssistantUiController { ...@@ -156,15 +174,23 @@ public class AutofillAssistantUiController {
} else if (tab.getWebContents() == mWebContents) { } else if (tab.getWebContents() == mWebContents) {
// The original tab was re-selected. Show it again and force an // The original tab was re-selected. Show it again and force an
// expansion on the bottom sheet. // expansion on the bottom sheet.
safeNativeSetVisible(true); if (!hint) {
if (mCoordinator.getBottomBarCoordinator() != null) { // Here and below, we're only interested in restoring the UI for the
showContentAndExpandBottomSheet(); // case where hint is false, meaning that the tab is shown. This is
// the only way to be sure that the bottomsheet is unsuppressed when
// we try to restore the status to what it was prior to switching.
safeOnTabSelected();
} }
} else { } else {
//
safeOnTabSwitched(getModel().getBottomSheetState());
// A new tab was selected. If Autofill Assistant is running on it, // A new tab was selected. If Autofill Assistant is running on it,
// attach the UI to that other instance, otherwise destroy the UI. // attach the UI to that other instance, otherwise destroy the UI.
AutofillAssistantClient.fromWebContents(mWebContents) AutofillAssistantClient.fromWebContents(mWebContents)
.transferUiTo(tab.getWebContents()); .transferUiTo(tab.getWebContents());
if (!hint) {
safeOnTabSelected();
}
} }
} }
...@@ -179,6 +205,7 @@ public class AutofillAssistantUiController { ...@@ -179,6 +205,7 @@ public class AutofillAssistantUiController {
return; return;
} }
safeOnTabSwitched(getModel().getBottomSheetState());
// If we have an open snackbar, execute the callback immediately. This // If we have an open snackbar, execute the callback immediately. This
// may shut down the Autofill Assistant. // may shut down the Autofill Assistant.
if (mSnackbarController != null) { if (mSnackbarController != null) {
...@@ -190,6 +217,23 @@ public class AutofillAssistantUiController { ...@@ -190,6 +217,23 @@ public class AutofillAssistantUiController {
}; };
} }
private void setWebContentObserver(Tab tab) {
getModel().addObserver(new PropertyObserver<PropertyKey>() {
@Override
public void onPropertyChanged(
PropertyObservable<PropertyKey> source, @Nullable PropertyKey propertyKey) {
if (AssistantModel.WEB_CONTENTS == propertyKey) {
getModel().removeObserver(this);
if (tab != null
&& tab.getWebContents()
== getModel().get(AssistantModel.WEB_CONTENTS)) {
safeOnTabSelected();
}
}
}
});
}
// Native => Java methods. // Native => Java methods.
// TODO(crbug.com/806868): Some of these functions still have a little bit of logic (e.g. make // TODO(crbug.com/806868): Some of these functions still have a little bit of logic (e.g. make
...@@ -229,7 +273,8 @@ public class AutofillAssistantUiController { ...@@ -229,7 +273,8 @@ public class AutofillAssistantUiController {
@CalledByNative @CalledByNative
private void showContentAndExpandBottomSheet() { private void showContentAndExpandBottomSheet() {
mCoordinator.getBottomBarCoordinator().showContentAndExpand(); mCoordinator.getBottomBarCoordinator().showContent(
/* shouldExpand = */ true, /* animate = */ true);
} }
@CalledByNative @CalledByNative
...@@ -257,6 +302,11 @@ public class AutofillAssistantUiController { ...@@ -257,6 +302,11 @@ public class AutofillAssistantUiController {
mCoordinator.getKeyboardCoordinator().hideKeyboard(); mCoordinator.getKeyboardCoordinator().hideKeyboard();
} }
@CalledByNative
private void restoreBottomSheetState(@SheetState int state) {
mCoordinator.getBottomBarCoordinator().restoreState(state);
}
@CalledByNative @CalledByNative
private void hideKeyboardIfFocusNotOnText() { private void hideKeyboardIfFocusNotOnText() {
mCoordinator.getKeyboardCoordinator().hideKeyboardIfFocusNotOnText(); mCoordinator.getKeyboardCoordinator().hideKeyboardIfFocusNotOnText();
...@@ -424,18 +474,24 @@ public class AutofillAssistantUiController { ...@@ -424,18 +474,24 @@ public class AutofillAssistantUiController {
} }
} }
private boolean safeNativeOnBackButtonClicked() { private void safeNativeSetVisible(boolean visible) {
if (mNativeUiController != 0) { if (mNativeUiController != 0) {
return AutofillAssistantUiControllerJni.get().onBackButtonClicked( AutofillAssistantUiControllerJni.get().setVisible(
mNativeUiController, AutofillAssistantUiController.this); mNativeUiController, AutofillAssistantUiController.this, visible);
} }
return false;
} }
private void safeNativeSetVisible(boolean visible) { private void safeOnTabSwitched(@SheetState int state) {
if (mNativeUiController != 0) { if (mNativeUiController != 0) {
AutofillAssistantUiControllerJni.get().setVisible( AutofillAssistantUiControllerJni.get().onTabSwitched(
mNativeUiController, AutofillAssistantUiController.this, visible); mNativeUiController, AutofillAssistantUiController.this, state);
}
}
private void safeOnTabSelected() {
if (mNativeUiController != 0) {
AutofillAssistantUiControllerJni.get().onTabSelected(
mNativeUiController, AutofillAssistantUiController.this);
} }
} }
...@@ -455,9 +511,10 @@ public class AutofillAssistantUiController { ...@@ -455,9 +511,10 @@ public class AutofillAssistantUiController {
long nativeUiControllerAndroid, AutofillAssistantUiController caller); long nativeUiControllerAndroid, AutofillAssistantUiController caller);
void onKeyboardVisibilityChanged(long nativeUiControllerAndroid, void onKeyboardVisibilityChanged(long nativeUiControllerAndroid,
AutofillAssistantUiController caller, boolean visible); AutofillAssistantUiController caller, boolean visible);
boolean onBackButtonClicked(
long nativeUiControllerAndroid, AutofillAssistantUiController caller);
void setVisible(long nativeUiControllerAndroid, AutofillAssistantUiController caller, void setVisible(long nativeUiControllerAndroid, AutofillAssistantUiController caller,
boolean visible); boolean visible);
void onTabSwitched(long nativeUiControllerAndroid, AutofillAssistantUiController caller,
@SheetState int state);
void onTabSelected(long nativeUiControllerAndroid, AutofillAssistantUiController caller);
} }
} }
...@@ -7,19 +7,24 @@ package org.chromium.chrome.browser.autofill_assistant; ...@@ -7,19 +7,24 @@ package org.chromium.chrome.browser.autofill_assistant;
import org.chromium.base.task.PostTask; import org.chromium.base.task.PostTask;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent; import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetController; import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.SheetState;
import org.chromium.components.browser_ui.bottomsheet.EmptyBottomSheetObserver; import org.chromium.components.browser_ui.bottomsheet.EmptyBottomSheetObserver;
import org.chromium.content_public.browser.UiThreadTaskTraits; import org.chromium.content_public.browser.UiThreadTaskTraits;
class BottomSheetUtils { class BottomSheetUtils {
/** Request {@code controller} to show {@code content} and expand the sheet when it is shown. */ /** Request {@code controller} to show {@code content} and expand the sheet when it is shown. */
static void showContentAndExpand(BottomSheetController controller, static void showContentAndMaybeExpand(BottomSheetController controller,
AssistantBottomSheetContent content, boolean animate) { AssistantBottomSheetContent content, boolean shouldExpand, boolean animate) {
// Show the content. // Show the content.
if (controller.requestShowContent(content, animate)) { boolean contentShown = controller.requestShowContent(content, animate);
if (!shouldExpand) {
return;
}
if (contentShown) {
controller.expandSheet(); controller.expandSheet();
} else { } else {
// If the content is not directly shown, add an observer that will expand the sheet when // If the content is not directly shown, add an observer that will expand the sheet
// it is. // when it is.
controller.addObserver(new EmptyBottomSheetObserver() { controller.addObserver(new EmptyBottomSheetObserver() {
@Override @Override
public void onSheetContentChanged(BottomSheetContent newContent) { public void onSheetContentChanged(BottomSheetContent newContent) {
...@@ -33,5 +38,53 @@ class BottomSheetUtils { ...@@ -33,5 +38,53 @@ class BottomSheetUtils {
} }
} }
static void restoreState(BottomSheetController controller, BottomSheetContent content,
@SheetState int targetState) {
if (controller.getSheetState() == targetState) {
return;
}
if (controller.getCurrentSheetContent() == content) {
restoreStateInternal(controller, content, targetState);
} else {
controller.addObserver(new EmptyBottomSheetObserver() {
@Override
public void onSheetContentChanged(BottomSheetContent newContent) {
if (newContent == content) {
controller.removeObserver(this);
restoreStateInternal(controller, content, targetState);
}
}
});
}
}
private static void restoreStateInternal(BottomSheetController controller,
BottomSheetContent content, @SheetState int targetState) {
if (controller.getSheetState() != SheetState.SCROLLING) {
setStateInternal(controller, content, targetState);
} else {
controller.addObserver(new EmptyBottomSheetObserver() {
@Override
public void onSheetStateChanged(int newState) {
controller.removeObserver(this);
if (newState != targetState) {
setStateInternal(controller, content, targetState);
}
}
});
}
}
private static void setStateInternal(BottomSheetController controller,
BottomSheetContent content, @SheetState int targetState) {
if (targetState == SheetState.HIDDEN && !controller.isSheetHiding()) {
controller.hideContent(content, /* animate = */ false);
} else if (targetState == SheetState.PEEK) {
controller.collapseSheet(/* animate = */ false);
} else if (targetState == SheetState.HALF || targetState == SheetState.FULL) {
controller.expandSheet();
}
}
private BottomSheetUtils() {} private BottomSheetUtils() {}
} }
...@@ -124,13 +124,14 @@ public class AutofillAssistantUiTest { ...@@ -124,13 +124,14 @@ public class AutofillAssistantUiTest {
mCustomTabActivityTestRule.startCustomTabActivityWithIntent(createMinimalCustomTabIntent()); mCustomTabActivityTestRule.startCustomTabActivityWithIntent(createMinimalCustomTabIntent());
BottomSheetController bottomSheetController = BottomSheetController bottomSheetController =
TestThreadUtils.runOnUiThreadBlocking(this::initializeBottomSheet); TestThreadUtils.runOnUiThreadBlocking(this::initializeBottomSheet);
AssistantCoordinator assistantCoordinator = TestThreadUtils.runOnUiThreadBlocking( AssistantCoordinator assistantCoordinator = TestThreadUtils.runOnUiThreadBlocking(() -> {
() AssistantCoordinator coordinator = new AssistantCoordinator(getActivity(),
-> new AssistantCoordinator(getActivity(), bottomSheetController, bottomSheetController, getActivity().getTabObscuringHandler(),
getActivity().getTabObscuringHandler(), /* overlayCoordinator= */ null,
/* overlayCoordinator= */ null, /* keyboardCoordinatorDelegate= */ null);
/* keyboardCoordinatorDelegate= */ null, coordinator.show();
/* bottomSheetDelegate= */ null)); return coordinator;
});
// Bottom sheet is shown in the BottomSheet when creating the AssistantCoordinator. // Bottom sheet is shown in the BottomSheet when creating the AssistantCoordinator.
View contentView = AutofillAssistantUiTestUtil.getBottomSheetController(getActivity()) View contentView = AutofillAssistantUiTestUtil.getBottomSheetController(getActivity())
...@@ -257,13 +258,14 @@ public class AutofillAssistantUiTest { ...@@ -257,13 +258,14 @@ public class AutofillAssistantUiTest {
mCustomTabActivityTestRule.startCustomTabActivityWithIntent(createMinimalCustomTabIntent()); mCustomTabActivityTestRule.startCustomTabActivityWithIntent(createMinimalCustomTabIntent());
BottomSheetController bottomSheetController = BottomSheetController bottomSheetController =
TestThreadUtils.runOnUiThreadBlocking(this::initializeBottomSheet); TestThreadUtils.runOnUiThreadBlocking(this::initializeBottomSheet);
AssistantCoordinator assistantCoordinator = TestThreadUtils.runOnUiThreadBlocking( AssistantCoordinator assistantCoordinator = TestThreadUtils.runOnUiThreadBlocking(() -> {
() AssistantCoordinator coordinator = new AssistantCoordinator(getActivity(),
-> new AssistantCoordinator(getActivity(), bottomSheetController, bottomSheetController, getActivity().getTabObscuringHandler(),
getActivity().getTabObscuringHandler(), /* overlayCoordinator= */ null,
/* overlayCoordinator= */ null, /* keyboardCoordinatorDelegate= */ null);
/* keyboardCoordinatorDelegate= */ null, coordinator.show();
/* bottomSheetDelegate= */ null)); return coordinator;
});
// Bottom sheet is shown in the BottomSheet when creating the AssistantCoordinator. // Bottom sheet is shown in the BottomSheet when creating the AssistantCoordinator.
View contentView = AutofillAssistantUiTestUtil.getBottomSheetController(getActivity()) View contentView = AutofillAssistantUiTestUtil.getBottomSheetController(getActivity())
......
...@@ -2219,6 +2219,8 @@ static_library("browser") { ...@@ -2219,6 +2219,8 @@ static_library("browser") {
"android/accessibility/font_size_prefs_android.cc", "android/accessibility/font_size_prefs_android.cc",
"android/accessibility/font_size_prefs_android.h", "android/accessibility/font_size_prefs_android.h",
"android/android_theme_resources.h", "android/android_theme_resources.h",
"android/autofill_assistant/assistant_bottom_bar_delegate.cc",
"android/autofill_assistant/assistant_bottom_bar_delegate.h",
"android/autofill_assistant/assistant_collect_user_data_delegate.cc", "android/autofill_assistant/assistant_collect_user_data_delegate.cc",
"android/autofill_assistant/assistant_collect_user_data_delegate.h", "android/autofill_assistant/assistant_collect_user_data_delegate.h",
"android/autofill_assistant/assistant_form_delegate.cc", "android/autofill_assistant/assistant_form_delegate.cc",
......
// 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.
#include "chrome/browser/android/autofill_assistant/assistant_bottom_bar_delegate.h"
#include "chrome/android/features/autofill_assistant/jni_headers/AssistantBottomBarNativeDelegate_jni.h"
#include "chrome/browser/android/autofill_assistant/ui_controller_android.h"
using base::android::AttachCurrentThread;
namespace autofill_assistant {
AssistantBottomBarDelegate::AssistantBottomBarDelegate(
UiControllerAndroid* ui_controller)
: ui_controller_(ui_controller) {
java_assistant_bottom_bar_delegate_ =
Java_AssistantBottomBarNativeDelegate_create(
AttachCurrentThread(), reinterpret_cast<intptr_t>(this));
}
AssistantBottomBarDelegate::~AssistantBottomBarDelegate() {
Java_AssistantBottomBarNativeDelegate_clearNativePtr(
AttachCurrentThread(), java_assistant_bottom_bar_delegate_);
}
bool AssistantBottomBarDelegate::OnBackButtonClicked(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller) {
return ui_controller_->OnBackButtonClicked();
}
base::android::ScopedJavaGlobalRef<jobject>
AssistantBottomBarDelegate::GetJavaObject() {
return java_assistant_bottom_bar_delegate_;
}
} // namespace autofill_assistant
// 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.
#ifndef CHROME_BROWSER_ANDROID_AUTOFILL_ASSISTANT_ASSISTANT_BOTTOM_BAR_DELEGATE_H_
#define CHROME_BROWSER_ANDROID_AUTOFILL_ASSISTANT_ASSISTANT_BOTTOM_BAR_DELEGATE_H_
#include "base/android/scoped_java_ref.h"
namespace autofill_assistant {
class UiControllerAndroid;
// Delegate class for the assistant bottom bar.
class AssistantBottomBarDelegate {
public:
explicit AssistantBottomBarDelegate(UiControllerAndroid* ui_controller);
~AssistantBottomBarDelegate();
// Returns true if the back button press was handled by Autofill Assistant.
bool OnBackButtonClicked(JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller);
base::android::ScopedJavaGlobalRef<jobject> GetJavaObject();
private:
UiControllerAndroid* ui_controller_;
// Java-side AssistantBottomBarDelegate object.
base::android::ScopedJavaGlobalRef<jobject>
java_assistant_bottom_bar_delegate_;
};
} // namespace autofill_assistant
#endif // CHROME_BROWSER_ANDROID_AUTOFILL_ASSISTANT_ASSISTANT_BOTTOM_BAR_DELEGATE_H_
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
#include "chrome/common/channel_info.h" #include "chrome/common/channel_info.h"
#include "components/autofill/core/browser/data_model/autofill_profile.h" #include "components/autofill/core/browser/data_model/autofill_profile.h"
#include "components/autofill/core/browser/data_model/credit_card.h" #include "components/autofill/core/browser/data_model/credit_card.h"
#include "components/autofill_assistant/browser/bottom_sheet_state.h"
#include "components/autofill_assistant/browser/client_settings.h" #include "components/autofill_assistant/browser/client_settings.h"
#include "components/autofill_assistant/browser/controller.h" #include "components/autofill_assistant/browser/controller.h"
#include "components/autofill_assistant/browser/event_handler.h" #include "components/autofill_assistant/browser/event_handler.h"
...@@ -283,7 +284,8 @@ UiControllerAndroid::UiControllerAndroid( ...@@ -283,7 +284,8 @@ UiControllerAndroid::UiControllerAndroid(
header_delegate_(this), header_delegate_(this),
collect_user_data_delegate_(this), collect_user_data_delegate_(this),
form_delegate_(this), form_delegate_(this),
generic_ui_delegate_(this) { generic_ui_delegate_(this),
bottom_bar_delegate_(this) {
java_object_ = Java_AutofillAssistantUiController_create( java_object_ = Java_AutofillAssistantUiController_create(
env, jactivity, env, jactivity,
/* allowTabSwitching= */ /* allowTabSwitching= */
...@@ -303,6 +305,8 @@ UiControllerAndroid::UiControllerAndroid( ...@@ -303,6 +305,8 @@ UiControllerAndroid::UiControllerAndroid(
Java_AssistantCollectUserDataModel_setDelegate( Java_AssistantCollectUserDataModel_setDelegate(
env, GetCollectUserDataModel(), env, GetCollectUserDataModel(),
collect_user_data_delegate_.GetJavaObject()); collect_user_data_delegate_.GetJavaObject());
Java_AssistantModel_setBottomBarDelegate(
env, GetModel(), bottom_bar_delegate_.GetJavaObject());
} }
void UiControllerAndroid::Attach(content::WebContents* web_contents, void UiControllerAndroid::Attach(content::WebContents* web_contents,
...@@ -327,61 +331,23 @@ void UiControllerAndroid::Attach(content::WebContents* web_contents, ...@@ -327,61 +331,23 @@ void UiControllerAndroid::Attach(content::WebContents* web_contents,
auto java_web_contents = web_contents->GetJavaWebContents(); auto java_web_contents = web_contents->GetJavaWebContents();
Java_AutofillAssistantUiController_setWebContents(env, java_object_, Java_AutofillAssistantUiController_setWebContents(env, java_object_,
java_web_contents); java_web_contents);
Java_AssistantModel_setWebContents(env, GetModel(), java_web_contents);
Java_AssistantCollectUserDataModel_setWebContents( Java_AssistantCollectUserDataModel_setWebContents(
env, GetCollectUserDataModel(), java_web_contents); env, GetCollectUserDataModel(), java_web_contents);
OnClientSettingsChanged(ui_delegate_->GetClientSettings()); OnClientSettingsChanged(ui_delegate_->GetClientSettings());
if (ui_delegate->GetState() != AutofillAssistantState::INACTIVE) { if (ui_delegate->GetState() != AutofillAssistantState::INACTIVE &&
ui_delegate->IsTabSelected()) {
// The UI was created for an existing Controller. // The UI was created for an existing Controller.
OnStatusMessageChanged(ui_delegate->GetStatusMessage()); RestoreUi();
OnBubbleMessageChanged(ui_delegate->GetBubbleMessage()); } else if (ui_delegate->GetState() == AutofillAssistantState::INACTIVE) {
auto step_progress_bar_configuration = SetVisible(true);
ui_delegate->GetStepProgressBarConfiguration();
if (step_progress_bar_configuration.has_value()) {
OnStepProgressBarConfigurationChanged(*step_progress_bar_configuration);
if (step_progress_bar_configuration->use_step_progress_bar()) {
auto active_step = ui_delegate_->GetProgressActiveStep();
if (active_step.has_value()) {
OnProgressActiveStepChanged(*active_step);
}
OnProgressBarErrorStateChanged(ui_delegate->GetProgressBarErrorState());
}
} else {
OnStepProgressBarConfigurationChanged(
ShowProgressBarProto::StepProgressBarConfiguration());
OnProgressChanged(ui_delegate->GetProgress());
}
OnProgressVisibilityChanged(ui_delegate->GetProgressVisible());
OnInfoBoxChanged(ui_delegate_->GetInfoBox());
OnDetailsChanged(ui_delegate->GetDetails());
OnUserActionsChanged(ui_delegate_->GetUserActions());
OnCollectUserDataOptionsChanged(ui_delegate->GetCollectUserDataOptions());
OnUserDataChanged(ui_delegate->GetUserData(), UserData::FieldChange::ALL);
OnGenericUserInterfaceChanged(ui_delegate->GetGenericUiProto());
std::vector<RectF> area;
ui_delegate->GetTouchableArea(&area);
std::vector<RectF> restricted_area;
ui_delegate->GetRestrictedArea(&restricted_area);
RectF visual_viewport;
ui_delegate->GetVisualViewport(&visual_viewport);
OnTouchableAreaChanged(visual_viewport, area, restricted_area);
OnViewportModeChanged(ui_delegate->GetViewportMode());
OnPeekModeChanged(ui_delegate->GetPeekMode());
OnFormChanged(ui_delegate->GetForm(), ui_delegate->GetFormResult());
// TODO(b/145204744): Store the collapsed or expanded state from the bottom
// sheet when detaching the UI so that it can be restored appropriately
// here.
UiDelegate::OverlayColors colors;
ui_delegate->GetOverlayColors(&colors);
OnOverlayColorsChanged(colors);
OnStateChanged(ui_delegate->GetState());
} }
// The call to set the web contents will, for some edge cases, trigger a call
SetVisible(true); // from the Java side to the onTabSelected method.
// We want this to happen only after the AttachUI method was fully executed,
// as it would otherwise find that IsTabSelected() is true when deciding if
// restoring the UI.
Java_AssistantModel_setWebContents(env, GetModel(), java_web_contents);
} }
void UiControllerAndroid::Detach() { void UiControllerAndroid::Detach() {
...@@ -441,7 +407,7 @@ void UiControllerAndroid::SetupForState() { ...@@ -441,7 +407,7 @@ void UiControllerAndroid::SetupForState() {
SetOverlayState(OverlayState::HIDDEN); SetOverlayState(OverlayState::HIDDEN);
SetSpinPoodle(false); SetSpinPoodle(false);
if (should_prompt_action_expand_sheet) if (should_prompt_action_expand_sheet && ui_delegate_->IsTabSelected())
ShowContentAndExpandBottomSheet(); ShowContentAndExpandBottomSheet();
return; return;
...@@ -449,7 +415,7 @@ void UiControllerAndroid::SetupForState() { ...@@ -449,7 +415,7 @@ void UiControllerAndroid::SetupForState() {
SetOverlayState(OverlayState::PARTIAL); SetOverlayState(OverlayState::PARTIAL);
SetSpinPoodle(false); SetSpinPoodle(false);
if (should_prompt_action_expand_sheet) if (should_prompt_action_expand_sheet && ui_delegate_->IsTabSelected())
ShowContentAndExpandBottomSheet(); ShowContentAndExpandBottomSheet();
return; return;
...@@ -468,7 +434,8 @@ void UiControllerAndroid::SetupForState() { ...@@ -468,7 +434,8 @@ void UiControllerAndroid::SetupForState() {
SetSpinPoodle(false); SetSpinPoodle(false);
// Make sure the user sees the error message. // Make sure the user sees the error message.
ShowContentAndExpandBottomSheet(); if (ui_delegate_->IsTabSelected())
ShowContentAndExpandBottomSheet();
ResetGenericUiControllers(); ResetGenericUiControllers();
return; return;
...@@ -673,6 +640,81 @@ void UiControllerAndroid::SetVisible(bool visible) { ...@@ -673,6 +640,81 @@ void UiControllerAndroid::SetVisible(bool visible) {
} }
} }
void UiControllerAndroid::RestoreUi() {
if (ui_delegate_ == nullptr)
return;
OnStatusMessageChanged(ui_delegate_->GetStatusMessage());
OnBubbleMessageChanged(ui_delegate_->GetBubbleMessage());
auto step_progress_bar_configuration =
ui_delegate_->GetStepProgressBarConfiguration();
if (step_progress_bar_configuration.has_value()) {
OnStepProgressBarConfigurationChanged(*step_progress_bar_configuration);
if (step_progress_bar_configuration->use_step_progress_bar()) {
auto active_step = ui_delegate_->GetProgressActiveStep();
if (active_step.has_value()) {
OnProgressActiveStepChanged(*active_step);
}
OnProgressBarErrorStateChanged(ui_delegate_->GetProgressBarErrorState());
}
} else {
OnStepProgressBarConfigurationChanged(
ShowProgressBarProto::StepProgressBarConfiguration());
OnProgressChanged(ui_delegate_->GetProgress());
}
OnProgressVisibilityChanged(ui_delegate_->GetProgressVisible());
OnInfoBoxChanged(ui_delegate_->GetInfoBox());
OnDetailsChanged(ui_delegate_->GetDetails());
OnUserActionsChanged(ui_delegate_->GetUserActions());
OnCollectUserDataOptionsChanged(ui_delegate_->GetCollectUserDataOptions());
OnUserDataChanged(ui_delegate_->GetUserData(), UserData::FieldChange::ALL);
OnGenericUserInterfaceChanged(ui_delegate_->GetGenericUiProto());
std::vector<RectF> area;
ui_delegate_->GetTouchableArea(&area);
std::vector<RectF> restricted_area;
ui_delegate_->GetRestrictedArea(&restricted_area);
RectF visual_viewport;
ui_delegate_->GetVisualViewport(&visual_viewport);
OnTouchableAreaChanged(visual_viewport, area, restricted_area);
OnViewportModeChanged(ui_delegate_->GetViewportMode());
OnPeekModeChanged(ui_delegate_->GetPeekMode());
OnFormChanged(ui_delegate_->GetForm(), ui_delegate_->GetFormResult());
UiDelegate::OverlayColors colors;
ui_delegate_->GetOverlayColors(&colors);
OnOverlayColorsChanged(colors);
SetVisible(true);
Java_AutofillAssistantUiController_restoreBottomSheetState(
AttachCurrentThread(), java_object_,
ui_controller_android_utils::ToJavaBottomSheetState(
ui_delegate_->GetBottomSheetState()));
}
void UiControllerAndroid::OnTabSwitched(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
jint state) {
if (ui_delegate_ == nullptr) {
return;
}
ui_delegate_->SetBottomSheetState(
ui_controller_android_utils::ToNativeBottomSheetState(state));
ui_delegate_->SetTabSelected(false);
}
void UiControllerAndroid::OnTabSelected(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller) {
if (ui_delegate_ == nullptr) {
return;
}
if (!ui_delegate_->IsTabSelected()) {
RestoreUi();
ui_delegate_->SetTabSelected(true);
}
}
// Actions carousels related methods. // Actions carousels related methods.
void UiControllerAndroid::UpdateActions( void UiControllerAndroid::UpdateActions(
...@@ -829,13 +871,13 @@ void UiControllerAndroid::OnKeyboardVisibilityChanged( ...@@ -829,13 +871,13 @@ void UiControllerAndroid::OnKeyboardVisibilityChanged(
!visible); !visible);
} }
bool UiControllerAndroid::OnBackButtonClicked( bool UiControllerAndroid::OnBackButtonClicked() {
JNIEnv* env,
const base::android::JavaParamRef<jobject>& caller) {
// If the keyboard is currently shown, clicking the back button should // If the keyboard is currently shown, clicking the back button should
// hide the keyboard rather than close autofill assistant. // hide the keyboard rather than close autofill assistant.
if (Java_AutofillAssistantUiController_isKeyboardShown(env, java_object_)) { if (Java_AutofillAssistantUiController_isKeyboardShown(AttachCurrentThread(),
Java_AutofillAssistantUiController_hideKeyboard(env, java_object_); java_object_)) {
Java_AutofillAssistantUiController_hideKeyboard(AttachCurrentThread(),
java_object_);
return true; return true;
} }
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "base/android/scoped_java_ref.h" #include "base/android/scoped_java_ref.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/timer/timer.h" #include "base/timer/timer.h"
#include "chrome/browser/android/autofill_assistant/assistant_bottom_bar_delegate.h"
#include "chrome/browser/android/autofill_assistant/assistant_collect_user_data_delegate.h" #include "chrome/browser/android/autofill_assistant/assistant_collect_user_data_delegate.h"
#include "chrome/browser/android/autofill_assistant/assistant_form_delegate.h" #include "chrome/browser/android/autofill_assistant/assistant_form_delegate.h"
#include "chrome/browser/android/autofill_assistant/assistant_generic_ui_delegate.h" #include "chrome/browser/android/autofill_assistant/assistant_generic_ui_delegate.h"
...@@ -165,6 +166,9 @@ class UiControllerAndroid : public ControllerObserver { ...@@ -165,6 +166,9 @@ class UiControllerAndroid : public ControllerObserver {
int choice_index, int choice_index,
bool selected); bool selected);
// Called by AssistantBottomBarNativeDelegate:
bool OnBackButtonClicked();
// Called by Java. // Called by Java.
void SnackbarResult(JNIEnv* env, void SnackbarResult(JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj, const base::android::JavaParamRef<jobject>& obj,
...@@ -193,11 +197,14 @@ class UiControllerAndroid : public ControllerObserver { ...@@ -193,11 +197,14 @@ class UiControllerAndroid : public ControllerObserver {
JNIEnv* env, JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller, const base::android::JavaParamRef<jobject>& jcaller,
jboolean visible); jboolean visible);
bool OnBackButtonClicked(JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller);
void SetVisible(JNIEnv* env, void SetVisible(JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller, const base::android::JavaParamRef<jobject>& jcaller,
jboolean visible); jboolean visible);
void OnTabSwitched(JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
jint state);
void OnTabSelected(JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller);
private: private:
// A pointer to the client. nullptr until Attach() is called. // A pointer to the client. nullptr until Attach() is called.
...@@ -210,6 +217,7 @@ class UiControllerAndroid : public ControllerObserver { ...@@ -210,6 +217,7 @@ class UiControllerAndroid : public ControllerObserver {
AssistantCollectUserDataDelegate collect_user_data_delegate_; AssistantCollectUserDataDelegate collect_user_data_delegate_;
AssistantFormDelegate form_delegate_; AssistantFormDelegate form_delegate_;
AssistantGenericUiDelegate generic_ui_delegate_; AssistantGenericUiDelegate generic_ui_delegate_;
AssistantBottomBarDelegate bottom_bar_delegate_;
// What to do if undo is not pressed on the current snackbar. // What to do if undo is not pressed on the current snackbar.
base::OnceCallback<void()> snackbar_action_; base::OnceCallback<void()> snackbar_action_;
...@@ -259,6 +267,9 @@ class UiControllerAndroid : public ControllerObserver { ...@@ -259,6 +267,9 @@ class UiControllerAndroid : public ControllerObserver {
// Makes the whole of AA invisible or visible again. // Makes the whole of AA invisible or visible again.
void SetVisible(bool visible); void SetVisible(bool visible);
// Restore the UI for the current UIDelegate.
void RestoreUi();
// Timer started when reaching the STOPPED state. It allows keeping the UI up // Timer started when reaching the STOPPED state. It allows keeping the UI up
// for a few seconds before it destroys itself. // for a few seconds before it destroys itself.
std::unique_ptr<base::OneShotTimer> destroy_timer_; std::unique_ptr<base::OneShotTimer> destroy_timer_;
......
...@@ -397,6 +397,32 @@ std::string SafeConvertJavaStringToNative( ...@@ -397,6 +397,32 @@ std::string SafeConvertJavaStringToNative(
return native_string; return native_string;
} }
BottomSheetState ToNativeBottomSheetState(int state) {
switch (state) {
case 1:
return BottomSheetState::COLLAPSED;
case 2:
case 3:
return BottomSheetState::EXPANDED;
default:
return BottomSheetState::UNDEFINED;
}
}
int ToJavaBottomSheetState(BottomSheetState state) {
switch (state) {
case BottomSheetState::COLLAPSED:
return 1;
case BottomSheetState::UNDEFINED:
// The current assumption is that Autobot always starts with the bottom
// sheet expanded.
case BottomSheetState::EXPANDED:
return 2;
default:
return -1;
}
}
} // namespace ui_controller_android_utils } // namespace ui_controller_android_utils
} // namespace autofill_assistant } // namespace autofill_assistant
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "base/android/jni_android.h" #include "base/android/jni_android.h"
#include "base/optional.h" #include "base/optional.h"
#include "components/autofill_assistant/browser/bottom_sheet_state.h"
#include "components/autofill_assistant/browser/service.pb.h" #include "components/autofill_assistant/browser/service.pb.h"
#include "components/autofill_assistant/browser/user_model.h" #include "components/autofill_assistant/browser/user_model.h"
#include "components/autofill_assistant/browser/view_layout.pb.h" #include "components/autofill_assistant/browser/view_layout.pb.h"
...@@ -80,6 +81,13 @@ std::string SafeConvertJavaStringToNative( ...@@ -80,6 +81,13 @@ std::string SafeConvertJavaStringToNative(
JNIEnv* env, JNIEnv* env,
const base::android::JavaParamRef<jstring>& jstring); const base::android::JavaParamRef<jstring>& jstring);
// Creates a BottomSheetState from the Android SheetState enum defined in
// components/browser_ui/bottomsheet/BottomSheetController.java.
BottomSheetState ToNativeBottomSheetState(int state);
// Converts a BottomSheetState to the Android SheetState enum.
int ToJavaBottomSheetState(BottomSheetState state);
} // namespace ui_controller_android_utils } // namespace ui_controller_android_utils
} // namespace autofill_assistant } // namespace autofill_assistant
......
...@@ -93,6 +93,8 @@ static_library("browser") { ...@@ -93,6 +93,8 @@ static_library("browser") {
"basic_interactions.h", "basic_interactions.h",
"batch_element_checker.cc", "batch_element_checker.cc",
"batch_element_checker.h", "batch_element_checker.h",
"bottom_sheet_state.cc",
"bottom_sheet_state.h",
"chip.cc", "chip.cc",
"chip.h", "chip.h",
"client.h", "client.h",
......
// 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.
#include "components/autofill_assistant/browser/bottom_sheet_state.h"
namespace autofill_assistant {
std::ostream& operator<<(std::ostream& out, const BottomSheetState& state) {
#ifdef NDEBUG
out << static_cast<int>(state);
return out;
#else
switch (state) {
case BottomSheetState::UNDEFINED:
out << "UNDEFINED";
break;
case BottomSheetState::COLLAPSED:
out << "COLLAPSED";
break;
case BottomSheetState::EXPANDED:
out << "EXPANDED";
break;
}
return out;
#endif
}
} // namespace autofill_assistant
// 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.
#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_BOTTOM_SHEET_STATE_H_
#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_BOTTOM_SHEET_STATE_H_
#include <ostream>
namespace autofill_assistant {
// See definition in
// components/browser_ui/bottomsheet/BottomSheetController.java
enum class BottomSheetState {
UNDEFINED = 0,
COLLAPSED = 1,
EXPANDED = 2,
};
std::ostream& operator<<(std::ostream& out, const BottomSheetState& state);
} // namespace autofill_assistant
#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_BOTTOM_SHEET_STATE_H_
\ No newline at end of file
...@@ -634,6 +634,22 @@ ConfigureBottomSheetProto::PeekMode Controller::GetPeekMode() { ...@@ -634,6 +634,22 @@ ConfigureBottomSheetProto::PeekMode Controller::GetPeekMode() {
return peek_mode_; return peek_mode_;
} }
BottomSheetState Controller::GetBottomSheetState() {
return bottom_sheet_state_;
}
void Controller::SetBottomSheetState(BottomSheetState state) {
bottom_sheet_state_ = state;
}
bool Controller::IsTabSelected() {
return tab_selected_;
}
void Controller::SetTabSelected(bool selected) {
tab_selected_ = selected;
}
void Controller::SetOverlayColors(std::unique_ptr<OverlayColors> colors) { void Controller::SetOverlayColors(std::unique_ptr<OverlayColors> colors) {
overlay_colors_ = std::move(colors); overlay_colors_ = std::move(colors);
if (overlay_colors_) { if (overlay_colors_) {
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "base/macros.h" #include "base/macros.h"
#include "base/optional.h" #include "base/optional.h"
#include "components/autofill_assistant/browser/basic_interactions.h" #include "components/autofill_assistant/browser/basic_interactions.h"
#include "components/autofill_assistant/browser/bottom_sheet_state.h"
#include "components/autofill_assistant/browser/client.h" #include "components/autofill_assistant/browser/client.h"
#include "components/autofill_assistant/browser/client_settings.h" #include "components/autofill_assistant/browser/client_settings.h"
#include "components/autofill_assistant/browser/element_area.h" #include "components/autofill_assistant/browser/element_area.h"
...@@ -221,6 +222,10 @@ class Controller : public ScriptExecutorDelegate, ...@@ -221,6 +222,10 @@ class Controller : public ScriptExecutorDelegate,
void MaybeReportFirstCheckDone(); void MaybeReportFirstCheckDone();
ViewportMode GetViewportMode() override; ViewportMode GetViewportMode() override;
ConfigureBottomSheetProto::PeekMode GetPeekMode() override; ConfigureBottomSheetProto::PeekMode GetPeekMode() override;
BottomSheetState GetBottomSheetState() override;
void SetBottomSheetState(BottomSheetState state) override;
bool IsTabSelected() override;
void SetTabSelected(bool selected) override;
void GetOverlayColors(OverlayColors* colors) const override; void GetOverlayColors(OverlayColors* colors) const override;
const ClientSettings& GetClientSettings() const override; const ClientSettings& GetClientSettings() const override;
const FormProto* GetForm() const override; const FormProto* GetForm() const override;
...@@ -421,6 +426,12 @@ class Controller : public ScriptExecutorDelegate, ...@@ -421,6 +426,12 @@ class Controller : public ScriptExecutorDelegate,
ConfigureBottomSheetProto::HANDLE; ConfigureBottomSheetProto::HANDLE;
bool auto_change_peek_mode_ = false; bool auto_change_peek_mode_ = false;
// The latest bottom sheet state stored.
BottomSheetState bottom_sheet_state_ = BottomSheetState::UNDEFINED;
// Whether the tab associated with this controller is currently selected.
bool tab_selected_ = true;
std::unique_ptr<OverlayColors> overlay_colors_; std::unique_ptr<OverlayColors> overlay_colors_;
// A copy of the most recently set user data options. Can be used to determine // A copy of the most recently set user data options. Can be used to determine
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <vector> #include <vector>
#include "base/optional.h" #include "base/optional.h"
#include "bottom_sheet_state.h"
#include "components/autofill_assistant/browser/event_handler.h" #include "components/autofill_assistant/browser/event_handler.h"
#include "components/autofill_assistant/browser/metrics.h" #include "components/autofill_assistant/browser/metrics.h"
#include "components/autofill_assistant/browser/rectf.h" #include "components/autofill_assistant/browser/rectf.h"
...@@ -190,6 +191,18 @@ class UiDelegate { ...@@ -190,6 +191,18 @@ class UiDelegate {
// Peek mode state and whether it was changed automatically last time. // Peek mode state and whether it was changed automatically last time.
virtual ConfigureBottomSheetProto::PeekMode GetPeekMode() = 0; virtual ConfigureBottomSheetProto::PeekMode GetPeekMode() = 0;
// Gets the bottom sheet state.
virtual BottomSheetState GetBottomSheetState() = 0;
// Sets the state of the bottom sheet.
virtual void SetBottomSheetState(BottomSheetState state) = 0;
// Gets whether the tab associated with this controller is currently selected.
virtual bool IsTabSelected() = 0;
// Sets whether the tab associated with this controller is currently selected.
virtual void SetTabSelected(bool selected) = 0;
// Fills in the overlay colors. // Fills in the overlay colors.
virtual void GetOverlayColors(OverlayColors* colors) const = 0; virtual void GetOverlayColors(OverlayColors* colors) const = 0;
......
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