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

[Autofill Assistant] Add popup to chips.

This allows chips to show a popup menu when tapped, instead of directly
invoking their callback. For now, this is only intended for trigger
scripts, specifically for the cancel popup.

A screenshot of the popup is in the linked bug.

Bug: b/172585820
Change-Id: Id32c529b7f32a11b2aaef18a9391e708795be38d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2521188
Commit-Queue: Clemens Arbesser <arbesser@google.com>
Reviewed-by: default avatarLuca Hunkeler <hluca@google.com>
Cr-Commit-Position: refs/heads/master@{#824909}
parent 11d95ef7
...@@ -5,9 +5,13 @@ ...@@ -5,9 +5,13 @@
package org.chromium.chrome.browser.autofill_assistant.carousel; package org.chromium.chrome.browser.autofill_assistant.carousel;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import org.chromium.base.Callback;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.util.List;
/** /**
* A chip to display to the user. * A chip to display to the user.
...@@ -73,6 +77,15 @@ public class AssistantChip { ...@@ -73,6 +77,15 @@ public class AssistantChip {
/** The callback that will be triggered when this chip is clicked. */ /** The callback that will be triggered when this chip is clicked. */
private final Runnable mSelectedListener; private final Runnable mSelectedListener;
/**
* The list of popup items to show when the chip is tapped. When specified, the regular {@code
* mSelectedListener} will be automatically replaced with a callback to display the popup menu.
*/
private @Nullable List<String> mPopupItems;
/** The callback to invoke when the n'th item in {@code mPopupItems} is selected. */
private @Nullable Callback<Integer> mOnPopupItemSelected;
public AssistantChip(@Type int type, @Icon int icon, String text, boolean disabled, public AssistantChip(@Type int type, @Icon int icon, String text, boolean disabled,
boolean sticky, boolean visible, Runnable selectedListener) { boolean sticky, boolean visible, Runnable selectedListener) {
mType = type; mType = type;
...@@ -120,6 +133,19 @@ public class AssistantChip { ...@@ -120,6 +133,19 @@ public class AssistantChip {
return mSelectedListener; return mSelectedListener;
} }
public void setPopupItems(List<String> popupItems, Callback<Integer> onSelectedCallback) {
mPopupItems = popupItems;
mOnPopupItemSelected = onSelectedCallback;
}
public @Nullable List<String> getPopupItems() {
return mPopupItems;
}
public @Nullable Callback<Integer> getOnPopupItemSelectedCallback() {
return mOnPopupItemSelected;
}
@Override @Override
public boolean equals(Object other) { public boolean equals(Object other) {
if (!(other instanceof AssistantChip)) { if (!(other instanceof AssistantChip)) {
......
...@@ -5,9 +5,12 @@ ...@@ -5,9 +5,12 @@
package org.chromium.chrome.browser.autofill_assistant.carousel; package org.chromium.chrome.browser.autofill_assistant.carousel;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.PopupMenu;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView.ViewHolder; import androidx.recyclerview.widget.RecyclerView.ViewHolder;
import org.chromium.chrome.autofill_assistant.R; import org.chromium.chrome.autofill_assistant.R;
...@@ -18,6 +21,7 @@ import org.chromium.chrome.autofill_assistant.R; ...@@ -18,6 +21,7 @@ import org.chromium.chrome.autofill_assistant.R;
*/ */
public class AssistantChipViewHolder extends ViewHolder { public class AssistantChipViewHolder extends ViewHolder {
private final ButtonView mView; private final ButtonView mView;
private @Nullable PopupMenu mPopupMenu;
/** The type of this ViewHolder, as returned by {@link #getViewType(AssistantChip)}. */ /** The type of this ViewHolder, as returned by {@link #getViewType(AssistantChip)}. */
private final int mType; private final int mType;
...@@ -80,7 +84,22 @@ public class AssistantChipViewHolder extends ViewHolder { ...@@ -80,7 +84,22 @@ public class AssistantChipViewHolder extends ViewHolder {
// Setting this view to clickable may be required for a11y to correctly announce it. // Setting this view to clickable may be required for a11y to correctly announce it.
mView.setClickable(true); mView.setClickable(true);
mView.setOnClickListener(ignoredView -> chip.getSelectedListener().run());
// If a popup is specified, instead of invoking chip.getSelectedListener, show the popup
// menu and invoke the popup callback.
if (chip.getPopupItems() != null) {
mPopupMenu = new PopupMenu(mView.getContext(), mView);
for (int i = 0; i < chip.getPopupItems().size(); i++) {
mPopupMenu.getMenu().add(Menu.NONE, i, Menu.NONE, chip.getPopupItems().get(i));
}
mPopupMenu.setOnMenuItemClickListener(item -> {
chip.getOnPopupItemSelectedCallback().onResult(item.getItemId());
return true;
});
mView.setOnClickListener(ignoredView -> mPopupMenu.show());
} else {
mView.setOnClickListener(ignoredView -> chip.getSelectedListener().run());
}
int iconResource; int iconResource;
int iconDescriptionResource = 0; int iconDescriptionResource = 0;
......
...@@ -5,12 +5,18 @@ ...@@ -5,12 +5,18 @@
package org.chromium.chrome.browser.autofill_assistant; package org.chromium.chrome.browser.autofill_assistant;
import static androidx.test.espresso.Espresso.onView; import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.assertion.ViewAssertions.matches; import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.RootMatchers.withDecorView;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withClassName;
import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription; import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
import static androidx.test.espresso.matcher.ViewMatchers.withId; import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText; import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.core.StringContains.containsString;
import static org.mockito.Mockito.verify;
import android.support.test.InstrumentationRegistry; import android.support.test.InstrumentationRegistry;
import android.view.Gravity; import android.view.Gravity;
import android.view.ViewGroup; import android.view.ViewGroup;
...@@ -23,6 +29,9 @@ import org.junit.Before; ...@@ -23,6 +29,9 @@ import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.chromium.base.test.util.CommandLineFlags; import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.chrome.autofill_assistant.R; import org.chromium.chrome.autofill_assistant.R;
...@@ -48,6 +57,15 @@ public class AutofillAssistantTriggerScriptTest { ...@@ -48,6 +57,15 @@ public class AutofillAssistantTriggerScriptTest {
private static final String TEST_PAGE = "/components/test/data/autofill_assistant/html/" private static final String TEST_PAGE = "/components/test/data/autofill_assistant/html/"
+ "autofill_assistant_target_website.html"; + "autofill_assistant_target_website.html";
@Mock
Runnable mRunnableMockCancelSession;
@Mock
Runnable mRunnableMockCancelForever;
@Rule
public MockitoRule mMockitoRule = MockitoJUnit.rule();
@Rule @Rule
public CustomTabActivityTestRule mTestRule = new CustomTabActivityTestRule(); public CustomTabActivityTestRule mTestRule = new CustomTabActivityTestRule();
...@@ -118,6 +136,14 @@ public class AutofillAssistantTriggerScriptTest { ...@@ -118,6 +136,14 @@ public class AutofillAssistantTriggerScriptTest {
List<AssistantChip> leftAlignedChips = triggerScript.getLeftAlignedChipsForTest(); List<AssistantChip> leftAlignedChips = triggerScript.getLeftAlignedChipsForTest();
leftAlignedChips.add(new AssistantChip(AssistantChip.Type.BUTTON_HAIRLINE, leftAlignedChips.add(new AssistantChip(AssistantChip.Type.BUTTON_HAIRLINE,
AssistantChip.Icon.OVERFLOW, "", false, false, true, () -> {})); AssistantChip.Icon.OVERFLOW, "", false, false, true, () -> {}));
leftAlignedChips.get(0).setPopupItems(
Arrays.asList("Not for this session", "Never show again"), result -> {
if (result == 0) {
mRunnableMockCancelSession.run();
} else if (result == 1) {
mRunnableMockCancelForever.run();
}
});
List<AssistantChip> rightAlignedChips = triggerScript.getRightAlignedChipsForTest(); List<AssistantChip> rightAlignedChips = triggerScript.getRightAlignedChipsForTest();
rightAlignedChips.add(new AssistantChip(AssistantChip.Type.BUTTON_HAIRLINE, rightAlignedChips.add(new AssistantChip(AssistantChip.Type.BUTTON_HAIRLINE,
...@@ -142,5 +168,19 @@ public class AutofillAssistantTriggerScriptTest { ...@@ -142,5 +168,19 @@ public class AutofillAssistantTriggerScriptTest {
.check(matches(isDisplayed())); .check(matches(isDisplayed()));
onView(withText("Not now")).check(matches(isDisplayed())); onView(withText("Not now")).check(matches(isDisplayed()));
onView(withText("Fast checkout")).check(matches(isDisplayed())); onView(withText("Fast checkout")).check(matches(isDisplayed()));
onView(withContentDescription(R.string.autofill_assistant_overflow_options))
.perform(click());
onView(withText("Not for this session"))
.inRoot(withDecorView(withClassName(containsString("Popup"))))
.perform(click());
verify(mRunnableMockCancelSession).run();
onView(withContentDescription(R.string.autofill_assistant_overflow_options))
.perform(click());
onView(withText("Never show again"))
.inRoot(withDecorView(withClassName(containsString("Popup"))))
.perform(click());
verify(mRunnableMockCancelForever).run();
} }
} }
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