Commit ae596325 authored by Clemens Arbesser's avatar Clemens Arbesser Committed by Commit Bot

Reland "[Autofill Assistant] Allow multiple chips in the header."

This is a reland of 81959047

There was a missing notification in our bottom bar coordinator, which
led to the bottom sheet not resizing properly. No idea why this passed
CQ last time, as the test should have failed every time.

Original change's description:
> [Autofill Assistant] Allow multiple chips in the header.
>
> This CL allows the header to support a full carousel.
>
> What it does:
>  - Move header chip logic into native.
>  - Use RecyclerView instead of hard-coded single chip view.
>  - Potentially fixes an issue where the status message does not stretch
> across the whole available width.
>  - Chips in the header now support change animations.
>
> There is an issue caused by a layout resize when switching between PEEK
> and non-PEEK mode. This is further discussed in the linked bug. The
> workaround is to delay the layout resize until the sheet state has
> settled.
>
> Bug: b/160856358
> Change-Id: Id7617ed41119b85fdaa00ebaff7e856984b74b0a
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2288567
> Commit-Queue: Clemens Arbesser <arbesser@google.com>
> Reviewed-by: Mathias Carlen <mcarlen@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#796376}

Bug: b/160856358
Change-Id: If3acb7cc6fabcbab7ce9bc82c0664c53110b5cd3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2350419Reviewed-by: default avatarMathias Carlen <mcarlen@chromium.org>
Commit-Queue: Clemens Arbesser <arbesser@google.com>
Cr-Commit-Position: refs/heads/master@{#797600}
parent 8b114db4
......@@ -8,6 +8,7 @@
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/header_top_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="56dp"
......
......@@ -151,6 +151,8 @@ class AssistantBottomBarCoordinator implements AssistantPeekHeightCoordinator.De
// We don't want to animate the carousels children views as they are already animated by the
// recyclers ItemAnimator, so we exclude them to avoid a clash between the animations.
mLayoutTransition.excludeChildren(mActionsCoordinator.getView(), /* exclude= */ true);
mLayoutTransition.excludeChildren(
mHeaderCoordinator.getCarouselView(), /* exclude= */ true);
// do not animate the contents of the payment method section inside the section choice list,
// since the animation is not required and causes a rendering crash.
......@@ -190,7 +192,11 @@ class AssistantBottomBarCoordinator implements AssistantPeekHeightCoordinator.De
controller.addObserver(new EmptyBottomSheetObserver() {
@Override
public void onSheetStateChanged(int newState) {
maybeShowHeaderChip();
// Note: recycler view updates while the bottom sheet is SCROLLING result in a
// BottomSheet assertion.
if (newState != BottomSheetController.SheetState.SCROLLING) {
maybeShowHeaderChips();
}
}
@Override
......@@ -256,10 +262,9 @@ class AssistantBottomBarCoordinator implements AssistantPeekHeightCoordinator.De
}
private void setupAnimations(AssistantModel model, ViewGroup rootView) {
// Animate when the chip in the header changes.
model.getHeaderModel().addObserver((source, propertyKey) -> {
if (propertyKey == AssistantHeaderModel.CHIP
|| propertyKey == AssistantHeaderModel.CHIP_VISIBLE) {
if (propertyKey == AssistantHeaderModel.CHIPS_VISIBLE
|| propertyKey == AssistantHeaderModel.CHIPS) {
animateChildren(rootView);
}
});
......@@ -295,12 +300,12 @@ class AssistantBottomBarCoordinator implements AssistantPeekHeightCoordinator.De
TransitionManager.beginDelayedTransition(rootView, mLayoutTransition);
}
private void maybeShowHeaderChip() {
boolean showChip =
private void maybeShowHeaderChips() {
boolean showChips =
mBottomSheetController.getSheetState() == BottomSheetController.SheetState.PEEK
&& mPeekHeightCoordinator.getPeekMode()
== AssistantPeekHeightCoordinator.PeekMode.HANDLE_HEADER;
mModel.getHeaderModel().set(AssistantHeaderModel.CHIP_VISIBLE, showChip);
mModel.getHeaderModel().set(AssistantHeaderModel.CHIPS_VISIBLE, showChips);
}
/**
......@@ -350,7 +355,7 @@ class AssistantBottomBarCoordinator implements AssistantPeekHeightCoordinator.De
/** Set the peek mode. */
void setPeekMode(@AssistantPeekHeightCoordinator.PeekMode int peekMode) {
mPeekHeightCoordinator.setPeekMode(peekMode);
maybeShowHeaderChip();
maybeShowHeaderChips();
}
/** Expand the bottom sheet. */
......
......@@ -18,7 +18,6 @@ import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantCarouselModel;
import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantChip;
import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantChip.Type;
import org.chromium.chrome.browser.autofill_assistant.header.AssistantHeaderModel;
import org.chromium.chrome.browser.autofill_assistant.metrics.DropOutReason;
import org.chromium.chrome.browser.customtabs.CustomTabActivity;
import org.chromium.chrome.browser.profiles.Profile;
......@@ -283,55 +282,56 @@ public class AutofillAssistantUiController {
}
/**
* Adds an action button to the chip list, which executes the action {@code actionIndex}.
* Creates an action button which executes the action {@code actionIndex}.
*/
@CalledByNative
private void addActionButton(List<AssistantChip> chips, int icon, String text, int actionIndex,
private AssistantChip createActionButton(int icon, String text, int actionIndex,
boolean disabled, boolean sticky, String identifier) {
chips.add(new AssistantChip(AssistantChip.Type.BUTTON_HAIRLINE, icon, text, disabled,
sticky, identifier, () -> safeNativeOnUserActionSelected(actionIndex)));
return new AssistantChip(AssistantChip.Type.BUTTON_HAIRLINE, icon, text, disabled, sticky,
identifier, () -> safeNativeOnUserActionSelected(actionIndex));
}
/**
* Adds a highlighted action button to the chip list, which executes the action {@code
* actionIndex}.
* Creates a highlighted action button which executes the action {@code actionIndex}.
*/
@CalledByNative
private void addHighlightedActionButton(List<AssistantChip> chips, int icon, String text,
int actionIndex, boolean disabled, boolean sticky, String identifier) {
chips.add(new AssistantChip(Type.BUTTON_FILLED_BLUE, icon, text, disabled, sticky,
identifier, () -> safeNativeOnUserActionSelected(actionIndex)));
private AssistantChip createHighlightedActionButton(int icon, String text, int actionIndex,
boolean disabled, boolean sticky, String identifier) {
return new AssistantChip(Type.BUTTON_FILLED_BLUE, icon, text, disabled, sticky, identifier,
() -> safeNativeOnUserActionSelected(actionIndex));
}
/**
* Adds a cancel action button to the chip list. If the keyboard is currently shown, it
* dismisses the keyboard. Otherwise, it shows the snackbar and then executes
* {@code actionIndex}, or shuts down Autofill Assistant if {@code actionIndex} is {@code -1}.
* Creates a cancel action button. If the keyboard is currently shown, it dismisses the
* keyboard. Otherwise, it shows the snackbar and then executes {@code actionIndex}, or shuts
* down Autofill Assistant if {@code actionIndex} is {@code -1}.
*/
@CalledByNative
private void addCancelButton(List<AssistantChip> chips, int icon, String text, int actionIndex,
private AssistantChip createCancelButton(int icon, String text, int actionIndex,
boolean disabled, boolean sticky, String identifier) {
chips.add(new AssistantChip(AssistantChip.Type.BUTTON_HAIRLINE, icon, text, disabled,
sticky, identifier, () -> safeNativeOnCancelButtonClicked(actionIndex)));
return new AssistantChip(AssistantChip.Type.BUTTON_HAIRLINE, icon, text, disabled, sticky,
identifier, () -> safeNativeOnCancelButtonClicked(actionIndex));
}
/**
* Adds a close action button to the chip list, which shuts down Autofill Assistant.
*/
@CalledByNative
private void addCloseButton(List<AssistantChip> chips, int icon, String text, boolean disabled,
boolean sticky, String identifier) {
chips.add(new AssistantChip(AssistantChip.Type.BUTTON_HAIRLINE, icon, text, disabled,
sticky, identifier, this::safeNativeOnCloseButtonClicked));
private AssistantChip createCloseButton(
int icon, String text, boolean disabled, boolean sticky, String identifier) {
return new AssistantChip(AssistantChip.Type.BUTTON_HAIRLINE, icon, text, disabled, sticky,
identifier, this::safeNativeOnCloseButtonClicked);
}
@CalledByNative
private static void appendChipToList(List<AssistantChip> chips, AssistantChip chip) {
chips.add(chip);
}
@CalledByNative
private void setActions(List<AssistantChip> chips) {
// TODO(b/144075373): Move this to AssistantCarouselModel and AssistantHeaderModel. Move
// header chip logic to native.
AssistantCarouselModel model = getModel().getActionsModel();
model.setChips(chips);
setHeaderChip(chips);
// TODO(b/144075373): Move this to AssistantCarouselModel.
getModel().getActionsModel().setChips(chips);
}
@CalledByNative
......@@ -358,19 +358,6 @@ public class AutofillAssistantUiController {
model.setChips(newChips);
}
private void setHeaderChip(List<AssistantChip> chips) {
// The header chip is the first sticky chip found in the actions.
AssistantChip headerChip = null;
for (AssistantChip chip : chips) {
if (chip.isSticky()) {
headerChip = chip;
break;
}
}
getModel().getHeaderModel().set(AssistantHeaderModel.CHIP, headerChip);
}
@CalledByNative
private void setViewportMode(@AssistantViewportMode int mode) {
mCoordinator.getBottomBarCoordinator().setViewportMode(mode);
......
......@@ -22,7 +22,7 @@ import java.util.List;
public class AssistantChipAdapter extends RecyclerView.Adapter<AssistantChipViewHolder> {
private final List<AssistantChip> mChips = new ArrayList<>();
void setChips(List<AssistantChip> chips) {
public void setChips(List<AssistantChip> chips) {
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffUtil.Callback() {
@Override
public int getOldListSize() {
......
......@@ -5,13 +5,19 @@
package org.chromium.chrome.browser.autofill_assistant.header;
import android.content.Context;
import android.graphics.Rect;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import org.chromium.chrome.autofill_assistant.R;
import org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiController;
import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantChipAdapter;
import org.chromium.chrome.browser.autofill_assistant.header.AssistantHeaderViewBinder.ViewHolder;
import org.chromium.chrome.browser.signin.DisplayableProfileData;
import org.chromium.chrome.browser.signin.IdentityServicesProvider;
......@@ -32,6 +38,7 @@ public class AssistantHeaderCoordinator implements ProfileDataCache.Observer {
private final ImageView mProfileView;
private final String mSignedInAccountName;
private final ViewHolder mViewHolder;
private final RecyclerView mChipsContainer;
public AssistantHeaderCoordinator(Context context, AssistantHeaderModel model) {
// Create the poodle and insert it before the status message. We have to create a view
......@@ -56,8 +63,52 @@ public class AssistantHeaderCoordinator implements ProfileDataCache.Observer {
identityManager.getPrimaryAccountInfo(ConsentLevel.SYNC));
setupProfileImage();
mChipsContainer = new RecyclerView(context);
final int innerChipSpacing = context.getResources().getDimensionPixelSize(
R.dimen.autofill_assistant_actions_spacing);
mChipsContainer.addItemDecoration(new RecyclerView.ItemDecoration() {
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view,
@NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
outRect.top = 0;
outRect.bottom = 0;
if (state.getItemCount() <= 1) {
return;
}
// If old position != NO_POSITION, it means the carousel is being animated and we
// should use that position in our logic.
int position = parent.getChildAdapterPosition(view);
RecyclerView.ViewHolder viewHolder = parent.getChildViewHolder(view);
if (viewHolder != null && viewHolder.getOldPosition() != RecyclerView.NO_POSITION) {
position = viewHolder.getOldPosition();
}
if (position == RecyclerView.NO_POSITION) {
return;
}
outRect.left = position == 0 ? 0 : innerChipSpacing;
outRect.right = 0;
}
});
AssistantChipAdapter chipAdapter = new AssistantChipAdapter();
mChipsContainer.setAdapter(chipAdapter);
LinearLayoutManager layoutManager = new LinearLayoutManager(context);
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
mChipsContainer.setLayoutManager(layoutManager);
mView.setPadding(mChipsContainer.getPaddingLeft(), mChipsContainer.getPaddingTop(),
context.getResources().getDimensionPixelSize(
R.dimen.autofill_assistant_profile_icon_padding),
mChipsContainer.getPaddingBottom());
ViewGroup topContainer = mView.findViewById(R.id.header_top_container);
topContainer.addView(mChipsContainer);
// Bind view and mediator through the model.
mViewHolder = new AssistantHeaderViewBinder.ViewHolder(context, mView, poodle);
mViewHolder =
new AssistantHeaderViewBinder.ViewHolder(context, mView, poodle, mChipsContainer);
AssistantHeaderViewBinder viewBinder = new AssistantHeaderViewBinder();
PropertyModelChangeProcessor.create(model, mViewHolder, viewBinder);
......@@ -77,6 +128,11 @@ public class AssistantHeaderCoordinator implements ProfileDataCache.Observer {
return mView;
}
/** Returns the view containing the chips. */
public View getCarouselView() {
return mChipsContainer;
}
/**
* Cleanup resources when this goes out of scope.
*/
......
......@@ -4,6 +4,8 @@
package org.chromium.chrome.browser.autofill_assistant.header;
import android.support.annotation.VisibleForTesting;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantChip;
......@@ -18,6 +20,9 @@ import java.util.List;
*/
@JNINamespace("autofill_assistant")
public class AssistantHeaderModel extends PropertyModel {
public static final WritableObjectPropertyKey<List<AssistantChip>> CHIPS =
new WritableObjectPropertyKey<>();
public static final WritableObjectPropertyKey<String> STATUS_MESSAGE =
new WritableObjectPropertyKey<>();
......@@ -45,10 +50,7 @@ public class AssistantHeaderModel extends PropertyModel {
public static final WritableObjectPropertyKey<Runnable> FEEDBACK_BUTTON_CALLBACK =
new WritableObjectPropertyKey<>();
public static final WritableObjectPropertyKey<AssistantChip> CHIP =
new WritableObjectPropertyKey<>();
public static final WritableBooleanPropertyKey CHIP_VISIBLE = new WritableBooleanPropertyKey();
public static final WritableBooleanPropertyKey CHIPS_VISIBLE = new WritableBooleanPropertyKey();
public static final WritableBooleanPropertyKey DISABLE_ANIMATIONS_FOR_TESTING =
new WritableBooleanPropertyKey();
......@@ -56,7 +58,8 @@ public class AssistantHeaderModel extends PropertyModel {
public AssistantHeaderModel() {
super(STATUS_MESSAGE, BUBBLE_MESSAGE, PROGRESS, PROGRESS_ACTIVE_STEP, PROGRESS_BAR_ERROR,
PROGRESS_VISIBLE, USE_STEP_PROGRESS_BAR, STEP_PROGRESS_BAR_ICONS, SPIN_POODLE,
FEEDBACK_BUTTON_CALLBACK, CHIP, CHIP_VISIBLE, DISABLE_ANIMATIONS_FOR_TESTING);
FEEDBACK_BUTTON_CALLBACK, CHIPS, CHIPS_VISIBLE, DISABLE_ANIMATIONS_FOR_TESTING);
set(CHIPS, new ArrayList<>());
}
@CalledByNative
......@@ -127,4 +130,16 @@ public class AssistantHeaderModel extends PropertyModel {
private void setDisableAnimations(boolean disableAnimations) {
set(DISABLE_ANIMATIONS_FOR_TESTING, disableAnimations);
}
@CalledByNative
@VisibleForTesting
public void setChips(List<AssistantChip> chips) {
// Move last chip (cancel) to first position. For legacy reasons, native builds this list
// such that the cancel chip is last, but the regular carousel will show it in the left-most
// position and the header should mirror this.
if (chips.size() > 1) {
chips.add(0, chips.remove(chips.size() - 1));
}
set(CHIPS, chips);
}
}
......@@ -11,11 +11,12 @@ import android.widget.PopupMenu;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.RecyclerView;
import org.chromium.chrome.autofill_assistant.R;
import org.chromium.chrome.browser.autofill_assistant.AssistantTextUtils;
import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantChip;
import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantChipViewHolder;
import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantChipAdapter;
import org.chromium.chrome.browser.settings.SettingsLauncher;
import org.chromium.chrome.browser.settings.SettingsLauncherImpl;
import org.chromium.chrome.browser.sync.settings.SyncAndServicesSettings;
......@@ -48,12 +49,12 @@ class AssistantHeaderViewBinder
final AssistantStepProgressBar mStepProgressBar;
final View mProfileIconView;
final PopupMenu mProfileIconMenu;
@Nullable
AssistantChipViewHolder mChip;
final RecyclerView mChipsContainer;
@Nullable
TextBubble mTextBubble;
ViewHolder(Context context, ViewGroup headerView, AnimatedPoodle poodle) {
ViewHolder(Context context, ViewGroup headerView, AnimatedPoodle poodle,
RecyclerView chipsContainer) {
mContext = context;
mPoodle = poodle;
mHeader = headerView;
......@@ -65,6 +66,7 @@ class AssistantHeaderViewBinder
mProfileIconMenu = new PopupMenu(context, mProfileIconView);
mProfileIconMenu.inflate(R.menu.profile_icon_menu);
mProfileIconView.setOnClickListener(unusedView -> mProfileIconMenu.show());
mChipsContainer = chipsContainer;
}
void disableAnimations(boolean disable) {
......@@ -73,6 +75,8 @@ class AssistantHeaderViewBinder
// Hiding the animated poodle seems to be the easiest way to disable its animation since
// {@link LogoView#setAnimationEnabled(boolean)} is private.
mPoodle.getView().setVisibility(View.INVISIBLE);
((DefaultItemAnimator) mChipsContainer.getItemAnimator())
.setSupportsChangeAnimations(!disable);
}
void updateProgressBarVisibility(boolean visible, boolean useStepProgressBar) {
......@@ -113,11 +117,13 @@ class AssistantHeaderViewBinder
view.mPoodle.setSpinEnabled(model.get(AssistantHeaderModel.SPIN_POODLE));
} else if (AssistantHeaderModel.FEEDBACK_BUTTON_CALLBACK == propertyKey) {
setProfileMenuListener(view, model.get(AssistantHeaderModel.FEEDBACK_BUTTON_CALLBACK));
} else if (AssistantHeaderModel.CHIP == propertyKey) {
bindChip(view, model.get(AssistantHeaderModel.CHIP));
maybeShowChip(model, view);
} else if (AssistantHeaderModel.CHIP_VISIBLE == propertyKey) {
maybeShowChip(model, view);
} else if (AssistantHeaderModel.CHIPS == propertyKey) {
view.mChipsContainer.invalidateItemDecorations();
((AssistantChipAdapter) view.mChipsContainer.getAdapter())
.setChips(model.get(AssistantHeaderModel.CHIPS));
maybeShowChips(model, view);
} else if (AssistantHeaderModel.CHIPS_VISIBLE == propertyKey) {
maybeShowChips(model, view);
} else if (AssistantHeaderModel.BUBBLE_MESSAGE == propertyKey) {
showOrDismissBubble(model, view);
} else if (AssistantHeaderModel.DISABLE_ANIMATIONS_FOR_TESTING == propertyKey) {
......@@ -127,45 +133,18 @@ class AssistantHeaderViewBinder
}
}
private void maybeShowChip(AssistantHeaderModel model, ViewHolder view) {
if (model.get(AssistantHeaderModel.CHIP_VISIBLE)
&& model.get(AssistantHeaderModel.CHIP) != null) {
view.mChip.getView().setVisibility(View.VISIBLE);
private void maybeShowChips(AssistantHeaderModel model, ViewHolder view) {
if (model.get(AssistantHeaderModel.CHIPS_VISIBLE)
&& !model.get(AssistantHeaderModel.CHIPS).isEmpty()) {
view.mChipsContainer.setVisibility(View.VISIBLE);
view.mProfileIconView.setVisibility(View.GONE);
} else {
if (view.mChip != null) {
view.mChip.getView().setVisibility(View.GONE);
}
view.mChipsContainer.setVisibility(View.GONE);
view.mProfileIconView.setVisibility(View.VISIBLE);
}
}
private void bindChip(ViewHolder view, @Nullable AssistantChip chip) {
if (chip == null) {
return;
}
int viewType = AssistantChipViewHolder.getViewType(chip);
// If there is already a chip in the header but with incompatible type, remove it.
ViewGroup parent = (ViewGroup) view.mStatusMessage.getParent();
if (view.mChip != null && view.mChip.getType() != viewType) {
parent.removeView(view.mChip.getView());
view.mChip = null;
}
// If there is no chip already in the header, create one and add it at the end of the
// header.
if (view.mChip == null) {
view.mChip = AssistantChipViewHolder.create(view.mHeader, viewType);
parent.addView(view.mChip.getView());
}
// Bind the chip to the view.
view.mChip.bind(chip);
}
private void setProfileMenuListener(ViewHolder view, @Nullable Runnable feedbackCallback) {
view.mProfileIconMenu.setOnMenuItemClickListener(item -> {
int itemId = item.getItemId();
......
......@@ -7,6 +7,7 @@ package org.chromium.chrome.browser.autofill_assistant;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.assertion.PositionAssertions.isRightOf;
import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
......@@ -52,6 +53,9 @@ import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.components.browser_ui.widget.MaterialProgressBar;
import org.chromium.content_public.browser.test.util.TestThreadUtils;
import java.util.ArrayList;
import java.util.List;
/**
* Tests for the Autofill Assistant header.
*/
......@@ -206,15 +210,17 @@ public class AutofillAssistantHeaderUiTest {
chipText, /* disabled= */ false, /* sticky= */ false, "", () -> {});
// Set the header chip without displaying it.
TestThreadUtils.runOnUiThreadBlocking(() -> model.set(AssistantHeaderModel.CHIP, chip));
List<AssistantChip> chips = new ArrayList<>();
chips.add(chip);
TestThreadUtils.runOnUiThreadBlocking(() -> model.setChips(chips));
Matcher<View> chipMatcher =
allOf(isDescendantOfA(is(coordinator.getView())), withText(chipText));
onView(chipMatcher).check(matches(not(isDisplayed())));
onView(chipMatcher).check(doesNotExist());
// Show the chip
TestThreadUtils.runOnUiThreadBlocking(
() -> model.set(AssistantHeaderModel.CHIP_VISIBLE, true));
() -> model.set(AssistantHeaderModel.CHIPS_VISIBLE, true));
onView(chipMatcher)
.check(matches(isDisplayed()))
.check(isRightOf(withId(R.id.status_message)));
......
......@@ -678,11 +678,13 @@ void UiControllerAndroid::UpdateActions(
JNIEnv* env = AttachCurrentThread();
bool has_close_or_cancel = false;
auto chips = Java_AutofillAssistantUiController_createChipList(env);
auto jchips = Java_AutofillAssistantUiController_createChipList(env);
auto jsticky_chips = Java_AutofillAssistantUiController_createChipList(env);
int user_action_count = static_cast<int>(user_actions.size());
for (int i = 0; i < user_action_count; i++) {
const auto& action = user_actions[i];
const Chip& chip = action.chip();
base::android::ScopedJavaLocalRef<jobject> jchip;
switch (chip.type) {
default: // Ignore actions with other chip types or with no chips.
break;
......@@ -692,16 +694,17 @@ void UiControllerAndroid::UpdateActions(
// can hide all the chips except for the cancel chip when the keyboard
// is showing.
// TODO(b/149543425): Find a better way to do this.
Java_AutofillAssistantUiController_addHighlightedActionButton(
env, java_object_, chips, chip.icon,
base::android::ConvertUTF8ToJavaString(env, chip.text), i,
!action.enabled(), chip.sticky,
base::android::ConvertUTF8ToJavaString(env, ""));
jchip =
Java_AutofillAssistantUiController_createHighlightedActionButton(
env, java_object_, chip.icon,
base::android::ConvertUTF8ToJavaString(env, chip.text), i,
!action.enabled(), chip.sticky,
base::android::ConvertUTF8ToJavaString(env, ""));
break;
case NORMAL_ACTION:
Java_AutofillAssistantUiController_addActionButton(
env, java_object_, chips, chip.icon,
jchip = Java_AutofillAssistantUiController_createActionButton(
env, java_object_, chip.icon,
base::android::ConvertUTF8ToJavaString(env, chip.text), i,
!action.enabled(), chip.sticky,
base::android::ConvertUTF8ToJavaString(env, ""));
......@@ -710,8 +713,8 @@ void UiControllerAndroid::UpdateActions(
case CANCEL_ACTION:
// A Cancel button sneaks in an UNDO snackbar before executing the
// action, while a close button behaves like a normal button.
Java_AutofillAssistantUiController_addCancelButton(
env, java_object_, chips, chip.icon,
jchip = Java_AutofillAssistantUiController_createCancelButton(
env, java_object_, chip.icon,
base::android::ConvertUTF8ToJavaString(env, chip.text), i,
!action.enabled(), chip.sticky,
base::android::ConvertUTF8ToJavaString(env, kCancelChipIdentifier));
......@@ -719,8 +722,8 @@ void UiControllerAndroid::UpdateActions(
break;
case CLOSE_ACTION:
Java_AutofillAssistantUiController_addActionButton(
env, java_object_, chips, chip.icon,
jchip = Java_AutofillAssistantUiController_createActionButton(
env, java_object_, chip.icon,
base::android::ConvertUTF8ToJavaString(env, chip.text), i,
!action.enabled(), chip.sticky,
base::android::ConvertUTF8ToJavaString(env, ""));
......@@ -728,33 +731,50 @@ void UiControllerAndroid::UpdateActions(
break;
case DONE_ACTION:
Java_AutofillAssistantUiController_addHighlightedActionButton(
env, java_object_, chips, chip.icon,
base::android::ConvertUTF8ToJavaString(env, chip.text), i,
!action.enabled(), chip.sticky,
base::android::ConvertUTF8ToJavaString(env, ""));
jchip =
Java_AutofillAssistantUiController_createHighlightedActionButton(
env, java_object_, chip.icon,
base::android::ConvertUTF8ToJavaString(env, chip.text), i,
!action.enabled(), chip.sticky,
base::android::ConvertUTF8ToJavaString(env, ""));
has_close_or_cancel = true;
break;
}
if (jchip) {
Java_AutofillAssistantUiController_appendChipToList(env, jchips, jchip);
if (chip.sticky) {
Java_AutofillAssistantUiController_appendChipToList(env, jsticky_chips,
jchip);
}
}
}
if (!has_close_or_cancel) {
base::android::ScopedJavaLocalRef<jobject> jcancel_chip;
if (ui_delegate_->GetState() == AutofillAssistantState::STOPPED) {
Java_AutofillAssistantUiController_addCloseButton(
env, java_object_, chips, ICON_CLEAR,
jcancel_chip = Java_AutofillAssistantUiController_createCloseButton(
env, java_object_, ICON_CLEAR,
base::android::ConvertUTF8ToJavaString(env, ""),
/* disabled= */ false, /* sticky= */ true,
base::android::ConvertUTF8ToJavaString(env, ""));
} else if (ui_delegate_->GetState() != AutofillAssistantState::INACTIVE) {
Java_AutofillAssistantUiController_addCancelButton(
env, java_object_, chips, ICON_CLEAR,
jcancel_chip = Java_AutofillAssistantUiController_createCancelButton(
env, java_object_, ICON_CLEAR,
base::android::ConvertUTF8ToJavaString(env, ""), -1,
/* disabled= */ false, /* sticky= */ true,
base::android::ConvertUTF8ToJavaString(env, kCancelChipIdentifier));
}
if (jcancel_chip) {
Java_AutofillAssistantUiController_appendChipToList(env, jchips,
jcancel_chip);
Java_AutofillAssistantUiController_appendChipToList(env, jsticky_chips,
jcancel_chip);
}
}
Java_AutofillAssistantUiController_setActions(env, java_object_, chips);
Java_AutofillAssistantUiController_setActions(env, java_object_, jchips);
Java_AssistantHeaderModel_setChips(AttachCurrentThread(), GetHeaderModel(),
jsticky_chips);
}
void UiControllerAndroid::OnUserActionsChanged(
......
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