Commit 07d0a95d authored by Clemens Arbesser's avatar Clemens Arbesser Committed by Commit Bot

[Autofill Assistant] Added support for conditionals to ui framework

Bug: b/145043394
Change-Id: I32068d58638d7dfaf94f1112d622118b6bdfafaa
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2107993
Commit-Queue: Clemens Arbesser <arbesser@google.com>
Reviewed-by: default avatarMathias Carlen <mcarlen@chromium.org>
Cr-Commit-Position: refs/heads/master@{#752008}
parent 882958ff
...@@ -7,6 +7,7 @@ package org.chromium.chrome.browser.autofill_assistant; ...@@ -7,6 +7,7 @@ package org.chromium.chrome.browser.autofill_assistant;
import static android.support.test.espresso.Espresso.onView; import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click; import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.assertion.PositionAssertions.isLeftAlignedWith; import static android.support.test.espresso.assertion.PositionAssertions.isLeftAlignedWith;
import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist;
import static android.support.test.espresso.assertion.ViewAssertions.matches; import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.contrib.PickerActions.setDate; import static android.support.test.espresso.contrib.PickerActions.setDate;
import static android.support.test.espresso.matcher.RootMatchers.isDialog; import static android.support.test.espresso.matcher.RootMatchers.isDialog;
...@@ -68,6 +69,7 @@ import org.chromium.chrome.browser.autofill_assistant.proto.GenericUserInterface ...@@ -68,6 +69,7 @@ import org.chromium.chrome.browser.autofill_assistant.proto.GenericUserInterface
import org.chromium.chrome.browser.autofill_assistant.proto.ImageViewProto; import org.chromium.chrome.browser.autofill_assistant.proto.ImageViewProto;
import org.chromium.chrome.browser.autofill_assistant.proto.InfoPopupProto; import org.chromium.chrome.browser.autofill_assistant.proto.InfoPopupProto;
import org.chromium.chrome.browser.autofill_assistant.proto.IntList; import org.chromium.chrome.browser.autofill_assistant.proto.IntList;
import org.chromium.chrome.browser.autofill_assistant.proto.IntegerSumProto;
import org.chromium.chrome.browser.autofill_assistant.proto.InteractionProto; import org.chromium.chrome.browser.autofill_assistant.proto.InteractionProto;
import org.chromium.chrome.browser.autofill_assistant.proto.InteractionsProto; import org.chromium.chrome.browser.autofill_assistant.proto.InteractionsProto;
import org.chromium.chrome.browser.autofill_assistant.proto.LinearLayoutProto; import org.chromium.chrome.browser.autofill_assistant.proto.LinearLayoutProto;
...@@ -94,6 +96,7 @@ import org.chromium.chrome.browser.autofill_assistant.proto.ToStringProto; ...@@ -94,6 +96,7 @@ import org.chromium.chrome.browser.autofill_assistant.proto.ToStringProto;
import org.chromium.chrome.browser.autofill_assistant.proto.ToggleUserActionProto; import org.chromium.chrome.browser.autofill_assistant.proto.ToggleUserActionProto;
import org.chromium.chrome.browser.autofill_assistant.proto.UserActionList; import org.chromium.chrome.browser.autofill_assistant.proto.UserActionList;
import org.chromium.chrome.browser.autofill_assistant.proto.UserActionProto; import org.chromium.chrome.browser.autofill_assistant.proto.UserActionProto;
import org.chromium.chrome.browser.autofill_assistant.proto.ValueComparisonProto;
import org.chromium.chrome.browser.autofill_assistant.proto.ValueProto; import org.chromium.chrome.browser.autofill_assistant.proto.ValueProto;
import org.chromium.chrome.browser.autofill_assistant.proto.ViewAttributesProto; import org.chromium.chrome.browser.autofill_assistant.proto.ViewAttributesProto;
import org.chromium.chrome.browser.autofill_assistant.proto.ViewContainerProto; import org.chromium.chrome.browser.autofill_assistant.proto.ViewContainerProto;
...@@ -1291,4 +1294,102 @@ public class AutofillAssistantGenericUiTest { ...@@ -1291,4 +1294,102 @@ public class AutofillAssistantGenericUiTest {
onView(withText("Click me to toggle the chip")).perform(click()); onView(withText("Click me to toggle the chip")).perform(click());
waitUntilViewMatchesCondition(withContentDescription("Done"), not(isEnabled())); waitUntilViewMatchesCondition(withContentDescription("Done"), not(isEnabled()));
} }
/**
* Shows a simple text view that, when clicked 3 or more times, will show a popup.
*/
@Test
@MediumTest
public void testShowPopupIfClickedAtLeastThreeTimes() {
List<InteractionProto> interactions = new ArrayList<>();
interactions.add(
(InteractionProto) InteractionProto.newBuilder()
.setTriggerEvent(EventProto.newBuilder().setOnViewClicked(
OnViewClickedEventProto.newBuilder().setViewIdentifier(
"text_view")))
.addCallbacks(CallbackProto.newBuilder().setComputeValue(
ComputeValueProto.newBuilder()
.setResultModelIdentifier("counter")
.setIntegerSum(IntegerSumProto.newBuilder()
.setModelIdentifierA("counter")
.setModelIdentifierB("increment"))))
.addCallbacks(CallbackProto.newBuilder()
.setConditionModelIdentifier("condition")
.setShowInfoPopup(
ShowInfoPopupProto.newBuilder().setInfoPopup(
InfoPopupProto.newBuilder()
.setTitle("Info popup title")
.setText("Info text"))))
.build());
interactions.add(
(InteractionProto) InteractionProto.newBuilder()
.setTriggerEvent(EventProto.newBuilder().setOnValueChanged(
OnModelValueChangedEventProto.newBuilder().setModelIdentifier(
"counter")))
.addCallbacks(CallbackProto.newBuilder().setComputeValue(
ComputeValueProto.newBuilder()
.setResultModelIdentifier("condition")
.setComparison(
ValueComparisonProto.newBuilder()
.setModelIdentifierA("counter")
.setModelIdentifierB("target_counter")
.setMode(ValueComparisonProto.Mode
.GREATER_OR_EQUAL))))
.build());
List<ModelProto.ModelValue> modelValues = new ArrayList<>();
modelValues.add((ModelProto.ModelValue) ModelProto.ModelValue.newBuilder()
.setIdentifier("counter")
.setValue(ValueProto.newBuilder().setInts(
IntList.newBuilder().addValues(0)))
.build());
modelValues.add((ModelProto.ModelValue) ModelProto.ModelValue.newBuilder()
.setIdentifier("increment")
.setValue(ValueProto.newBuilder().setInts(
IntList.newBuilder().addValues(1)))
.build());
modelValues.add((ModelProto.ModelValue) ModelProto.ModelValue.newBuilder()
.setIdentifier("target_counter")
.setValue(ValueProto.newBuilder().setInts(
IntList.newBuilder().addValues(3)))
.build());
modelValues.add((ModelProto.ModelValue) ModelProto.ModelValue.newBuilder()
.setIdentifier("condition")
.setValue(ValueProto.newBuilder().setBooleans(
BooleanList.newBuilder().addValues(false)))
.build());
GenericUserInterfaceProto genericUserInterface =
(GenericUserInterfaceProto) GenericUserInterfaceProto.newBuilder()
.setRootView(createTextView("Click me three+ times", "text_view"))
.setInteractions(
InteractionsProto.newBuilder().addAllInteractions(interactions))
.setModel(ModelProto.newBuilder().addAllValues(modelValues))
.build();
ArrayList<ActionProto> list = new ArrayList<>();
list.add((ActionProto) ActionProto.newBuilder()
.setShowGenericUi(ShowGenericUiProto.newBuilder().setGenericUserInterface(
genericUserInterface))
.build());
AutofillAssistantTestScript script = new AutofillAssistantTestScript(
(SupportedScriptProto) SupportedScriptProto.newBuilder()
.setPath("form_target_website.html")
.setPresentation(PresentationProto.newBuilder().setAutostart(true).setChip(
ChipProto.newBuilder().setText("Autostart")))
.build(),
list);
AutofillAssistantTestService testService =
new AutofillAssistantTestService(Collections.singletonList(script));
startAutofillAssistant(mTestRule.getActivity(), testService);
waitUntilViewMatchesCondition(withText("Click me three+ times"), isCompletelyDisplayed());
onView(withText("Click me three+ times")).perform(click());
onView(withText("Info popup title")).check(doesNotExist());
onView(withText("Click me three+ times")).perform(click());
onView(withText("Info popup title")).check(doesNotExist());
onView(withText("Click me three+ times")).perform(click());
onView(withText("Info popup title")).check(matches(isDisplayed()));
}
} }
...@@ -66,6 +66,16 @@ void TryToggleUserAction(base::WeakPtr<BasicInteractions> basic_interactions, ...@@ -66,6 +66,16 @@ void TryToggleUserAction(base::WeakPtr<BasicInteractions> basic_interactions,
basic_interactions->ToggleUserAction(proto); basic_interactions->ToggleUserAction(proto);
} }
void TryRunConditionalCallback(
base::WeakPtr<BasicInteractions> basic_interactions,
const std::string& condition_identifier,
InteractionHandlerAndroid::InteractionCallback callback) {
if (!basic_interactions) {
return;
}
basic_interactions->RunConditionalCallback(condition_identifier, callback);
}
void ShowInfoPopup(const InfoPopupProto& proto, void ShowInfoPopup(const InfoPopupProto& proto,
base::android::ScopedJavaGlobalRef<jobject> jcontext) { base::android::ScopedJavaGlobalRef<jobject> jcontext) {
JNIEnv* env = base::android::AttachCurrentThread(); JNIEnv* env = base::android::AttachCurrentThread();
...@@ -448,6 +458,15 @@ bool InteractionHandlerAndroid::AddInteractionsFromProto( ...@@ -448,6 +458,15 @@ bool InteractionHandlerAndroid::AddInteractionsFromProto(
VLOG(1) << "Invalid callback for interaction"; VLOG(1) << "Invalid callback for interaction";
return false; return false;
} }
// Wrap callback in condition handler if necessary.
if (callback_proto.has_condition_model_identifier()) {
callback =
base::Optional<InteractionHandlerAndroid::InteractionCallback>(
base::BindRepeating(&TryRunConditionalCallback,
basic_interactions->GetWeakPtr(),
callback_proto.condition_model_identifier(),
*callback));
}
AddInteraction(*key, *callback); AddInteraction(*key, *callback);
} }
} }
......
...@@ -385,7 +385,7 @@ bool BasicInteractions::ToggleUserAction(const ToggleUserActionProto& proto) { ...@@ -385,7 +385,7 @@ bool BasicInteractions::ToggleUserAction(const ToggleUserActionProto& proto) {
<< proto.enabled_model_identifier() << " not found in model"; << proto.enabled_model_identifier() << " not found in model";
return false; return false;
} }
if (enabled_value->booleans().values_size() != 1) { if (enabled_value->booleans().values().size() != 1) {
DVLOG(2) << "Error evaluating " << __func__ DVLOG(2) << "Error evaluating " << __func__
<< ": expected enabled_model_identifier to contain a single bool, " << ": expected enabled_model_identifier to contain a single bool, "
"but was " "but was "
...@@ -435,4 +435,26 @@ void BasicInteractions::SetEndActionCallback( ...@@ -435,4 +435,26 @@ void BasicInteractions::SetEndActionCallback(
end_action_callback_ = std::move(end_action_callback); end_action_callback_ = std::move(end_action_callback);
} }
bool BasicInteractions::RunConditionalCallback(
const std::string& condition_identifier,
base::RepeatingCallback<void()> callback) {
auto condition_value =
delegate_->GetUserModel()->GetValue(condition_identifier);
if (!condition_value.has_value()) {
DVLOG(2) << "Error evaluating " << __func__ << ": " << condition_identifier
<< " not found in model";
return false;
}
if (condition_value->booleans().values().size() != 1) {
DVLOG(2) << "Error evaluating " << __func__ << ": expected "
<< condition_identifier << " to contain a single bool, but was "
<< *condition_value;
return false;
}
if (condition_value->booleans().values(0)) {
callback.Run();
}
return true;
}
} // namespace autofill_assistant } // namespace autofill_assistant
...@@ -53,6 +53,12 @@ class BasicInteractions { ...@@ -53,6 +53,12 @@ class BasicInteractions {
// Clears the |end_action_callback_|. // Clears the |end_action_callback_|.
void ClearEndActionCallback(); void ClearEndActionCallback();
// Runs |callback| if |condition_identifier| points to a single boolean set to
// 'true'. Returns true on success (i.e., condition was evaluated
// successfully), false on failure.
bool RunConditionalCallback(const std::string& condition_identifier,
base::RepeatingCallback<void()> callback);
private: private:
ScriptExecutorDelegate* delegate_; ScriptExecutorDelegate* delegate_;
// Only valid during a ShowGenericUiAction. // Only valid during a ShowGenericUiAction.
......
...@@ -15,9 +15,9 @@ namespace autofill_assistant { ...@@ -15,9 +15,9 @@ namespace autofill_assistant {
using ::testing::ElementsAre; using ::testing::ElementsAre;
using ::testing::Eq; using ::testing::Eq;
using ::testing::InSequence;
using ::testing::Property; using ::testing::Property;
using ::testing::StrEq; using ::testing::StrEq;
namespace { namespace {
DateProto CreateDateProto(int year, int month, int day) { DateProto CreateDateProto(int year, int month, int day) {
DateProto proto; DateProto proto;
...@@ -419,4 +419,29 @@ TEST_F(BasicInteractionsTest, ToggleUserAction) { ...@@ -419,4 +419,29 @@ TEST_F(BasicInteractionsTest, ToggleUserAction) {
Property(&UserActionProto::enabled, Eq(false))))); Property(&UserActionProto::enabled, Eq(false)))));
} }
TEST_F(BasicInteractionsTest, RunConditionalCallback) {
InSequence seq;
base::MockCallback<base::RepeatingCallback<void()>> callback;
EXPECT_CALL(callback, Run()).Times(0);
EXPECT_FALSE(
basic_interactions_.RunConditionalCallback("condition", callback.Get()));
ValueProto multi_bool;
multi_bool.mutable_booleans()->add_values(true);
multi_bool.mutable_booleans()->add_values(false);
user_model_.SetValue("condition", multi_bool);
EXPECT_FALSE(
basic_interactions_.RunConditionalCallback("condition", callback.Get()));
user_model_.SetValue("condition", SimpleValue(false));
EXPECT_TRUE(
basic_interactions_.RunConditionalCallback("condition", callback.Get()));
EXPECT_CALL(callback, Run()).Times(1);
user_model_.SetValue("condition", SimpleValue(true));
EXPECT_TRUE(
basic_interactions_.RunConditionalCallback("condition", callback.Get()));
}
} // namespace autofill_assistant } // namespace autofill_assistant
...@@ -38,6 +38,9 @@ message CallbackProto { ...@@ -38,6 +38,9 @@ message CallbackProto {
SetTextProto set_text = 8; SetTextProto set_text = 8;
ToggleUserActionProto toggle_user_action = 9; ToggleUserActionProto toggle_user_action = 9;
} }
// Optional model identifier pointing to a single boolean. If set, the
// callback will only be invoked if the condition is true.
optional string condition_model_identifier = 10;
} }
message EventProto { message EventProto {
......
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