1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/android/autofill_assistant/ui_controller_android.h"
6
7 #include <map>
8 #include <memory>
9 #include <utility>
10
11 #include "base/android/jni_android.h"
12 #include "base/android/jni_array.h"
13 #include "base/android/jni_string.h"
14 #include "base/bind.h"
15 #include "base/command_line.h"
16 #include "base/metrics/field_trial_params.h"
17 #include "base/optional.h"
18 #include "base/task/post_task.h"
19 #include "base/time/time.h"
20 #include "chrome/android/features/autofill_assistant/jni_headers/AssistantCollectUserDataModel_jni.h"
21 #include "chrome/android/features/autofill_assistant/jni_headers/AssistantDetailsModel_jni.h"
22 #include "chrome/android/features/autofill_assistant/jni_headers/AssistantDetails_jni.h"
23 #include "chrome/android/features/autofill_assistant/jni_headers/AssistantFormInput_jni.h"
24 #include "chrome/android/features/autofill_assistant/jni_headers/AssistantFormModel_jni.h"
25 #include "chrome/android/features/autofill_assistant/jni_headers/AssistantGenericUiModel_jni.h"
26 #include "chrome/android/features/autofill_assistant/jni_headers/AssistantInfoBoxModel_jni.h"
27 #include "chrome/android/features/autofill_assistant/jni_headers/AssistantInfoBox_jni.h"
28 #include "chrome/android/features/autofill_assistant/jni_headers/AssistantModel_jni.h"
29 #include "chrome/android/features/autofill_assistant/jni_headers/AssistantOverlayModel_jni.h"
30 #include "chrome/android/features/autofill_assistant/jni_headers/AutofillAssistantUiController_jni.h"
31 #include "chrome/browser/android/autofill_assistant/client_android.h"
32 #include "chrome/browser/android/autofill_assistant/generic_ui_root_controller_android.h"
33 #include "chrome/browser/android/autofill_assistant/ui_controller_android_utils.h"
34 #include "chrome/browser/autofill/android/personal_data_manager_android.h"
35 #include "chrome/browser/autofill/personal_data_manager_factory.h"
36 #include "chrome/browser/flags/android/chrome_feature_list.h"
37 #include "chrome/browser/profiles/profile.h"
38 #include "chrome/browser/profiles/profile_manager.h"
39 #include "chrome/browser/signin/identity_manager_factory.h"
40 #include "chrome/common/channel_info.h"
41 #include "components/autofill/core/browser/data_model/autofill_profile.h"
42 #include "components/autofill/core/browser/data_model/credit_card.h"
43 #include "components/autofill_assistant/browser/bottom_sheet_state.h"
44 #include "components/autofill_assistant/browser/client_settings.h"
45 #include "components/autofill_assistant/browser/controller.h"
46 #include "components/autofill_assistant/browser/event_handler.h"
47 #include "components/autofill_assistant/browser/features.h"
48 #include "components/autofill_assistant/browser/metrics.h"
49 #include "components/autofill_assistant/browser/rectf.h"
50 #include "components/autofill_assistant/browser/user_data.h"
51 #include "components/autofill_assistant/browser/user_data_util.h"
52 #include "components/autofill_assistant/browser/user_model.h"
53 #include "components/signin/public/identity_manager/account_info.h"
54 #include "components/signin/public/identity_manager/identity_manager.h"
55 #include "components/strings/grit/components_strings.h"
56 #include "components/version_info/channel.h"
57 #include "content/public/browser/browser_task_traits.h"
58 #include "content/public/browser/browser_thread.h"
59 #include "content/public/browser/web_contents.h"
60 #include "google_apis/google_api_keys.h"
61 #include "ui/base/l10n/l10n_util.h"
62
63 using base::android::AttachCurrentThread;
64 using base::android::JavaParamRef;
65 using base::android::JavaRef;
66
67 namespace autofill_assistant {
68
69 namespace {
ToFloatVector(const std::vector<RectF> & areas)70 std::vector<float> ToFloatVector(const std::vector<RectF>& areas) {
71 std::vector<float> flattened;
72 for (const auto& rect : areas) {
73 flattened.emplace_back(rect.left);
74 flattened.emplace_back(rect.top);
75 flattened.emplace_back(rect.right);
76 flattened.emplace_back(rect.bottom);
77 }
78 return flattened;
79 }
80
CreateJavaDateTime(JNIEnv * env,const DateTimeProto & proto)81 base::android::ScopedJavaLocalRef<jobject> CreateJavaDateTime(
82 JNIEnv* env,
83 const DateTimeProto& proto) {
84 return Java_AssistantCollectUserDataModel_createAssistantDateTime(
85 env, (int)proto.date().year(), proto.date().month(), proto.date().day(),
86 proto.time().hour(), proto.time().minute(), proto.time().second());
87 }
88
CreateJavaDate(JNIEnv * env,const DateProto & proto)89 base::android::ScopedJavaLocalRef<jobject> CreateJavaDate(
90 JNIEnv* env,
91 const DateProto& proto) {
92 DateTimeProto date_time;
93 *date_time.mutable_date() = proto;
94 return CreateJavaDateTime(env, date_time);
95 }
96
97 // Creates the Java equivalent to |login_choices|.
CreateJavaLoginChoiceList(JNIEnv * env,const std::vector<LoginChoice> & login_choices)98 base::android::ScopedJavaLocalRef<jobject> CreateJavaLoginChoiceList(
99 JNIEnv* env,
100 const std::vector<LoginChoice>& login_choices) {
101 auto jlist = Java_AssistantCollectUserDataModel_createLoginChoiceList(env);
102 for (const auto& login_choice : login_choices) {
103 base::android::ScopedJavaLocalRef<jobject> jinfo_popup = nullptr;
104 if (login_choice.info_popup.has_value()) {
105 jinfo_popup = ui_controller_android_utils::CreateJavaInfoPopup(
106 env, *login_choice.info_popup);
107 }
108 base::android::ScopedJavaLocalRef<jstring> jsublabel_accessibility_hint =
109 nullptr;
110 if (login_choice.sublabel_accessibility_hint.has_value()) {
111 jsublabel_accessibility_hint = base::android::ConvertUTF8ToJavaString(
112 env, login_choice.sublabel_accessibility_hint.value());
113 }
114 Java_AssistantCollectUserDataModel_addLoginChoice(
115 env, jlist,
116 base::android::ConvertUTF8ToJavaString(env, login_choice.identifier),
117 base::android::ConvertUTF8ToJavaString(env, login_choice.label),
118 base::android::ConvertUTF8ToJavaString(env, login_choice.sublabel),
119 jsublabel_accessibility_hint, login_choice.preselect_priority,
120 jinfo_popup);
121 }
122 return jlist;
123 }
124
125 // Creates the java equivalent to the text inputs specified in |section|.
CreateJavaTextInputsForSection(JNIEnv * env,const TextInputSectionProto & section)126 base::android::ScopedJavaLocalRef<jobject> CreateJavaTextInputsForSection(
127 JNIEnv* env,
128 const TextInputSectionProto& section) {
129 auto jinput_list =
130 Java_AssistantCollectUserDataModel_createTextInputList(env);
131 for (const auto& input : section.input_fields()) {
132 TextInputType type;
133 switch (input.input_type()) {
134 case TextInputProto::INPUT_TEXT:
135 type = TextInputType::INPUT_TEXT;
136 break;
137 case TextInputProto::INPUT_ALPHANUMERIC:
138 type = TextInputType::INPUT_ALPHANUMERIC;
139 break;
140 case TextInputProto::UNDEFINED:
141 NOTREACHED();
142 continue;
143 }
144 Java_AssistantCollectUserDataModel_appendTextInput(
145 env, jinput_list, type,
146 base::android::ConvertUTF8ToJavaString(env, input.hint()),
147 base::android::ConvertUTF8ToJavaString(env, input.value()),
148 base::android::ConvertUTF8ToJavaString(env, input.client_memory_key()));
149 }
150 return jinput_list;
151 }
152
153 // Creates the java equivalent to |sections|.
CreateJavaAdditionalSections(JNIEnv * env,const std::vector<UserFormSectionProto> & sections)154 base::android::ScopedJavaLocalRef<jobject> CreateJavaAdditionalSections(
155 JNIEnv* env,
156 const std::vector<UserFormSectionProto>& sections) {
157 auto jsection_list =
158 Java_AssistantCollectUserDataModel_createAdditionalSectionsList(env);
159 for (const auto& section : sections) {
160 switch (section.section_case()) {
161 case UserFormSectionProto::kStaticTextSection:
162 Java_AssistantCollectUserDataModel_appendStaticTextSection(
163 env, jsection_list,
164 base::android::ConvertUTF8ToJavaString(env, section.title()),
165 base::android::ConvertUTF8ToJavaString(
166 env, section.static_text_section().text()));
167 break;
168 case UserFormSectionProto::kTextInputSection: {
169 Java_AssistantCollectUserDataModel_appendTextInputSection(
170 env, jsection_list,
171 base::android::ConvertUTF8ToJavaString(env, section.title()),
172 CreateJavaTextInputsForSection(env, section.text_input_section()));
173 break;
174 }
175 case UserFormSectionProto::kPopupListSection: {
176 std::vector<std::string> items;
177 std::copy(section.popup_list_section().item_names().begin(),
178 section.popup_list_section().item_names().end(),
179 std::back_inserter(items));
180 std::vector<int> initial_selections;
181 std::copy(section.popup_list_section().initial_selection().begin(),
182 section.popup_list_section().initial_selection().end(),
183 std::back_inserter(initial_selections));
184 Java_AssistantCollectUserDataModel_appendPopupListSection(
185 env, jsection_list,
186 base::android::ConvertUTF8ToJavaString(env, section.title()),
187 base::android::ConvertUTF8ToJavaString(
188 env, section.popup_list_section().additional_value_key()),
189 base::android::ToJavaArrayOfStrings(env, items),
190 base::android::ToJavaIntArray(env, initial_selections),
191 section.popup_list_section().allow_multiselect(),
192 section.popup_list_section().selection_mandatory(),
193 base::android::ConvertUTF8ToJavaString(
194 env,
195 section.popup_list_section().no_selection_error_message()));
196 break;
197 }
198 case UserFormSectionProto::SECTION_NOT_SET:
199 NOTREACHED();
200 break;
201 }
202 }
203 return jsection_list;
204 }
205
GetPreviousFormCounterResult(const FormProto::Result * result,int input_index,int counter_index)206 base::Optional<int> GetPreviousFormCounterResult(
207 const FormProto::Result* result,
208 int input_index,
209 int counter_index) {
210 if (result == nullptr) {
211 return base::nullopt;
212 }
213
214 if (input_index >= result->input_results().size()) {
215 return base::nullopt;
216 }
217 auto input_result = result->input_results(input_index);
218
219 if (counter_index >= input_result.counter().values().size()) {
220 return base::nullopt;
221 }
222 return input_result.counter().values(counter_index);
223 }
224
GetPreviousFormSelectionResult(const FormProto::Result * result,int input_index,int selection_index)225 base::Optional<bool> GetPreviousFormSelectionResult(
226 const FormProto::Result* result,
227 int input_index,
228 int selection_index) {
229 if (result == nullptr) {
230 return base::nullopt;
231 }
232
233 if (input_index >= result->input_results().size()) {
234 return base::nullopt;
235 }
236 auto input_result = result->input_results(input_index);
237
238 if (selection_index >= input_result.selection().selected().size()) {
239 return base::nullopt;
240 }
241 return input_result.selection().selected(selection_index);
242 }
243
ShouldAllowSoftKeyboardForState(AutofillAssistantState state)244 bool ShouldAllowSoftKeyboardForState(AutofillAssistantState state) {
245 switch (state) {
246 case AutofillAssistantState::STARTING:
247 case AutofillAssistantState::RUNNING:
248 return false;
249
250 case AutofillAssistantState::AUTOSTART_FALLBACK_PROMPT:
251 case AutofillAssistantState::PROMPT:
252 case AutofillAssistantState::BROWSE:
253 case AutofillAssistantState::MODAL_DIALOG:
254 case AutofillAssistantState::STOPPED:
255 case AutofillAssistantState::TRACKING:
256 case AutofillAssistantState::INACTIVE:
257 return true;
258 }
259 }
260
261 } // namespace
262
263 // static
CreateFromWebContents(content::WebContents * web_contents,const base::android::JavaParamRef<jobject> & joverlay_coordinator)264 std::unique_ptr<UiControllerAndroid> UiControllerAndroid::CreateFromWebContents(
265 content::WebContents* web_contents,
266 const base::android::JavaParamRef<jobject>& joverlay_coordinator) {
267 JNIEnv* env = AttachCurrentThread();
268 auto jactivity = Java_AutofillAssistantUiController_findAppropriateActivity(
269 env, web_contents->GetJavaWebContents());
270 if (!jactivity) {
271 return nullptr;
272 }
273 return std::make_unique<UiControllerAndroid>(env, jactivity,
274 joverlay_coordinator);
275 }
276
UiControllerAndroid(JNIEnv * env,const base::android::JavaRef<jobject> & jactivity,const base::android::JavaParamRef<jobject> & joverlay_coordinator)277 UiControllerAndroid::UiControllerAndroid(
278 JNIEnv* env,
279 const base::android::JavaRef<jobject>& jactivity,
280 const base::android::JavaParamRef<jobject>& joverlay_coordinator)
281 : overlay_delegate_(this),
282 header_delegate_(this),
283 collect_user_data_delegate_(this),
284 form_delegate_(this),
285 generic_ui_delegate_(this),
286 bottom_bar_delegate_(this) {
287 java_object_ = Java_AutofillAssistantUiController_create(
288 env, jactivity,
289 /* allowTabSwitching= */
290 base::FeatureList::IsEnabled(features::kAutofillAssistantChromeEntry),
291 reinterpret_cast<intptr_t>(this), joverlay_coordinator);
292 header_model_ = std::make_unique<AssistantHeaderModel>(
293 Java_AssistantModel_getHeaderModel(env, GetModel()));
294
295 // Register overlay_delegate_ as delegate for the overlay.
296 Java_AssistantOverlayModel_setDelegate(env, GetOverlayModel(),
297 overlay_delegate_.GetJavaObject());
298
299 // Register header_delegate_ as delegate for clicks on header buttons.
300 header_model_->SetDelegate(header_delegate_);
301
302 // Register collect_user_data_delegate_ as delegate for the collect user data
303 // UI.
304 Java_AssistantCollectUserDataModel_setDelegate(
305 env, GetCollectUserDataModel(),
306 collect_user_data_delegate_.GetJavaObject());
307 Java_AssistantModel_setBottomBarDelegate(
308 env, GetModel(), bottom_bar_delegate_.GetJavaObject());
309 }
310
Attach(content::WebContents * web_contents,ClientAndroid * client,UiDelegate * ui_delegate)311 void UiControllerAndroid::Attach(content::WebContents* web_contents,
312 ClientAndroid* client,
313 UiDelegate* ui_delegate) {
314 DCHECK(web_contents);
315 DCHECK(client);
316 DCHECK(ui_delegate);
317
318 client_ = client;
319
320 // Detach from the current ui_delegate, if one was set previously.
321 Detach();
322
323 // Attach to the new ui_delegate.
324 ui_delegate_ = ui_delegate;
325 ui_delegate_->AddObserver(this);
326
327 JNIEnv* env = AttachCurrentThread();
328 auto java_web_contents = web_contents->GetJavaWebContents();
329 Java_AutofillAssistantUiController_setWebContents(env, java_object_,
330 java_web_contents);
331 Java_AssistantCollectUserDataModel_setWebContents(
332 env, GetCollectUserDataModel(), java_web_contents);
333 OnClientSettingsChanged(ui_delegate_->GetClientSettings());
334 Java_AssistantModel_setPeekModeDisabled(env, GetModel(),
335 ui_delegate->IsRunningLiteScript());
336
337 if (ui_delegate->GetState() != AutofillAssistantState::INACTIVE &&
338 ui_delegate->IsTabSelected()) {
339 // The UI was created for an existing Controller.
340 RestoreUi();
341 } else if (ui_delegate->GetState() == AutofillAssistantState::INACTIVE) {
342 SetVisible(true);
343 }
344 // The call to set the web contents will, for some edge cases, trigger a call
345 // from the Java side to the onTabSelected method.
346 // We want this to happen only after the AttachUI method was fully executed,
347 // as it would otherwise find that IsTabSelected() is true when deciding if
348 // restoring the UI.
349 Java_AssistantModel_setWebContents(env, GetModel(), java_web_contents);
350 }
351
Detach()352 void UiControllerAndroid::Detach() {
353 if (ui_delegate_) {
354 ui_delegate_->RemoveObserver(this);
355 }
356 ui_delegate_ = nullptr;
357 }
358
~UiControllerAndroid()359 UiControllerAndroid::~UiControllerAndroid() {
360 Java_AutofillAssistantUiController_clearNativePtr(AttachCurrentThread(),
361 java_object_);
362 if (ui_delegate_) {
363 ui_delegate_->SetUiShown(false);
364 }
365 Detach();
366 }
367
GetModel()368 base::android::ScopedJavaLocalRef<jobject> UiControllerAndroid::GetModel() {
369 return Java_AutofillAssistantUiController_getModel(AttachCurrentThread(),
370 java_object_);
371 }
372
OnStateChanged(AutofillAssistantState new_state)373 void UiControllerAndroid::OnStateChanged(AutofillAssistantState new_state) {
374 if (!Java_AssistantModel_getVisible(AttachCurrentThread(), GetModel())) {
375 // Leave the UI alone as long as it's invisible. Missed state changes will
376 // be recovered by SetVisible(true).
377 return;
378 }
379 SetupForState();
380 }
381
SetupForState()382 void UiControllerAndroid::SetupForState() {
383 DCHECK(ui_delegate_ != nullptr);
384
385 UpdateActions(ui_delegate_->GetUserActions());
386 AutofillAssistantState state = ui_delegate_->GetState();
387 AllowShowingSoftKeyboard(ShouldAllowSoftKeyboardForState(state));
388 bool should_prompt_action_expand_sheet =
389 ui_delegate_->ShouldPromptActionExpandSheet();
390 switch (state) {
391 case AutofillAssistantState::STARTING:
392 SetOverlayState(OverlayState::FULL);
393 SetSpinPoodle(true);
394 return;
395
396 case AutofillAssistantState::RUNNING:
397 SetOverlayState(OverlayState::FULL);
398 SetSpinPoodle(true);
399 return;
400
401 case AutofillAssistantState::AUTOSTART_FALLBACK_PROMPT:
402 SetOverlayState(OverlayState::HIDDEN);
403 SetSpinPoodle(false);
404
405 if (should_prompt_action_expand_sheet && ui_delegate_->IsTabSelected())
406 ShowContentAndExpandBottomSheet();
407 return;
408
409 case AutofillAssistantState::PROMPT:
410 SetOverlayState(OverlayState::PARTIAL);
411 SetSpinPoodle(false);
412
413 if (should_prompt_action_expand_sheet && ui_delegate_->IsTabSelected())
414 ShowContentAndExpandBottomSheet();
415 return;
416
417 case AutofillAssistantState::BROWSE:
418 SetOverlayState(OverlayState::HIDDEN);
419 SetSpinPoodle(false);
420 return;
421
422 case AutofillAssistantState::MODAL_DIALOG:
423 SetOverlayState(OverlayState::FULL);
424 SetSpinPoodle(true);
425 return;
426
427 case AutofillAssistantState::STOPPED:
428 SetOverlayState(OverlayState::HIDDEN);
429 SetSpinPoodle(false);
430
431 // Make sure the user sees the error message.
432 if (ui_delegate_->IsTabSelected())
433 ShowContentAndExpandBottomSheet();
434 ResetGenericUiControllers();
435 return;
436
437 case AutofillAssistantState::TRACKING:
438 SetOverlayState(OverlayState::HIDDEN);
439 SetSpinPoodle(false);
440
441 Java_AssistantModel_setVisible(AttachCurrentThread(), GetModel(), false);
442 DestroySelf();
443 return;
444
445 case AutofillAssistantState::INACTIVE:
446 // Wait for the state to change.
447 return;
448 }
449 NOTREACHED() << "Unknown state: " << static_cast<int>(state);
450 }
451
OnStatusMessageChanged(const std::string & message)452 void UiControllerAndroid::OnStatusMessageChanged(const std::string& message) {
453 header_model_->SetStatusMessage(message);
454 }
455
OnBubbleMessageChanged(const std::string & message)456 void UiControllerAndroid::OnBubbleMessageChanged(const std::string& message) {
457 if (!message.empty()) {
458 header_model_->SetBubbleMessage(message);
459 }
460 }
461
OnProgressChanged(int progress)462 void UiControllerAndroid::OnProgressChanged(int progress) {
463 header_model_->SetProgress(progress);
464 }
465
OnProgressActiveStepChanged(int active_step)466 void UiControllerAndroid::OnProgressActiveStepChanged(int active_step) {
467 header_model_->SetProgressActiveStep(active_step);
468 }
469
OnProgressVisibilityChanged(bool visible)470 void UiControllerAndroid::OnProgressVisibilityChanged(bool visible) {
471 header_model_->SetProgressVisible(visible);
472 }
473
OnProgressBarErrorStateChanged(bool error)474 void UiControllerAndroid::OnProgressBarErrorStateChanged(bool error) {
475 header_model_->SetProgressBarErrorState(error);
476 }
477
OnStepProgressBarConfigurationChanged(const ShowProgressBarProto::StepProgressBarConfiguration & configuration)478 void UiControllerAndroid::OnStepProgressBarConfigurationChanged(
479 const ShowProgressBarProto::StepProgressBarConfiguration& configuration) {
480 header_model_->SetStepProgressBarConfiguration(
481 configuration, Java_AutofillAssistantUiController_getContext(
482 AttachCurrentThread(), java_object_));
483 }
484
OnViewportModeChanged(ViewportMode mode)485 void UiControllerAndroid::OnViewportModeChanged(ViewportMode mode) {
486 Java_AutofillAssistantUiController_setViewportMode(AttachCurrentThread(),
487 java_object_, mode);
488 }
489
OnPeekModeChanged(ConfigureBottomSheetProto::PeekMode peek_mode)490 void UiControllerAndroid::OnPeekModeChanged(
491 ConfigureBottomSheetProto::PeekMode peek_mode) {
492 Java_AutofillAssistantUiController_setPeekMode(AttachCurrentThread(),
493 java_object_, peek_mode);
494 }
495
OnExpandBottomSheet()496 void UiControllerAndroid::OnExpandBottomSheet() {
497 Java_AutofillAssistantUiController_expandBottomSheet(AttachCurrentThread(),
498 java_object_);
499 }
500
OnCollapseBottomSheet()501 void UiControllerAndroid::OnCollapseBottomSheet() {
502 Java_AutofillAssistantUiController_collapseBottomSheet(AttachCurrentThread(),
503 java_object_);
504 }
505
OnOverlayColorsChanged(const UiDelegate::OverlayColors & colors)506 void UiControllerAndroid::OnOverlayColorsChanged(
507 const UiDelegate::OverlayColors& colors) {
508 JNIEnv* env = AttachCurrentThread();
509 auto overlay_model = GetOverlayModel();
510 Java_AssistantOverlayModel_setBackgroundColor(
511 env, overlay_model,
512 ui_controller_android_utils::GetJavaColor(env, colors.background));
513 Java_AssistantOverlayModel_setHighlightBorderColor(
514 env, overlay_model,
515 ui_controller_android_utils::GetJavaColor(env, colors.highlight_border));
516 }
517
AllowShowingSoftKeyboard(bool enabled)518 void UiControllerAndroid::AllowShowingSoftKeyboard(bool enabled) {
519 Java_AssistantModel_setAllowSoftKeyboard(AttachCurrentThread(), GetModel(),
520 enabled);
521 }
522
ShowContentAndExpandBottomSheet()523 void UiControllerAndroid::ShowContentAndExpandBottomSheet() {
524 Java_AutofillAssistantUiController_showContentAndExpandBottomSheet(
525 AttachCurrentThread(), java_object_);
526 }
527
SetSpinPoodle(bool enabled)528 void UiControllerAndroid::SetSpinPoodle(bool enabled) {
529 header_model_->SetSpinPoodle(enabled);
530 }
531
OnFeedbackButtonClicked()532 void UiControllerAndroid::OnFeedbackButtonClicked() {
533 JNIEnv* env = AttachCurrentThread();
534 Java_AutofillAssistantUiController_showFeedback(
535 env, java_object_,
536 base::android::ConvertUTF8ToJavaString(env,
537 ui_delegate_->GetDebugContext()));
538 }
539
OnFeedbackFormRequested()540 void UiControllerAndroid::OnFeedbackFormRequested() {
541 OnFeedbackButtonClicked();
542 }
543
OnViewEvent(const EventHandler::EventKey & key)544 void UiControllerAndroid::OnViewEvent(const EventHandler::EventKey& key) {
545 ui_delegate_->DispatchEvent(key);
546 }
547
OnValueChanged(const std::string & identifier,const ValueProto & value)548 void UiControllerAndroid::OnValueChanged(const std::string& identifier,
549 const ValueProto& value) {
550 ui_delegate_->GetUserModel()->SetValue(identifier, value);
551 }
552
Shutdown(Metrics::DropOutReason reason)553 void UiControllerAndroid::Shutdown(Metrics::DropOutReason reason) {
554 client_->Shutdown(reason);
555 }
556
ShowSnackbar(base::TimeDelta delay,const std::string & message,base::OnceCallback<void ()> action)557 void UiControllerAndroid::ShowSnackbar(base::TimeDelta delay,
558 const std::string& message,
559 base::OnceCallback<void()> action) {
560 if (delay.is_zero()) {
561 std::move(action).Run();
562 return;
563 }
564
565 JNIEnv* env = AttachCurrentThread();
566 auto jmodel = GetModel();
567 if (!Java_AssistantModel_getVisible(env, jmodel)) {
568 // If the UI is not visible, execute the action immediately.
569 std::move(action).Run();
570 return;
571 }
572 SetVisible(false);
573 snackbar_action_ = std::move(action);
574 Java_AutofillAssistantUiController_showSnackbar(
575 env, java_object_, static_cast<jint>(delay.InMilliseconds()),
576 base::android::ConvertUTF8ToJavaString(env, message));
577 }
578
SnackbarResult(JNIEnv * env,const base::android::JavaParamRef<jobject> & obj,jboolean undo)579 void UiControllerAndroid::SnackbarResult(
580 JNIEnv* env,
581 const base::android::JavaParamRef<jobject>& obj,
582 jboolean undo) {
583 base::OnceCallback<void()> action = std::move(snackbar_action_);
584 if (!action) {
585 NOTREACHED();
586 return;
587 }
588 if (undo) {
589 SetVisible(true);
590 return;
591 }
592 std::move(action).Run();
593 }
594
DestroySelf()595 void UiControllerAndroid::DestroySelf() {
596 // Note: shutdown happens asynchronously (if at all), so calling code after
597 // |ShutdownIfNecessary| returns should be ok.
598 if (ui_delegate_)
599 ui_delegate_->ShutdownIfNecessary();
600
601 // Destroy self in separate task to avoid UaFs. Obviously, having this method
602 // in the first place is a terrible idea. We should refactor. The controller
603 // should always be in control. ui_controller should always hold a reference
604 // to ui_delegate, even when detached. ui_controller should not have a
605 // reference for client_android at all. It introduces a circular dependency,
606 // among other things.
607 //
608 // TODO(mcarlen): refactor lifecycle and deps of ui_controller.
609 content::GetUIThreadTaskRunner({})->PostTask(
610 FROM_HERE,
611 base::BindOnce(&ClientAndroid::DestroyUI, client_->GetWeakPtr()));
612 }
613
SetVisible(JNIEnv * env,const base::android::JavaParamRef<jobject> & jcaller,jboolean visible)614 void UiControllerAndroid::SetVisible(
615 JNIEnv* env,
616 const base::android::JavaParamRef<jobject>& jcaller,
617 jboolean visible) {
618 SetVisible(visible);
619 }
620
SetVisible(bool visible)621 void UiControllerAndroid::SetVisible(bool visible) {
622 Java_AssistantModel_setVisible(AttachCurrentThread(), GetModel(), visible);
623 if (visible) {
624 // Recover possibly state changes missed by OnStateChanged()
625 SetupForState();
626 } else {
627 SetOverlayState(OverlayState::HIDDEN);
628 }
629 ui_delegate_->SetUiShown(visible);
630 }
631
RestoreUi()632 void UiControllerAndroid::RestoreUi() {
633 if (ui_delegate_ == nullptr)
634 return;
635
636 OnStatusMessageChanged(ui_delegate_->GetStatusMessage());
637 OnBubbleMessageChanged(ui_delegate_->GetBubbleMessage());
638 auto step_progress_bar_configuration =
639 ui_delegate_->GetStepProgressBarConfiguration();
640 if (step_progress_bar_configuration.has_value()) {
641 OnStepProgressBarConfigurationChanged(*step_progress_bar_configuration);
642 if (step_progress_bar_configuration->use_step_progress_bar()) {
643 auto active_step = ui_delegate_->GetProgressActiveStep();
644 if (active_step.has_value()) {
645 OnProgressActiveStepChanged(*active_step);
646 }
647 OnProgressBarErrorStateChanged(ui_delegate_->GetProgressBarErrorState());
648 }
649 } else {
650 OnStepProgressBarConfigurationChanged(
651 ShowProgressBarProto::StepProgressBarConfiguration());
652 OnProgressChanged(ui_delegate_->GetProgress());
653 }
654 OnProgressVisibilityChanged(ui_delegate_->GetProgressVisible());
655 OnInfoBoxChanged(ui_delegate_->GetInfoBox());
656 OnDetailsChanged(ui_delegate_->GetDetails());
657 OnUserActionsChanged(ui_delegate_->GetUserActions());
658 OnCollectUserDataOptionsChanged(ui_delegate_->GetCollectUserDataOptions());
659 OnUserDataChanged(ui_delegate_->GetUserData(), UserData::FieldChange::ALL);
660 OnGenericUserInterfaceChanged(ui_delegate_->GetGenericUiProto());
661
662 std::vector<RectF> area;
663 ui_delegate_->GetTouchableArea(&area);
664 std::vector<RectF> restricted_area;
665 ui_delegate_->GetRestrictedArea(&restricted_area);
666 RectF visual_viewport;
667 ui_delegate_->GetVisualViewport(&visual_viewport);
668 OnTouchableAreaChanged(visual_viewport, area, restricted_area);
669 OnViewportModeChanged(ui_delegate_->GetViewportMode());
670 OnPeekModeChanged(ui_delegate_->GetPeekMode());
671 OnFormChanged(ui_delegate_->GetForm(), ui_delegate_->GetFormResult());
672 UiDelegate::OverlayColors colors;
673 ui_delegate_->GetOverlayColors(&colors);
674 OnOverlayColorsChanged(colors);
675 SetVisible(true);
676 Java_AutofillAssistantUiController_restoreBottomSheetState(
677 AttachCurrentThread(), java_object_,
678 ui_controller_android_utils::ToJavaBottomSheetState(
679 ui_delegate_->GetBottomSheetState()));
680 }
681
OnTabSwitched(JNIEnv * env,const base::android::JavaParamRef<jobject> & jcaller,jint state,jboolean activity_changed)682 void UiControllerAndroid::OnTabSwitched(
683 JNIEnv* env,
684 const base::android::JavaParamRef<jobject>& jcaller,
685 jint state,
686 jboolean activity_changed) {
687 if (ui_delegate_ == nullptr) {
688 return;
689 }
690
691 // TODO(b/167947210) Allow lite scripts to transition from CCT to regular
692 // scripts.
693 if (activity_changed && ui_delegate_->IsRunningLiteScript()) {
694 Shutdown(Metrics::DropOutReason::CUSTOM_TAB_CLOSED);
695 return;
696 }
697
698 ui_delegate_->SetBottomSheetState(
699 ui_controller_android_utils::ToNativeBottomSheetState(state));
700 ui_delegate_->SetTabSelected(false);
701 }
702
OnTabSelected(JNIEnv * env,const base::android::JavaParamRef<jobject> & jcaller)703 void UiControllerAndroid::OnTabSelected(
704 JNIEnv* env,
705 const base::android::JavaParamRef<jobject>& jcaller) {
706 if (ui_delegate_ == nullptr) {
707 return;
708 }
709 if (!ui_delegate_->IsTabSelected()) {
710 RestoreUi();
711 ui_delegate_->SetTabSelected(true);
712 }
713 }
714
715 // Actions carousels related methods.
716
UpdateActions(const std::vector<UserAction> & user_actions)717 void UiControllerAndroid::UpdateActions(
718 const std::vector<UserAction>& user_actions) {
719 DCHECK(ui_delegate_);
720
721 JNIEnv* env = AttachCurrentThread();
722
723 bool has_close_or_cancel = false;
724 auto jchips = Java_AutofillAssistantUiController_createChipList(env);
725 auto jsticky_chips = Java_AutofillAssistantUiController_createChipList(env);
726 int user_action_count = static_cast<int>(user_actions.size());
727 for (int i = 0; i < user_action_count; i++) {
728 const auto& action = user_actions[i];
729 const Chip& chip = action.chip();
730 base::android::ScopedJavaLocalRef<jobject> jchip;
731 // TODO(arbesser): Refactor this to use
732 // ui_controller_android_utils::CreateJavaAssistantChip.
733 switch (chip.type) {
734 default: // Ignore actions with other chip types or with no chips.
735 break;
736
737 case HIGHLIGHTED_ACTION:
738 jchip =
739 Java_AutofillAssistantUiController_createHighlightedActionButton(
740 env, java_object_, chip.icon,
741 base::android::ConvertUTF8ToJavaString(env, chip.text), i,
742 !action.enabled(), chip.sticky, chip.visible);
743 break;
744
745 case NORMAL_ACTION:
746 jchip = Java_AutofillAssistantUiController_createActionButton(
747 env, java_object_, chip.icon,
748 base::android::ConvertUTF8ToJavaString(env, chip.text), i,
749 !action.enabled(), chip.sticky, chip.visible);
750 break;
751
752 case FEEDBACK_ACTION:
753 // A "Send feedback" button which will show the feedback form before
754 // executing the action.
755 jchip = Java_AutofillAssistantUiController_createFeedbackButton(
756 env, java_object_, chip.icon,
757 base::android::ConvertUTF8ToJavaString(env, chip.text), i,
758 !action.enabled(), chip.sticky, chip.visible);
759 break;
760
761 case CANCEL_ACTION:
762 // A Cancel button sneaks in an UNDO snackbar before executing the
763 // action, while a close button behaves like a normal button.
764 jchip = Java_AutofillAssistantUiController_createCancelButton(
765 env, java_object_, chip.icon,
766 base::android::ConvertUTF8ToJavaString(env, chip.text), i,
767 !action.enabled(), chip.sticky, chip.visible);
768 has_close_or_cancel = true;
769 break;
770
771 case CLOSE_ACTION:
772 jchip = Java_AutofillAssistantUiController_createActionButton(
773 env, java_object_, chip.icon,
774 base::android::ConvertUTF8ToJavaString(env, chip.text), i,
775 !action.enabled(), chip.sticky, chip.visible);
776 has_close_or_cancel = true;
777 break;
778
779 case DONE_ACTION:
780 jchip =
781 Java_AutofillAssistantUiController_createHighlightedActionButton(
782 env, java_object_, chip.icon,
783 base::android::ConvertUTF8ToJavaString(env, chip.text), i,
784 !action.enabled(), chip.sticky, chip.visible);
785 has_close_or_cancel = true;
786 break;
787 }
788 if (jchip) {
789 Java_AutofillAssistantUiController_appendChipToList(env, jchips, jchip);
790 if (chip.sticky) {
791 Java_AutofillAssistantUiController_appendChipToList(env, jsticky_chips,
792 jchip);
793 }
794 }
795 }
796
797 if (!has_close_or_cancel) {
798 base::android::ScopedJavaLocalRef<jobject> jcancel_chip;
799 if (ui_delegate_->GetState() == AutofillAssistantState::STOPPED) {
800 jcancel_chip = Java_AutofillAssistantUiController_createCloseButton(
801 env, java_object_, ICON_CLEAR,
802 base::android::ConvertUTF8ToJavaString(env, ""),
803 /* disabled= */ false, /* sticky= */ true, /* visible=*/true);
804 } else if (ui_delegate_->GetState() != AutofillAssistantState::INACTIVE) {
805 jcancel_chip = Java_AutofillAssistantUiController_createCancelButton(
806 env, java_object_, ICON_CLEAR,
807 base::android::ConvertUTF8ToJavaString(env, ""), -1,
808 /* disabled= */ false, /* sticky= */ true, /* visible=*/true);
809 }
810 if (jcancel_chip) {
811 Java_AutofillAssistantUiController_appendChipToList(env, jchips,
812 jcancel_chip);
813 Java_AutofillAssistantUiController_appendChipToList(env, jsticky_chips,
814 jcancel_chip);
815 }
816 }
817
818 Java_AutofillAssistantUiController_setActions(env, java_object_, jchips);
819 header_model_->SetChips(jsticky_chips);
820 }
821
OnUserActionsChanged(const std::vector<UserAction> & actions)822 void UiControllerAndroid::OnUserActionsChanged(
823 const std::vector<UserAction>& actions) {
824 UpdateActions(actions);
825 }
826
OnUserActionSelected(JNIEnv * env,const base::android::JavaParamRef<jobject> & jcaller,jint index)827 void UiControllerAndroid::OnUserActionSelected(
828 JNIEnv* env,
829 const base::android::JavaParamRef<jobject>& jcaller,
830 jint index) {
831 if (ui_delegate_)
832 ui_delegate_->PerformUserAction(index);
833 }
834
OnCancelButtonClicked(JNIEnv * env,const base::android::JavaParamRef<jobject> & jcaller,jint index)835 void UiControllerAndroid::OnCancelButtonClicked(
836 JNIEnv* env,
837 const base::android::JavaParamRef<jobject>& jcaller,
838 jint index) {
839 // If the keyboard is currently shown, clicking the cancel button should
840 // hide the keyboard rather than close autofill assistant, because the cancel
841 // chip will be displayed right above the keyboard.
842 if (Java_AutofillAssistantUiController_isKeyboardShown(env, java_object_)) {
843 Java_AutofillAssistantUiController_hideKeyboard(env, java_object_);
844 return;
845 }
846
847 CloseOrCancel(index, TriggerContext::CreateEmpty(),
848 Metrics::DropOutReason::SHEET_CLOSED);
849 }
850
OnCloseButtonClicked(JNIEnv * env,const base::android::JavaParamRef<jobject> & jcaller)851 void UiControllerAndroid::OnCloseButtonClicked(
852 JNIEnv* env,
853 const base::android::JavaParamRef<jobject>& jcaller) {
854 DestroySelf();
855 }
856
OnFeedbackButtonClicked(JNIEnv * env,const base::android::JavaParamRef<jobject> & jcaller,jint index)857 void UiControllerAndroid::OnFeedbackButtonClicked(
858 JNIEnv* env,
859 const base::android::JavaParamRef<jobject>& jcaller,
860 jint index) {
861 // Show the feedback form then directly run the associated action.
862 // Unfortunately there is no way to associate a callback to run after the user
863 // actually sent (or close) the form, so we have to continue directly after
864 // showing it. It should be good enough, given that in most use cases we will
865 // directly stop.
866 Java_AutofillAssistantUiController_showFeedback(
867 env, java_object_,
868 base::android::ConvertUTF8ToJavaString(env,
869 ui_delegate_->GetDebugContext()));
870
871 OnUserActionSelected(env, jcaller, index);
872 }
873
OnKeyboardVisibilityChanged(JNIEnv * env,const base::android::JavaParamRef<jobject> & jcaller,jboolean visible)874 void UiControllerAndroid::OnKeyboardVisibilityChanged(
875 JNIEnv* env,
876 const base::android::JavaParamRef<jobject>& jcaller,
877 jboolean visible) {
878 if (ui_delegate_)
879 ui_delegate_->OnKeyboardVisibilityChanged(visible);
880 }
881
OnBackButtonClicked()882 bool UiControllerAndroid::OnBackButtonClicked() {
883 // If the keyboard is currently shown, clicking the back button should
884 // hide the keyboard rather than close autofill assistant.
885 if (Java_AutofillAssistantUiController_isKeyboardShown(AttachCurrentThread(),
886 java_object_)) {
887 Java_AutofillAssistantUiController_hideKeyboard(AttachCurrentThread(),
888 java_object_);
889 return true;
890 }
891
892 // For BROWSE state the back button should react in its default way.
893 if (ui_delegate_ != nullptr &&
894 (ui_delegate_->GetState() == AutofillAssistantState::BROWSE)) {
895 return false;
896 }
897
898 if (ui_delegate_ == nullptr ||
899 ui_delegate_->GetState() == AutofillAssistantState::STOPPED ||
900 ui_delegate_->IsRunningLiteScript()) {
901 if (client_->GetWebContents() != nullptr &&
902 client_->GetWebContents()->GetController().CanGoBack()) {
903 client_->GetWebContents()->GetController().GoBack();
904 }
905
906 // Lite scripts should not shut down here. The navigation will be handled
907 // by the lite script coordinator.
908 if (!ui_delegate_ || !ui_delegate_->IsRunningLiteScript()) {
909 Shutdown(Metrics::DropOutReason::BACK_BUTTON_CLICKED);
910 }
911
912 return true;
913 }
914
915 // ui_delegate_ must never be nullptr here!
916 auto back_button_settings =
917 ui_delegate_->GetClientSettings().back_button_settings;
918 if (back_button_settings.has_value()) {
919 ui_delegate_->OnStop(back_button_settings->message(),
920 back_button_settings->undo_label());
921 } else {
922 CloseOrCancel(-1, TriggerContext::CreateEmpty(),
923 Metrics::DropOutReason::BACK_BUTTON_CLICKED);
924 }
925 return true;
926 }
927
OnBottomSheetClosedWithSwipe()928 void UiControllerAndroid::OnBottomSheetClosedWithSwipe() {
929 if (ui_delegate_->IsTabSelected() && ui_delegate_->IsRunningLiteScript()) {
930 Shutdown(Metrics::DropOutReason::SHEET_CLOSED);
931 }
932 }
933
CloseOrCancel(int action_index,std::unique_ptr<TriggerContext> trigger_context,Metrics::DropOutReason dropout_reason)934 void UiControllerAndroid::CloseOrCancel(
935 int action_index,
936 std::unique_ptr<TriggerContext> trigger_context,
937 Metrics::DropOutReason dropout_reason) {
938 // Close immediately.
939 if (!ui_delegate_ ||
940 ui_delegate_->GetState() == AutofillAssistantState::STOPPED) {
941 DestroySelf();
942 return;
943 }
944
945 // Close, with an action.
946 const std::vector<UserAction>& user_actions = ui_delegate_->GetUserActions();
947 if (action_index >= 0 &&
948 static_cast<size_t>(action_index) < user_actions.size() &&
949 user_actions[action_index].chip().type == CLOSE_ACTION &&
950 ui_delegate_->PerformUserActionWithContext(action_index,
951 std::move(trigger_context))) {
952 return;
953 }
954
955 // Cancel, with a snackbar to allow UNDO.
956 ShowSnackbar(ui_delegate_->GetClientSettings().cancel_delay,
957 l10n_util::GetStringUTF8(IDS_AUTOFILL_ASSISTANT_STOPPED),
958 base::BindOnce(&UiControllerAndroid::OnCancel,
959 weak_ptr_factory_.GetWeakPtr(), action_index,
960 std::move(trigger_context), dropout_reason));
961 }
962
OnCancel(int action_index,std::unique_ptr<TriggerContext> trigger_context,Metrics::DropOutReason dropout_reason)963 void UiControllerAndroid::OnCancel(
964 int action_index,
965 std::unique_ptr<TriggerContext> trigger_context,
966 Metrics::DropOutReason dropout_reason) {
967 if (action_index == -1 || !ui_delegate_ ||
968 !ui_delegate_->PerformUserActionWithContext(action_index,
969 std::move(trigger_context))) {
970 Shutdown(dropout_reason);
971 }
972 }
973
974 // Overlay related methods.
975 base::android::ScopedJavaLocalRef<jobject>
GetOverlayModel()976 UiControllerAndroid::GetOverlayModel() {
977 return Java_AssistantModel_getOverlayModel(AttachCurrentThread(), GetModel());
978 }
979
SetOverlayState(OverlayState state)980 void UiControllerAndroid::SetOverlayState(OverlayState state) {
981 desired_overlay_state_ = state;
982
983 // Ensure that we don't set partial state if the touchable area is empty. This
984 // is important because a partial overlay will be hidden if TalkBack is
985 // enabled, and we want to completely prevent TalkBack from accessing the web
986 // page if there is no touchable area.
987 if (state == OverlayState::PARTIAL) {
988 std::vector<RectF> area;
989 ui_delegate_->GetTouchableArea(&area);
990 if (area.empty()) {
991 state = OverlayState::FULL;
992 }
993 }
994 overlay_state_ = state;
995
996 if (ui_delegate_ && ui_delegate_->ShouldShowOverlay()) {
997 ApplyOverlayState(state);
998 }
999 }
1000
ApplyOverlayState(OverlayState state)1001 void UiControllerAndroid::ApplyOverlayState(OverlayState state) {
1002 Java_AssistantOverlayModel_setState(AttachCurrentThread(), GetOverlayModel(),
1003 state);
1004 Java_AssistantModel_setAllowTalkbackOnWebsite(
1005 AttachCurrentThread(), GetModel(), state != OverlayState::FULL);
1006 }
1007
OnShouldShowOverlayChanged(bool should_show)1008 void UiControllerAndroid::OnShouldShowOverlayChanged(bool should_show) {
1009 if (should_show) {
1010 ApplyOverlayState(overlay_state_);
1011 } else {
1012 ApplyOverlayState(OverlayState::HIDDEN);
1013 }
1014 }
1015
OnTouchableAreaChanged(const RectF & visual_viewport,const std::vector<RectF> & touchable_areas,const std::vector<RectF> & restricted_areas)1016 void UiControllerAndroid::OnTouchableAreaChanged(
1017 const RectF& visual_viewport,
1018 const std::vector<RectF>& touchable_areas,
1019 const std::vector<RectF>& restricted_areas) {
1020 if (!touchable_areas.empty() &&
1021 desired_overlay_state_ == OverlayState::PARTIAL) {
1022 SetOverlayState(OverlayState::PARTIAL);
1023 }
1024
1025 JNIEnv* env = AttachCurrentThread();
1026 Java_AssistantOverlayModel_setVisualViewport(
1027 env, GetOverlayModel(), visual_viewport.left, visual_viewport.top,
1028 visual_viewport.right, visual_viewport.bottom);
1029
1030 Java_AssistantOverlayModel_setTouchableArea(
1031 env, GetOverlayModel(),
1032 base::android::ToJavaFloatArray(env, ToFloatVector(touchable_areas)));
1033
1034 Java_AssistantOverlayModel_setRestrictedArea(
1035 AttachCurrentThread(), GetOverlayModel(),
1036 base::android::ToJavaFloatArray(env, ToFloatVector(restricted_areas)));
1037 }
1038
OnUnexpectedTaps()1039 void UiControllerAndroid::OnUnexpectedTaps() {
1040 if (!ui_delegate_) {
1041 Shutdown(Metrics::DropOutReason::OVERLAY_STOP);
1042 return;
1043 }
1044
1045 ShowSnackbar(ui_delegate_->GetClientSettings().tap_shutdown_delay,
1046 l10n_util::GetStringUTF8(IDS_AUTOFILL_ASSISTANT_MAYBE_GIVE_UP),
1047 base::BindOnce(&UiControllerAndroid::Shutdown,
1048 weak_ptr_factory_.GetWeakPtr(),
1049 Metrics::DropOutReason::OVERLAY_STOP));
1050 }
1051
OnUserInteractionInsideTouchableArea()1052 void UiControllerAndroid::OnUserInteractionInsideTouchableArea() {
1053 if (ui_delegate_)
1054 ui_delegate_->OnUserInteractionInsideTouchableArea();
1055 }
1056
1057 // Other methods.
CloseCustomTab()1058 void UiControllerAndroid::CloseCustomTab() {
1059 Java_AutofillAssistantUiController_scheduleCloseCustomTab(
1060 AttachCurrentThread(), java_object_);
1061 }
1062
1063 // Collect user data related methods.
1064
1065 base::android::ScopedJavaLocalRef<jobject>
GetCollectUserDataModel()1066 UiControllerAndroid::GetCollectUserDataModel() {
1067 return Java_AssistantModel_getCollectUserDataModel(AttachCurrentThread(),
1068 GetModel());
1069 }
1070
OnShippingAddressChanged(std::unique_ptr<autofill::AutofillProfile> address)1071 void UiControllerAndroid::OnShippingAddressChanged(
1072 std::unique_ptr<autofill::AutofillProfile> address) {
1073 ui_delegate_->SetShippingAddress(std::move(address));
1074 }
1075
OnContactInfoChanged(std::unique_ptr<autofill::AutofillProfile> profile)1076 void UiControllerAndroid::OnContactInfoChanged(
1077 std::unique_ptr<autofill::AutofillProfile> profile) {
1078 ui_delegate_->SetContactInfo(std::move(profile));
1079 }
1080
OnCreditCardChanged(std::unique_ptr<autofill::CreditCard> card,std::unique_ptr<autofill::AutofillProfile> billing_profile)1081 void UiControllerAndroid::OnCreditCardChanged(
1082 std::unique_ptr<autofill::CreditCard> card,
1083 std::unique_ptr<autofill::AutofillProfile> billing_profile) {
1084 ui_delegate_->SetCreditCard(std::move(card), std::move(billing_profile));
1085 }
1086
OnTermsAndConditionsChanged(TermsAndConditionsState state)1087 void UiControllerAndroid::OnTermsAndConditionsChanged(
1088 TermsAndConditionsState state) {
1089 ui_delegate_->SetTermsAndConditions(state);
1090 }
1091
OnLoginChoiceChanged(std::string identifier)1092 void UiControllerAndroid::OnLoginChoiceChanged(std::string identifier) {
1093 ui_delegate_->SetLoginOption(identifier);
1094 }
1095
OnTextLinkClicked(int link)1096 void UiControllerAndroid::OnTextLinkClicked(int link) {
1097 ui_delegate_->OnTextLinkClicked(link);
1098 }
1099
OnFormActionLinkClicked(int link)1100 void UiControllerAndroid::OnFormActionLinkClicked(int link) {
1101 ui_delegate_->OnFormActionLinkClicked(link);
1102 }
1103
OnDateTimeRangeStartDateChanged(int year,int month,int day)1104 void UiControllerAndroid::OnDateTimeRangeStartDateChanged(int year,
1105 int month,
1106 int day) {
1107 auto date = base::make_optional<DateProto>();
1108 date->set_year(year);
1109 date->set_month(month);
1110 date->set_day(day);
1111 ui_delegate_->SetDateTimeRangeStartDate(date);
1112 }
1113
OnDateTimeRangeStartDateCleared()1114 void UiControllerAndroid::OnDateTimeRangeStartDateCleared() {
1115 ui_delegate_->SetDateTimeRangeStartDate(base::nullopt);
1116 }
1117
OnDateTimeRangeStartTimeSlotChanged(int index)1118 void UiControllerAndroid::OnDateTimeRangeStartTimeSlotChanged(int index) {
1119 ui_delegate_->SetDateTimeRangeStartTimeSlot(base::make_optional<int>(index));
1120 }
1121
OnDateTimeRangeStartTimeSlotCleared()1122 void UiControllerAndroid::OnDateTimeRangeStartTimeSlotCleared() {
1123 ui_delegate_->SetDateTimeRangeStartTimeSlot(base::nullopt);
1124 }
1125
OnDateTimeRangeEndDateChanged(int year,int month,int day)1126 void UiControllerAndroid::OnDateTimeRangeEndDateChanged(int year,
1127 int month,
1128 int day) {
1129 auto date = base::make_optional<DateProto>();
1130 date->set_year(year);
1131 date->set_month(month);
1132 date->set_day(day);
1133 ui_delegate_->SetDateTimeRangeEndDate(date);
1134 }
1135
OnDateTimeRangeEndDateCleared()1136 void UiControllerAndroid::OnDateTimeRangeEndDateCleared() {
1137 ui_delegate_->SetDateTimeRangeEndDate(base::nullopt);
1138 }
1139
OnDateTimeRangeEndTimeSlotChanged(int index)1140 void UiControllerAndroid::OnDateTimeRangeEndTimeSlotChanged(int index) {
1141 ui_delegate_->SetDateTimeRangeEndTimeSlot(base::make_optional<int>(index));
1142 }
1143
OnDateTimeRangeEndTimeSlotCleared()1144 void UiControllerAndroid::OnDateTimeRangeEndTimeSlotCleared() {
1145 ui_delegate_->SetDateTimeRangeEndTimeSlot(base::nullopt);
1146 }
1147
OnKeyValueChanged(const std::string & key,const ValueProto & value)1148 void UiControllerAndroid::OnKeyValueChanged(const std::string& key,
1149 const ValueProto& value) {
1150 ui_delegate_->SetAdditionalValue(key, value);
1151 }
1152
OnTextFocusLost()1153 void UiControllerAndroid::OnTextFocusLost() {
1154 // We set a delay to avoid having the keyboard flickering when the focus goes
1155 // from one text field to another
1156 content::GetUIThreadTaskRunner({})->PostDelayedTask(
1157 FROM_HERE,
1158 base::BindOnce(&UiControllerAndroid::HideKeyboardIfFocusNotOnText,
1159 weak_ptr_factory_.GetWeakPtr()),
1160 base::TimeDelta::FromMilliseconds(50));
1161 }
1162
IsContactComplete(autofill::AutofillProfile * contact)1163 bool UiControllerAndroid::IsContactComplete(
1164 autofill::AutofillProfile* contact) {
1165 auto* options = ui_delegate_->GetCollectUserDataOptions();
1166 if (options == nullptr) {
1167 return false;
1168 }
1169 return IsCompleteContact(contact, *options);
1170 }
1171
IsShippingAddressComplete(autofill::AutofillProfile * address)1172 bool UiControllerAndroid::IsShippingAddressComplete(
1173 autofill::AutofillProfile* address) {
1174 auto* options = ui_delegate_->GetCollectUserDataOptions();
1175 if (options == nullptr) {
1176 return false;
1177 }
1178 return IsCompleteShippingAddress(address, *options);
1179 }
1180
IsPaymentInstrumentComplete(autofill::CreditCard * card,autofill::AutofillProfile * address)1181 bool UiControllerAndroid::IsPaymentInstrumentComplete(
1182 autofill::CreditCard* card,
1183 autofill::AutofillProfile* address) {
1184 auto* options = ui_delegate_->GetCollectUserDataOptions();
1185 if (options == nullptr) {
1186 return false;
1187 }
1188 return IsCompleteCreditCard(card, address, *options);
1189 }
1190
HideKeyboardIfFocusNotOnText()1191 void UiControllerAndroid::HideKeyboardIfFocusNotOnText() {
1192 Java_AutofillAssistantUiController_hideKeyboardIfFocusNotOnText(
1193 AttachCurrentThread(), java_object_);
1194 }
1195
OnCollectUserDataOptionsChanged(const CollectUserDataOptions * collect_user_data_options)1196 void UiControllerAndroid::OnCollectUserDataOptionsChanged(
1197 const CollectUserDataOptions* collect_user_data_options) {
1198 JNIEnv* env = AttachCurrentThread();
1199 auto jmodel = GetCollectUserDataModel();
1200 if (!collect_user_data_options) {
1201 ResetGenericUiControllers();
1202 Java_AssistantCollectUserDataModel_setVisible(env, jmodel, false);
1203 return;
1204 }
1205
1206 Java_AssistantCollectUserDataModel_setRequestName(
1207 env, jmodel, collect_user_data_options->request_payer_name);
1208 Java_AssistantCollectUserDataModel_setRequestEmail(
1209 env, jmodel, collect_user_data_options->request_payer_email);
1210 Java_AssistantCollectUserDataModel_setRequestPhone(
1211 env, jmodel, collect_user_data_options->request_payer_phone);
1212 std::vector<int> contact_summary_fields;
1213 for (const auto& field : collect_user_data_options->contact_summary_fields) {
1214 contact_summary_fields.emplace_back((int)field);
1215 }
1216 Java_AssistantCollectUserDataModel_setContactSummaryDescriptionOptions(
1217 env, jmodel,
1218 Java_AssistantCollectUserDataModel_createContactDescriptionOptions(
1219 env, base::android::ToJavaIntArray(env, contact_summary_fields),
1220 collect_user_data_options->contact_summary_max_lines));
1221 std::vector<int> contact_full_fields;
1222 for (const auto& field : collect_user_data_options->contact_full_fields) {
1223 contact_full_fields.emplace_back((int)field);
1224 }
1225 Java_AssistantCollectUserDataModel_setContactFullDescriptionOptions(
1226 env, jmodel,
1227 Java_AssistantCollectUserDataModel_createContactDescriptionOptions(
1228 env, base::android::ToJavaIntArray(env, contact_full_fields),
1229 collect_user_data_options->contact_full_max_lines));
1230 Java_AssistantCollectUserDataModel_setRequestShippingAddress(
1231 env, jmodel, collect_user_data_options->request_shipping);
1232 Java_AssistantCollectUserDataModel_setRequestPayment(
1233 env, jmodel, collect_user_data_options->request_payment_method);
1234 Java_AssistantCollectUserDataModel_setRequestLoginChoice(
1235 env, jmodel, collect_user_data_options->request_login_choice);
1236 Java_AssistantCollectUserDataModel_setLoginSectionTitle(
1237 env, jmodel,
1238 base::android::ConvertUTF8ToJavaString(
1239 env, collect_user_data_options->login_section_title));
1240 Java_AssistantCollectUserDataModel_setContactSectionTitle(
1241 env, jmodel,
1242 base::android::ConvertUTF8ToJavaString(
1243 env, collect_user_data_options->contact_details_section_title));
1244 Java_AssistantCollectUserDataModel_setShippingSectionTitle(
1245 env, jmodel,
1246 base::android::ConvertUTF8ToJavaString(
1247 env, collect_user_data_options->shipping_address_section_title));
1248 Java_AssistantCollectUserDataModel_setAcceptTermsAndConditionsText(
1249 env, jmodel,
1250 base::android::ConvertUTF8ToJavaString(
1251 env, collect_user_data_options->accept_terms_and_conditions_text));
1252 Java_AssistantCollectUserDataModel_setShowTermsAsCheckbox(
1253 env, jmodel, collect_user_data_options->show_terms_as_checkbox);
1254 Java_AssistantCollectUserDataModel_setRequireBillingPostalCode(
1255 env, jmodel, collect_user_data_options->require_billing_postal_code);
1256 Java_AssistantCollectUserDataModel_setBillingPostalCodeMissingText(
1257 env, jmodel,
1258 base::android::ConvertUTF8ToJavaString(
1259 env, collect_user_data_options->billing_postal_code_missing_text));
1260 Java_AssistantCollectUserDataModel_setCreditCardExpiredText(
1261 env, jmodel,
1262 base::android::ConvertUTF8ToJavaString(
1263 env, collect_user_data_options->credit_card_expired_text));
1264 Java_AssistantCollectUserDataModel_setSupportedBasicCardNetworks(
1265 env, jmodel,
1266 base::android::ToJavaArrayOfStrings(
1267 env, collect_user_data_options->supported_basic_card_networks));
1268 if (collect_user_data_options->request_login_choice) {
1269 auto jlist = CreateJavaLoginChoiceList(
1270 env, collect_user_data_options->login_choices);
1271 Java_AssistantCollectUserDataModel_setLoginChoices(env, jmodel, jlist);
1272 }
1273 Java_AssistantCollectUserDataModel_setRequestDateRange(
1274 env, jmodel, collect_user_data_options->request_date_time_range);
1275 if (collect_user_data_options->request_date_time_range) {
1276 auto jmin_date = CreateJavaDate(
1277 env, collect_user_data_options->date_time_range.min_date());
1278 auto jmax_date = CreateJavaDate(
1279 env, collect_user_data_options->date_time_range.max_date());
1280 std::vector<std::string> time_slots;
1281 for (const auto& slot :
1282 collect_user_data_options->date_time_range.time_slots()) {
1283 time_slots.emplace_back(slot.label());
1284 }
1285 auto jtime_slots = base::android::ToJavaArrayOfStrings(env, time_slots);
1286 Java_AssistantCollectUserDataModel_setDateTimeRangeStartOptions(
1287 env, jmodel, jmin_date, jmax_date, jtime_slots);
1288 Java_AssistantCollectUserDataModel_setDateTimeRangeEndOptions(
1289 env, jmodel, jmin_date, jmax_date, jtime_slots);
1290 Java_AssistantCollectUserDataModel_setDateTimeRangeStartDateLabel(
1291 env, jmodel,
1292 base::android::ConvertUTF8ToJavaString(
1293 env,
1294 collect_user_data_options->date_time_range.start_date_label()));
1295 Java_AssistantCollectUserDataModel_setDateTimeRangeStartTimeLabel(
1296 env, jmodel,
1297 base::android::ConvertUTF8ToJavaString(
1298 env,
1299 collect_user_data_options->date_time_range.start_time_label()));
1300 Java_AssistantCollectUserDataModel_setDateTimeRangeEndDateLabel(
1301 env, jmodel,
1302 base::android::ConvertUTF8ToJavaString(
1303 env, collect_user_data_options->date_time_range.end_date_label()));
1304 Java_AssistantCollectUserDataModel_setDateTimeRangeEndTimeLabel(
1305 env, jmodel,
1306 base::android::ConvertUTF8ToJavaString(
1307 env, collect_user_data_options->date_time_range.end_time_label()));
1308 Java_AssistantCollectUserDataModel_setDateTimeRangeDateNotSetErrorMessage(
1309 env, jmodel,
1310 base::android::ConvertUTF8ToJavaString(
1311 env,
1312 collect_user_data_options->date_time_range.date_not_set_error()));
1313 Java_AssistantCollectUserDataModel_setDateTimeRangeTimeNotSetErrorMessage(
1314 env, jmodel,
1315 base::android::ConvertUTF8ToJavaString(
1316 env,
1317 collect_user_data_options->date_time_range.time_not_set_error()));
1318 }
1319 Java_AssistantCollectUserDataModel_setTermsRequireReviewText(
1320 env, jmodel,
1321 base::android::ConvertUTF8ToJavaString(
1322 env, collect_user_data_options->terms_require_review_text));
1323 Java_AssistantCollectUserDataModel_setInfoSectionText(
1324 env, jmodel,
1325 base::android::ConvertUTF8ToJavaString(
1326 env, collect_user_data_options->info_section_text),
1327 collect_user_data_options->info_section_text_center);
1328 Java_AssistantCollectUserDataModel_setPrivacyNoticeText(
1329 env, jmodel,
1330 base::android::ConvertUTF8ToJavaString(
1331 env, collect_user_data_options->privacy_notice_text));
1332
1333 Java_AssistantCollectUserDataModel_setPrependedSections(
1334 env, jmodel,
1335 CreateJavaAdditionalSections(
1336 env, collect_user_data_options->additional_prepended_sections));
1337 Java_AssistantCollectUserDataModel_setAppendedSections(
1338 env, jmodel,
1339 CreateJavaAdditionalSections(
1340 env, collect_user_data_options->additional_appended_sections));
1341
1342 if (collect_user_data_options->generic_user_interface_prepended.has_value()) {
1343 collect_user_data_prepended_generic_ui_controller_ =
1344 CreateGenericUiControllerForProto(
1345 *collect_user_data_options->generic_user_interface_prepended);
1346 Java_AssistantCollectUserDataModel_setGenericUserInterfacePrepended(
1347 env, jmodel,
1348 collect_user_data_prepended_generic_ui_controller_ != nullptr
1349 ? collect_user_data_prepended_generic_ui_controller_->GetRootView()
1350 : nullptr);
1351 }
1352 if (collect_user_data_options->generic_user_interface_appended.has_value()) {
1353 collect_user_data_appended_generic_ui_controller_ =
1354 CreateGenericUiControllerForProto(
1355 *collect_user_data_options->generic_user_interface_appended);
1356 Java_AssistantCollectUserDataModel_setGenericUserInterfaceAppended(
1357 env, jmodel,
1358 collect_user_data_appended_generic_ui_controller_ != nullptr
1359 ? collect_user_data_appended_generic_ui_controller_->GetRootView()
1360 : nullptr);
1361 }
1362
1363 Java_AssistantCollectUserDataModel_setVisible(env, jmodel, true);
1364 }
1365
OnUserDataChanged(const UserData * state,UserData::FieldChange field_change)1366 void UiControllerAndroid::OnUserDataChanged(
1367 const UserData* state,
1368 UserData::FieldChange field_change) {
1369 JNIEnv* env = AttachCurrentThread();
1370 auto jmodel = GetCollectUserDataModel();
1371 if (!state) {
1372 return;
1373 }
1374 DCHECK(ui_delegate_ != nullptr);
1375 DCHECK(client_->GetWebContents() != nullptr);
1376 const CollectUserDataOptions* collect_user_data_options =
1377 ui_delegate_->GetCollectUserDataOptions();
1378 if (collect_user_data_options == nullptr) {
1379 // If there are no options, there currently is no active
1380 // CollectUserDataAction, the UI is not shown and does not need to be
1381 // updated.
1382 return;
1383 }
1384
1385 auto jcontext =
1386 Java_AutofillAssistantUiController_getContext(env, java_object_);
1387 auto web_contents = client_->GetWebContents()->GetJavaWebContents();
1388
1389 if (field_change == UserData::FieldChange::ALL ||
1390 field_change == UserData::FieldChange::TERMS_AND_CONDITIONS) {
1391 Java_AssistantCollectUserDataModel_setTermsStatus(
1392 env, jmodel, state->terms_and_conditions_);
1393 }
1394
1395 if (field_change == UserData::FieldChange::ALL ||
1396 field_change == UserData::FieldChange::AVAILABLE_PROFILES) {
1397 // Contact profiles.
1398 auto jcontactlist =
1399 Java_AssistantCollectUserDataModel_createAutofillContactList(env);
1400 auto contact_indices = SortContactsByCompleteness(
1401 *collect_user_data_options, state->available_profiles_);
1402 for (int index : contact_indices) {
1403 auto jcontact = Java_AssistantCollectUserDataModel_createAutofillContact(
1404 env, jcontext,
1405 autofill::PersonalDataManagerAndroid::CreateJavaProfileFromNative(
1406 env, *state->available_profiles_[index]),
1407 collect_user_data_options->request_payer_name,
1408 collect_user_data_options->request_payer_phone,
1409 collect_user_data_options->request_payer_email);
1410 if (jcontact) {
1411 Java_AssistantCollectUserDataModel_addAutofillContact(env, jcontactlist,
1412 jcontact);
1413 }
1414 }
1415 Java_AssistantCollectUserDataModel_setAvailableContacts(env, jmodel,
1416 jcontactlist);
1417
1418 // Ignore changes to FieldChange::CONTACT_PROFILE, this is already coming
1419 // from the view.
1420 const autofill::AutofillProfile* contact_profile = state->selected_address(
1421 collect_user_data_options->contact_details_name);
1422 Java_AssistantCollectUserDataModel_setSelectedContactDetails(
1423 env, jmodel,
1424 contact_profile == nullptr
1425 ? nullptr
1426 : Java_AssistantCollectUserDataModel_createAutofillContact(
1427 env, jcontext,
1428 autofill::PersonalDataManagerAndroid::
1429 CreateJavaProfileFromNative(env, *contact_profile),
1430 collect_user_data_options->request_payer_name,
1431 collect_user_data_options->request_payer_phone,
1432 collect_user_data_options->request_payer_email));
1433
1434 // Billing addresses profiles.
1435 auto jbillinglist =
1436 Java_AssistantCollectUserDataModel_createAutofillAddressList(env);
1437 for (const auto& profile : state->available_profiles_) {
1438 auto jaddress = Java_AssistantCollectUserDataModel_createAutofillAddress(
1439 env, jcontext,
1440 autofill::PersonalDataManagerAndroid::CreateJavaProfileFromNative(
1441 env, *profile));
1442 if (jaddress) {
1443 Java_AssistantCollectUserDataModel_addAutofillAddress(env, jbillinglist,
1444 jaddress);
1445 }
1446 }
1447 Java_AssistantCollectUserDataModel_setAvailableBillingAddresses(
1448 env, jmodel, jbillinglist);
1449
1450 // Address profiles.
1451 auto jshippinglist =
1452 Java_AssistantCollectUserDataModel_createAutofillAddressList(env);
1453 auto address_indices = SortAddressesByCompleteness(
1454 *collect_user_data_options, state->available_profiles_);
1455 for (int index : address_indices) {
1456 auto jaddress = Java_AssistantCollectUserDataModel_createAutofillAddress(
1457 env, jcontext,
1458 autofill::PersonalDataManagerAndroid::CreateJavaProfileFromNative(
1459 env, *state->available_profiles_[index]));
1460 if (jaddress) {
1461 Java_AssistantCollectUserDataModel_addAutofillAddress(
1462 env, jshippinglist, jaddress);
1463 }
1464 }
1465 Java_AssistantCollectUserDataModel_setAvailableShippingAddresses(
1466 env, jmodel, jshippinglist);
1467
1468 // Ignore changes to FieldChange::SHIPPING_ADDRESS, this is already coming
1469 // from the view.
1470 const autofill::AutofillProfile* shipping_address = state->selected_address(
1471 collect_user_data_options->shipping_address_name);
1472 Java_AssistantCollectUserDataModel_setSelectedShippingAddress(
1473 env, jmodel,
1474 shipping_address == nullptr
1475 ? nullptr
1476 : Java_AssistantCollectUserDataModel_createAutofillAddress(
1477 env, jcontext,
1478 autofill::PersonalDataManagerAndroid::
1479 CreateJavaProfileFromNative(env, *shipping_address)));
1480 }
1481
1482 if (field_change == UserData::FieldChange::ALL ||
1483 field_change == UserData::FieldChange::AVAILABLE_PAYMENT_INSTRUMENTS) {
1484 auto jlist =
1485 Java_AssistantCollectUserDataModel_createAutofillPaymentInstrumentList(
1486 env);
1487 auto sorted_payment_instrument_indices =
1488 SortPaymentInstrumentsByCompleteness(
1489 *collect_user_data_options, state->available_payment_instruments_);
1490 for (int index : sorted_payment_instrument_indices) {
1491 const auto& instrument = state->available_payment_instruments_[index];
1492 Java_AssistantCollectUserDataModel_addAutofillPaymentInstrument(
1493 env, jlist, web_contents,
1494 instrument->card == nullptr
1495 ? nullptr
1496 : autofill::PersonalDataManagerAndroid::
1497 CreateJavaCreditCardFromNative(env, *(instrument->card)),
1498 instrument->billing_address == nullptr
1499 ? nullptr
1500 : autofill::PersonalDataManagerAndroid::
1501 CreateJavaProfileFromNative(
1502 env, *(instrument->billing_address)));
1503 }
1504 Java_AssistantCollectUserDataModel_setAvailablePaymentInstruments(
1505 env, jmodel, jlist);
1506
1507 // Ignore changes to FieldChange::CARD, this is already coming from the
1508 // view.
1509 autofill::CreditCard* card = state->selected_card_.get();
1510 const autofill::AutofillProfile* billing_address = state->selected_address(
1511 collect_user_data_options->billing_address_name);
1512 Java_AssistantCollectUserDataModel_setSelectedPaymentInstrument(
1513 env, jmodel, web_contents,
1514 card == nullptr ? nullptr
1515 : autofill::PersonalDataManagerAndroid::
1516 CreateJavaCreditCardFromNative(env, *card),
1517 billing_address == nullptr
1518 ? nullptr
1519 : autofill::PersonalDataManagerAndroid::CreateJavaProfileFromNative(
1520 env, *billing_address));
1521 }
1522
1523 if (field_change == UserData::FieldChange::ALL ||
1524 field_change == UserData::FieldChange::DATE_TIME_RANGE_START) {
1525 if (state->date_time_range_start_date_.has_value()) {
1526 Java_AssistantCollectUserDataModel_setDateTimeRangeStartDate(
1527 env, jmodel,
1528 CreateJavaDate(env, *state->date_time_range_start_date_));
1529 } else {
1530 Java_AssistantCollectUserDataModel_clearDateTimeRangeStartDate(env,
1531 jmodel);
1532 }
1533
1534 if (state->date_time_range_start_timeslot_.has_value()) {
1535 Java_AssistantCollectUserDataModel_setDateTimeRangeStartTimeSlot(
1536 env, jmodel, *state->date_time_range_start_timeslot_);
1537 } else {
1538 Java_AssistantCollectUserDataModel_clearDateTimeRangeStartTimeSlot(
1539 env, jmodel);
1540 }
1541 }
1542
1543 if (field_change == UserData::FieldChange::ALL ||
1544 field_change == UserData::FieldChange::DATE_TIME_RANGE_END) {
1545 if (state->date_time_range_end_date_.has_value()) {
1546 Java_AssistantCollectUserDataModel_setDateTimeRangeEndDate(
1547 env, jmodel, CreateJavaDate(env, *state->date_time_range_end_date_));
1548 } else {
1549 Java_AssistantCollectUserDataModel_clearDateTimeRangeEndDate(env, jmodel);
1550 }
1551
1552 if (state->date_time_range_end_timeslot_.has_value()) {
1553 Java_AssistantCollectUserDataModel_setDateTimeRangeEndTimeSlot(
1554 env, jmodel, *state->date_time_range_end_timeslot_);
1555 } else {
1556 Java_AssistantCollectUserDataModel_clearDateTimeRangeEndTimeSlot(env,
1557 jmodel);
1558 }
1559 }
1560
1561 // TODO(crbug.com/806868): Add |setSelectedLogin|.
1562 }
1563
1564 // FormProto related methods.
GetFormModel()1565 base::android::ScopedJavaLocalRef<jobject> UiControllerAndroid::GetFormModel() {
1566 return Java_AssistantModel_getFormModel(AttachCurrentThread(), GetModel());
1567 }
1568
OnFormChanged(const FormProto * form,const FormProto::Result * result)1569 void UiControllerAndroid::OnFormChanged(const FormProto* form,
1570 const FormProto::Result* result) {
1571 JNIEnv* env = AttachCurrentThread();
1572
1573 if (!form) {
1574 Java_AssistantFormModel_clearInputs(env, GetFormModel());
1575 return;
1576 }
1577
1578 auto jinput_list = Java_AssistantFormModel_createInputList(env);
1579 for (int i = 0; i < form->inputs_size(); i++) {
1580 const FormInputProto input = form->inputs(i);
1581
1582 switch (input.input_type_case()) {
1583 case FormInputProto::InputTypeCase::kCounter: {
1584 CounterInputProto counter_input = input.counter();
1585
1586 auto jcounters = Java_AssistantFormInput_createCounterList(env);
1587 for (int j = 0; j < counter_input.counters_size(); ++j) {
1588 const CounterInputProto::Counter& counter = counter_input.counters(j);
1589
1590 std::vector<int> allowed_values;
1591 for (int value : counter.allowed_values()) {
1592 allowed_values.push_back(value);
1593 }
1594
1595 auto result_value = GetPreviousFormCounterResult(result, i, j);
1596 Java_AssistantFormInput_addCounter(
1597 env, jcounters,
1598 Java_AssistantFormInput_createCounter(
1599 env,
1600 base::android::ConvertUTF8ToJavaString(env, counter.label()),
1601 base::android::ConvertUTF8ToJavaString(
1602 env, counter.description_line_1()),
1603 base::android::ConvertUTF8ToJavaString(
1604 env, counter.description_line_2()),
1605 result_value.has_value() ? result_value.value()
1606 : counter.initial_value(),
1607 counter.min_value(), counter.max_value(),
1608 base::android::ToJavaIntArray(env, allowed_values)));
1609 }
1610
1611 Java_AssistantFormModel_addInput(
1612 env, jinput_list,
1613 Java_AssistantFormInput_createCounterInput(
1614 env, i,
1615 base::android::ConvertUTF8ToJavaString(env,
1616 counter_input.label()),
1617 base::android::ConvertUTF8ToJavaString(
1618 env, counter_input.expand_text()),
1619 base::android::ConvertUTF8ToJavaString(
1620 env, counter_input.minimize_text()),
1621 jcounters, counter_input.minimized_count(),
1622 counter_input.min_counters_sum(),
1623 counter_input.max_counters_sum(),
1624 form_delegate_.GetJavaObject()));
1625 break;
1626 }
1627 case FormInputProto::InputTypeCase::kSelection: {
1628 SelectionInputProto selection_input = input.selection();
1629
1630 auto jchoices = Java_AssistantFormInput_createChoiceList(env);
1631 for (int j = 0; j < selection_input.choices_size(); ++j) {
1632 const SelectionInputProto::Choice& choice =
1633 selection_input.choices(j);
1634
1635 auto result_value = GetPreviousFormSelectionResult(result, i, j);
1636 Java_AssistantFormInput_addChoice(
1637 env, jchoices,
1638 Java_AssistantFormInput_createChoice(
1639 env,
1640 base::android::ConvertUTF8ToJavaString(env, choice.label()),
1641 base::android::ConvertUTF8ToJavaString(
1642 env, choice.description_line_1()),
1643 base::android::ConvertUTF8ToJavaString(
1644 env, choice.description_line_2()),
1645 result_value.has_value() ? result_value.value()
1646 : choice.selected()));
1647 }
1648
1649 Java_AssistantFormModel_addInput(
1650 env, jinput_list,
1651 Java_AssistantFormInput_createSelectionInput(
1652 env, i,
1653 base::android::ConvertUTF8ToJavaString(env,
1654 selection_input.label()),
1655 jchoices, selection_input.allow_multiple(),
1656 form_delegate_.GetJavaObject()));
1657 break;
1658 }
1659 case FormInputProto::InputTypeCase::INPUT_TYPE_NOT_SET:
1660 NOTREACHED();
1661 break;
1662 // Intentionally no default case to make compilation fail if a new value
1663 // was added to the enum but not to this list.
1664 }
1665 }
1666 Java_AssistantFormModel_setInputs(env, GetFormModel(), jinput_list);
1667
1668 if (form->has_info_label()) {
1669 Java_AssistantFormModel_setInfoLabel(
1670 env, GetFormModel(),
1671 base::android::ConvertUTF8ToJavaString(env, form->info_label()));
1672 } else {
1673 Java_AssistantFormModel_clearInfoLabel(env, GetFormModel());
1674 }
1675
1676 if (form->has_info_popup()) {
1677 Java_AssistantFormModel_setInfoPopup(
1678 env, GetFormModel(),
1679 ui_controller_android_utils::CreateJavaInfoPopup(env,
1680 form->info_popup()));
1681 } else {
1682 Java_AssistantFormModel_clearInfoPopup(env, GetFormModel());
1683 }
1684 }
1685
OnClientSettingsChanged(const ClientSettings & settings)1686 void UiControllerAndroid::OnClientSettingsChanged(
1687 const ClientSettings& settings) {
1688 JNIEnv* env = AttachCurrentThread();
1689 Java_AssistantOverlayModel_setTapTracking(
1690 env, GetOverlayModel(), settings.tap_count,
1691 settings.tap_tracking_duration.InMilliseconds());
1692
1693 if (settings.overlay_image.has_value()) {
1694 auto jcontext =
1695 Java_AutofillAssistantUiController_getContext(env, java_object_);
1696 const auto& image = *(settings.overlay_image);
1697 int image_size = ui_controller_android_utils::GetPixelSizeOrDefault(
1698 env, jcontext, image.image_size(), 0);
1699 int top_margin = ui_controller_android_utils::GetPixelSizeOrDefault(
1700 env, jcontext, image.image_top_margin(), 0);
1701 int bottom_margin = ui_controller_android_utils::GetPixelSizeOrDefault(
1702 env, jcontext, image.image_bottom_margin(), 0);
1703 int text_size = ui_controller_android_utils::GetPixelSizeOrDefault(
1704 env, jcontext, image.text_size(), 0);
1705
1706 Java_AssistantOverlayModel_setOverlayImage(
1707 env, GetOverlayModel(), jcontext,
1708 ui_controller_android_utils::CreateJavaDrawable(
1709 env, jcontext, image.image_drawable(),
1710 ui_delegate_->GetUserModel()),
1711 image_size, top_margin, bottom_margin,
1712 base::android::ConvertUTF8ToJavaString(env, image.text()),
1713 ui_controller_android_utils::GetJavaColor(env, image.text_color()),
1714 text_size);
1715 } else {
1716 Java_AssistantOverlayModel_clearOverlayImage(env, GetOverlayModel());
1717 }
1718 if (settings.integration_test_settings.has_value()) {
1719 header_model_->SetDisableAnimations(
1720 settings.integration_test_settings->disable_header_animations());
1721 Java_AutofillAssistantUiController_setDisableChipChangeAnimations(
1722 env, java_object_,
1723 settings.integration_test_settings
1724 ->disable_carousel_change_animations());
1725 }
1726 Java_AssistantModel_setTalkbackSheetSizeFraction(
1727 env, GetModel(), settings.talkback_sheet_size_fraction);
1728 }
1729
OnGenericUserInterfaceChanged(const GenericUserInterfaceProto * generic_ui)1730 void UiControllerAndroid::OnGenericUserInterfaceChanged(
1731 const GenericUserInterfaceProto* generic_ui) {
1732 // Try to inflate user interface from proto.
1733 if (generic_ui != nullptr) {
1734 generic_ui_controller_ = CreateGenericUiControllerForProto(*generic_ui);
1735 ClientStatus status(generic_ui_controller_ ? ACTION_APPLIED
1736 : INVALID_ACTION);
1737 ui_delegate_->GetBasicInteractions()->NotifyViewInflationFinished(status);
1738 } else {
1739 generic_ui_controller_.reset();
1740 }
1741
1742 // Set or clear generic UI.
1743 Java_AssistantGenericUiModel_setView(
1744 AttachCurrentThread(), GetGenericUiModel(),
1745 generic_ui_controller_ != nullptr ? generic_ui_controller_->GetRootView()
1746 : nullptr);
1747 }
1748
OnCounterChanged(int input_index,int counter_index,int value)1749 void UiControllerAndroid::OnCounterChanged(int input_index,
1750 int counter_index,
1751 int value) {
1752 ui_delegate_->SetCounterValue(input_index, counter_index, value);
1753 }
1754
OnChoiceSelectionChanged(int input_index,int choice_index,bool selected)1755 void UiControllerAndroid::OnChoiceSelectionChanged(int input_index,
1756 int choice_index,
1757 bool selected) {
1758 ui_delegate_->SetChoiceSelected(input_index, choice_index, selected);
1759 }
1760
1761 // Details related method.
1762
1763 base::android::ScopedJavaLocalRef<jobject>
GetDetailsModel()1764 UiControllerAndroid::GetDetailsModel() {
1765 return Java_AssistantModel_getDetailsModel(AttachCurrentThread(), GetModel());
1766 }
1767
OnDetailsChanged(const Details * details)1768 void UiControllerAndroid::OnDetailsChanged(const Details* details) {
1769 JNIEnv* env = AttachCurrentThread();
1770 auto jmodel = GetDetailsModel();
1771 if (!details) {
1772 Java_AssistantDetailsModel_clearDetails(env, jmodel);
1773 return;
1774 }
1775 auto opt_image_accessibility_hint = details->imageAccessibilityHint();
1776 base::android::ScopedJavaLocalRef<jstring> jimage_accessibility_hint =
1777 nullptr;
1778 if (opt_image_accessibility_hint.has_value()) {
1779 jimage_accessibility_hint = base::android::ConvertUTF8ToJavaString(
1780 env, opt_image_accessibility_hint.value());
1781 }
1782 auto jdetails = Java_AssistantDetails_create(
1783 env, base::android::ConvertUTF8ToJavaString(env, details->title()),
1784 details->titleMaxLines(),
1785 base::android::ConvertUTF8ToJavaString(env, details->imageUrl()),
1786 jimage_accessibility_hint, details->imageAllowClickthrough(),
1787 base::android::ConvertUTF8ToJavaString(env, details->imageDescription()),
1788 base::android::ConvertUTF8ToJavaString(env, details->imagePositiveText()),
1789 base::android::ConvertUTF8ToJavaString(env, details->imageNegativeText()),
1790 base::android::ConvertUTF8ToJavaString(env,
1791 details->imageClickthroughUrl()),
1792 details->showImagePlaceholder(),
1793 base::android::ConvertUTF8ToJavaString(env, details->totalPriceLabel()),
1794 base::android::ConvertUTF8ToJavaString(env, details->totalPrice()),
1795 base::android::ConvertUTF8ToJavaString(env, details->descriptionLine1()),
1796 base::android::ConvertUTF8ToJavaString(env, details->descriptionLine2()),
1797 base::android::ConvertUTF8ToJavaString(env, details->descriptionLine3()),
1798 base::android::ConvertUTF8ToJavaString(env, details->priceAttribution()),
1799 details->userApprovalRequired(), details->highlightTitle(),
1800 details->highlightLine1(), details->highlightLine2(),
1801 details->highlightLine3(), details->animatePlaceholders());
1802 Java_AssistantDetailsModel_setDetails(env, jmodel, jdetails);
1803 }
1804
1805 // InfoBox related method.
1806
1807 base::android::ScopedJavaLocalRef<jobject>
GetInfoBoxModel()1808 UiControllerAndroid::GetInfoBoxModel() {
1809 return Java_AssistantModel_getInfoBoxModel(AttachCurrentThread(), GetModel());
1810 }
1811
OnInfoBoxChanged(const InfoBox * info_box)1812 void UiControllerAndroid::OnInfoBoxChanged(const InfoBox* info_box) {
1813 JNIEnv* env = AttachCurrentThread();
1814 auto jmodel = GetInfoBoxModel();
1815 if (!info_box) {
1816 Java_AssistantInfoBoxModel_clearInfoBox(env, jmodel);
1817 return;
1818 }
1819
1820 const InfoBoxProto& proto = info_box->proto().info_box();
1821 auto jinfo_box = Java_AssistantInfoBox_create(
1822 env, base::android::ConvertUTF8ToJavaString(env, proto.image_path()),
1823 base::android::ConvertUTF8ToJavaString(env, proto.explanation()));
1824 Java_AssistantInfoBoxModel_setInfoBox(env, jmodel, jinfo_box);
1825 }
1826
Stop(JNIEnv * env,const base::android::JavaParamRef<jobject> & obj,int jreason)1827 void UiControllerAndroid::Stop(JNIEnv* env,
1828 const base::android::JavaParamRef<jobject>& obj,
1829 int jreason) {
1830 client_->Shutdown(static_cast<Metrics::DropOutReason>(jreason));
1831 }
1832
OnFatalError(JNIEnv * env,const base::android::JavaParamRef<jobject> & obj,const base::android::JavaParamRef<jstring> & jmessage,int jreason)1833 void UiControllerAndroid::OnFatalError(
1834 JNIEnv* env,
1835 const base::android::JavaParamRef<jobject>& obj,
1836 const base::android::JavaParamRef<jstring>& jmessage,
1837 int jreason) {
1838 if (!ui_delegate_)
1839 return;
1840 ui_delegate_->OnFatalError(
1841 base::android::ConvertJavaStringToUTF8(env, jmessage),
1842 /*show_feedback_chip=*/false,
1843 static_cast<Metrics::DropOutReason>(jreason));
1844 }
1845
ResetGenericUiControllers()1846 void UiControllerAndroid::ResetGenericUiControllers() {
1847 JNIEnv* env = AttachCurrentThread();
1848 collect_user_data_prepended_generic_ui_controller_.reset();
1849 collect_user_data_appended_generic_ui_controller_.reset();
1850 generic_ui_controller_.reset();
1851 auto jcollectuserdatamodel = GetCollectUserDataModel();
1852 Java_AssistantCollectUserDataModel_setGenericUserInterfacePrepended(
1853 env, jcollectuserdatamodel, nullptr);
1854 Java_AssistantCollectUserDataModel_setGenericUserInterfaceAppended(
1855 env, jcollectuserdatamodel, nullptr);
1856 Java_AssistantGenericUiModel_setView(env, GetGenericUiModel(), nullptr);
1857 }
1858
1859 std::unique_ptr<GenericUiRootControllerAndroid>
CreateGenericUiControllerForProto(const GenericUserInterfaceProto & proto)1860 UiControllerAndroid::CreateGenericUiControllerForProto(
1861 const GenericUserInterfaceProto& proto) {
1862 JNIEnv* env = AttachCurrentThread();
1863 auto jcontext =
1864 Java_AutofillAssistantUiController_getContext(env, java_object_);
1865 return GenericUiRootControllerAndroid::CreateFromProto(
1866 proto, base::android::ScopedJavaGlobalRef<jobject>(jcontext),
1867 generic_ui_delegate_.GetJavaObject(), ui_delegate_->GetEventHandler(),
1868 ui_delegate_->GetUserModel(), ui_delegate_->GetBasicInteractions());
1869 }
1870
1871 base::android::ScopedJavaLocalRef<jobject>
GetGenericUiModel()1872 UiControllerAndroid::GetGenericUiModel() {
1873 return Java_AssistantModel_getGenericUiModel(AttachCurrentThread(),
1874 GetModel());
1875 }
1876
1877 } // namespace autofill_assistant
1878