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