blob: 6113de2b9d7dc0d4e886b82bf6c54033695d837b [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/android/autofill_assistant/ui_controller_android.h"
#include <map>
#include <memory>
#include <utility>
#include "base/android/jni_android.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/metrics/field_trial_params.h"
#include "base/task/post_task.h"
#include "base/time/time.h"
#include "chrome/browser/android/chrome_feature_list.h"
#include "chrome/browser/autofill/android/personal_data_manager_android.h"
#include "chrome/browser/autofill/personal_data_manager_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/common/channel_info.h"
#include "components/autofill/core/browser/data_model/autofill_profile.h"
#include "components/autofill/core/browser/data_model/credit_card.h"
#include "components/autofill_assistant/browser/controller.h"
#include "components/autofill_assistant/browser/features.h"
#include "components/autofill_assistant/browser/metrics.h"
#include "components/autofill_assistant/browser/rectf.h"
#include "components/signin/core/browser/account_info.h"
#include "components/strings/grit/components_strings.h"
#include "components/version_info/channel.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_contents.h"
#include "google_apis/google_api_keys.h"
#include "jni/AssistantDetailsModel_jni.h"
#include "jni/AssistantDetails_jni.h"
#include "jni/AssistantFormInput_jni.h"
#include "jni/AssistantFormModel_jni.h"
#include "jni/AssistantHeaderModel_jni.h"
#include "jni/AssistantInfoBoxModel_jni.h"
#include "jni/AssistantInfoBox_jni.h"
#include "jni/AssistantModel_jni.h"
#include "jni/AssistantOverlayModel_jni.h"
#include "jni/AssistantPaymentRequestModel_jni.h"
#include "jni/AutofillAssistantUiController_jni.h"
#include "services/identity/public/cpp/identity_manager.h"
#include "ui/base/l10n/l10n_util.h"
using base::android::AttachCurrentThread;
using base::android::JavaParamRef;
using base::android::JavaRef;
namespace autofill_assistant {
namespace {
// How long to leave the UI showing after AA has stopped.
static constexpr base::TimeDelta kGracefulShutdownDelay =
base::TimeDelta::FromSeconds(5);
} // namespace
// static
std::unique_ptr<UiControllerAndroid> UiControllerAndroid::CreateFromWebContents(
content::WebContents* web_contents) {
JNIEnv* env = AttachCurrentThread();
auto jactivity = Java_AutofillAssistantUiController_findAppropriateActivity(
env, web_contents->GetJavaWebContents());
if (!jactivity) {
return nullptr;
}
return std::make_unique<UiControllerAndroid>(env, jactivity);
}
UiControllerAndroid::UiControllerAndroid(
JNIEnv* env,
const base::android::JavaRef<jobject>& jactivity)
: overlay_delegate_(this),
payment_request_delegate_(this),
form_delegate_(this),
weak_ptr_factory_(this) {
java_object_ = Java_AutofillAssistantUiController_create(
env, jactivity,
/* allowTabSwitching= */
base::FeatureList::IsEnabled(features::kAutofillAssistantChromeEntry),
reinterpret_cast<intptr_t>(this));
// Register overlay_delegate_ as delegate for the overlay.
Java_AssistantOverlayModel_setDelegate(env, GetOverlayModel(),
overlay_delegate_.GetJavaObject());
// Register payment_request_delegate_ as delegate for the payment request UI.
Java_AssistantPaymentRequestModel_setDelegate(
env, GetPaymentRequestModel(), payment_request_delegate_.GetJavaObject());
}
void UiControllerAndroid::Attach(content::WebContents* web_contents,
Client* client,
UiDelegate* ui_delegate) {
DCHECK(web_contents);
DCHECK(client);
DCHECK(ui_delegate);
client_ = client;
ui_delegate_ = ui_delegate;
JNIEnv* env = AttachCurrentThread();
auto java_web_contents = web_contents->GetJavaWebContents();
Java_AutofillAssistantUiController_setWebContents(env, java_object_,
java_web_contents);
Java_AssistantPaymentRequestModel_setWebContents(
env, GetPaymentRequestModel(), java_web_contents);
Java_AssistantOverlayModel_setWebContents(env, GetOverlayModel(),
java_web_contents);
if (ui_delegate->GetState() != AutofillAssistantState::INACTIVE) {
// The UI was created for an existing Controller.
OnStatusMessageChanged(ui_delegate->GetStatusMessage());
OnProgressChanged(ui_delegate->GetProgress());
OnProgressVisibilityChanged(ui_delegate->GetProgressVisible());
OnInfoBoxChanged(ui_delegate_->GetInfoBox());
OnDetailsChanged(ui_delegate->GetDetails());
OnSuggestionsChanged(ui_delegate->GetSuggestions());
UpdateActions();
OnPaymentRequestOptionsChanged(ui_delegate->GetPaymentRequestOptions());
OnPaymentRequestInformationChanged(
ui_delegate->GetPaymentRequestInformation());
std::vector<RectF> area;
ui_delegate->GetTouchableArea(&area);
OnTouchableAreaChanged(area);
OnResizeViewportChanged(ui_delegate->GetResizeViewport());
OnPeekModeChanged(ui_delegate->GetPeekMode());
OnFormChanged(ui_delegate->GetForm());
UiDelegate::OverlayColors colors;
ui_delegate->GetOverlayColors(&colors);
OnOverlayColorsChanged(colors);
OnStateChanged(ui_delegate->GetState());
}
SetVisible(true);
}
UiControllerAndroid::~UiControllerAndroid() {
Java_AutofillAssistantUiController_clearNativePtr(AttachCurrentThread(),
java_object_);
}
base::android::ScopedJavaLocalRef<jobject> UiControllerAndroid::GetModel() {
return Java_AutofillAssistantUiController_getModel(AttachCurrentThread(),
java_object_);
}
// Header related methods.
base::android::ScopedJavaLocalRef<jobject>
UiControllerAndroid::GetHeaderModel() {
return Java_AssistantModel_getHeaderModel(AttachCurrentThread(), GetModel());
}
void UiControllerAndroid::OnStateChanged(AutofillAssistantState new_state) {
if (!Java_AssistantModel_getVisible(AttachCurrentThread(), GetModel())) {
// Leave the UI alone as long as it's invisible. Missed state changes will
// be recovered by SetVisible(true).
return;
}
SetupForState();
}
void UiControllerAndroid::SetupForState() {
UpdateActions();
AutofillAssistantState state = ui_delegate_->GetState();
switch (state) {
case AutofillAssistantState::STARTING:
SetOverlayState(OverlayState::FULL);
AllowShowingSoftKeyboard(false);
SetSpinPoodle(true);
return;
case AutofillAssistantState::RUNNING:
SetOverlayState(OverlayState::FULL);
AllowShowingSoftKeyboard(false);
SetSpinPoodle(true);
return;
case AutofillAssistantState::AUTOSTART_FALLBACK_PROMPT:
SetOverlayState(OverlayState::HIDDEN);
AllowShowingSoftKeyboard(true);
SetSpinPoodle(false);
// user interaction is needed.
ExpandBottomSheet();
return;
case AutofillAssistantState::PROMPT:
SetOverlayState(OverlayState::PARTIAL);
AllowShowingSoftKeyboard(true);
SetSpinPoodle(false);
// user interaction is needed.
ExpandBottomSheet();
return;
case AutofillAssistantState::MODAL_DIALOG:
SetOverlayState(OverlayState::FULL);
AllowShowingSoftKeyboard(true);
SetSpinPoodle(true);
return;
case AutofillAssistantState::STOPPED:
SetOverlayState(OverlayState::HIDDEN);
AllowShowingSoftKeyboard(true);
SetSpinPoodle(false);
// make sure user sees the error message.
ExpandBottomSheet();
return;
case AutofillAssistantState::INACTIVE:
// TODO(crbug.com/806868): Add support for switching back to the inactive
// state, which is the initial state.
return;
}
NOTREACHED() << "Unknown state: " << static_cast<int>(state);
}
void UiControllerAndroid::OnStatusMessageChanged(const std::string& message) {
if (!message.empty()) {
JNIEnv* env = AttachCurrentThread();
Java_AssistantHeaderModel_setStatusMessage(
env, GetHeaderModel(),
base::android::ConvertUTF8ToJavaString(env, message));
}
}
void UiControllerAndroid::OnProgressChanged(int progress) {
Java_AssistantHeaderModel_setProgress(AttachCurrentThread(), GetHeaderModel(),
progress);
}
void UiControllerAndroid::OnProgressVisibilityChanged(bool visible) {
Java_AssistantHeaderModel_setProgressVisible(AttachCurrentThread(),
GetHeaderModel(), visible);
}
void UiControllerAndroid::OnResizeViewportChanged(bool resize_viewport) {
Java_AutofillAssistantUiController_setResizeViewport(
AttachCurrentThread(), java_object_, resize_viewport);
}
void UiControllerAndroid::OnPeekModeChanged(
ConfigureBottomSheetProto::PeekMode peek_mode) {
Java_AutofillAssistantUiController_setPeekMode(AttachCurrentThread(),
java_object_, peek_mode);
}
void UiControllerAndroid::OnOverlayColorsChanged(
const UiDelegate::OverlayColors& colors) {
JNIEnv* env = AttachCurrentThread();
auto overlay_model = GetOverlayModel();
if (!Java_AssistantOverlayModel_setBackgroundColor(
env, overlay_model,
base::android::ConvertUTF8ToJavaString(env, colors.background))) {
DVLOG(1) << __func__
<< ": Ignoring invalid overlay color: " << colors.background;
}
if (!Java_AssistantOverlayModel_setHighlightBorderColor(
env, overlay_model,
base::android::ConvertUTF8ToJavaString(env,
colors.highlight_border))) {
DVLOG(1) << __func__ << ": Ignoring invalid highlight border color: "
<< colors.highlight_border;
}
}
void UiControllerAndroid::AllowShowingSoftKeyboard(bool enabled) {
Java_AssistantModel_setAllowSoftKeyboard(AttachCurrentThread(), GetModel(),
enabled);
}
void UiControllerAndroid::ExpandBottomSheet() {
Java_AutofillAssistantUiController_expandBottomSheet(AttachCurrentThread(),
java_object_);
}
void UiControllerAndroid::SetSpinPoodle(bool enabled) {
Java_AssistantHeaderModel_setSpinPoodle(AttachCurrentThread(),
GetHeaderModel(), enabled);
}
void UiControllerAndroid::Shutdown(Metrics::DropOutReason reason) {
client_->Shutdown(reason);
}
void UiControllerAndroid::ShowSnackbar(const std::string& message,
base::OnceCallback<void()> action) {
JNIEnv* env = AttachCurrentThread();
auto jmodel = GetModel();
if (!Java_AssistantModel_getVisible(env, jmodel)) {
// If the UI is not visible, execute the action immediately.
std::move(action).Run();
return;
}
SetVisible(false);
snackbar_action_ = std::move(action);
Java_AutofillAssistantUiController_showSnackbar(
env, java_object_, base::android::ConvertUTF8ToJavaString(env, message));
}
void UiControllerAndroid::SnackbarResult(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj,
jboolean undo) {
base::OnceCallback<void()> action = std::move(snackbar_action_);
if (!action) {
NOTREACHED();
return;
}
if (undo) {
SetVisible(true);
return;
}
std::move(action).Run();
}
void UiControllerAndroid::DestroySelf() {
client_->DestroyUI();
}
void UiControllerAndroid::SetVisible(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
jboolean visible) {
SetVisible(visible);
}
void UiControllerAndroid::SetVisible(bool visible) {
Java_AssistantModel_setVisible(AttachCurrentThread(), GetModel(), visible);
if (visible) {
// Recover possibly state changes missed by OnStateChanged()
SetupForState();
} else {
SetOverlayState(OverlayState::HIDDEN);
}
}
// Suggestions and actions carousels related methods.
void UiControllerAndroid::OnSuggestionsChanged(
const std::vector<Chip>& suggestions) {
JNIEnv* env = AttachCurrentThread();
std::vector<int> icons(suggestions.size());
std::vector<std::string> texts(suggestions.size());
bool disabled[suggestions.size()];
for (size_t i = 0; i < suggestions.size(); i++) {
icons[i] = suggestions[i].icon;
texts[i] = suggestions[i].text;
disabled[i] = suggestions[i].disabled;
}
Java_AutofillAssistantUiController_setSuggestions(
env, java_object_, base::android::ToJavaIntArray(env, icons),
base::android::ToJavaArrayOfStrings(env, texts),
base::android::ToJavaBooleanArray(env, disabled, suggestions.size()));
}
void UiControllerAndroid::UpdateActions() {
DCHECK(ui_delegate_);
JNIEnv* env = AttachCurrentThread();
bool has_close_or_cancel = false;
auto chips = Java_AutofillAssistantUiController_createChipList(env);
const auto& actions = ui_delegate_->GetActions();
int action_count = static_cast<int>(actions.size());
for (int i = 0; i < action_count; i++) {
const auto& action = actions[i];
auto text = base::android::ConvertUTF8ToJavaString(env, action.text);
int icon = action.icon;
switch (action.type) {
case HIGHLIGHTED_ACTION:
Java_AutofillAssistantUiController_addHighlightedActionButton(
env, java_object_, chips, icon, text, i, action.disabled);
break;
case NORMAL_ACTION:
Java_AutofillAssistantUiController_addActionButton(
env, java_object_, chips, icon, text, i, action.disabled);
break;
case CANCEL_ACTION:
// A Cancel button sneaks in an UNDO snackbar before executing the
// action, while a close button behaves like a normal button.
Java_AutofillAssistantUiController_addCancelButton(
env, java_object_, chips, icon, text, i, action.disabled);
has_close_or_cancel = true;
break;
case CLOSE_ACTION:
Java_AutofillAssistantUiController_addActionButton(
env, java_object_, chips, icon, text, i, action.disabled);
has_close_or_cancel = true;
break;
case DONE_ACTION:
Java_AutofillAssistantUiController_addHighlightedActionButton(
env, java_object_, chips, icon, text, i, action.disabled);
has_close_or_cancel = true;
break;
case SUGGESTION: // Suggestions are handled separately.
case UNKNOWN_CHIP_TYPE: // Ignore unknown types
break;
// Default intentionally left out to cause a compilation error when a
// new type is added.
}
}
if (!has_close_or_cancel) {
if (ui_delegate_->GetState() == AutofillAssistantState::STOPPED) {
Java_AutofillAssistantUiController_addCloseButton(
env, java_object_, chips, ICON_CLEAR,
base::android::ConvertUTF8ToJavaString(env, ""),
/* disabled= */ false);
} else if (ui_delegate_->GetState() != AutofillAssistantState::INACTIVE) {
Java_AutofillAssistantUiController_addCancelButton(
env, java_object_, chips, ICON_CLEAR,
base::android::ConvertUTF8ToJavaString(env, ""), -1,
/* disabled= */ false);
}
}
Java_AutofillAssistantUiController_setActions(env, java_object_, chips);
}
void UiControllerAndroid::OnActionsChanged(const std::vector<Chip>& actions) {
UpdateActions();
}
void UiControllerAndroid::OnSuggestionSelected(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
jint index) {
if (ui_delegate_)
ui_delegate_->SelectSuggestion(index);
}
void UiControllerAndroid::OnActionSelected(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
jint index) {
if (ui_delegate_)
ui_delegate_->SelectAction(index);
}
void UiControllerAndroid::OnCancelButtonClicked(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
jint index) {
OnCancelButtonWithActionIndexClicked(index);
}
void UiControllerAndroid::OnCancelButtonClicked() {
OnCancelButtonWithActionIndexClicked(-1);
}
void UiControllerAndroid::OnCancelButtonWithActionIndexClicked(
int action_index) {
ShowSnackbar(l10n_util::GetStringUTF8(IDS_AUTOFILL_ASSISTANT_STOPPED),
base::BindOnce(&UiControllerAndroid::OnCancel,
weak_ptr_factory_.GetWeakPtr(), action_index));
}
void UiControllerAndroid::OnCancel(int action_index) {
if (action_index == -1 || !ui_delegate_) {
Shutdown(Metrics::SHEET_CLOSED);
return;
}
ui_delegate_->SelectAction(action_index);
}
void UiControllerAndroid::OnCloseButtonClicked(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller) {
DestroySelf();
}
// Overlay related methods.
base::android::ScopedJavaLocalRef<jobject>
UiControllerAndroid::GetOverlayModel() {
return Java_AssistantModel_getOverlayModel(AttachCurrentThread(), GetModel());
}
void UiControllerAndroid::SetOverlayState(OverlayState state) {
Java_AssistantOverlayModel_setState(AttachCurrentThread(), GetOverlayModel(),
state);
}
void UiControllerAndroid::OnTouchableAreaChanged(
const std::vector<RectF>& areas) {
JNIEnv* env = AttachCurrentThread();
std::vector<float> flattened;
for (const auto& rect : areas) {
flattened.emplace_back(rect.left);
flattened.emplace_back(rect.top);
flattened.emplace_back(rect.right);
flattened.emplace_back(rect.bottom);
}
Java_AssistantOverlayModel_setTouchableArea(
env, GetOverlayModel(), base::android::ToJavaFloatArray(env, flattened));
}
void UiControllerAndroid::OnUnexpectedTaps() {
ShowSnackbar(
l10n_util::GetStringUTF8(IDS_AUTOFILL_ASSISTANT_MAYBE_GIVE_UP),
base::BindOnce(&UiControllerAndroid::Shutdown,
weak_ptr_factory_.GetWeakPtr(), Metrics::SHEET_CLOSED));
}
void UiControllerAndroid::UpdateTouchableArea() {
if (ui_delegate_)
ui_delegate_->UpdateTouchableArea();
}
void UiControllerAndroid::OnUserInteractionInsideTouchableArea() {
if (ui_delegate_)
ui_delegate_->OnUserInteractionInsideTouchableArea();
}
// Other methods.
void UiControllerAndroid::ShowOnboarding(
JNIEnv* env,
const base::android::JavaParamRef<jstring>& jexperiment_ids,
const base::android::JavaParamRef<jobject>& on_accept) {
Java_AutofillAssistantUiController_onShowOnboarding(
env, java_object_, jexperiment_ids, on_accept);
}
void UiControllerAndroid::WillShutdown(Metrics::DropOutReason reason) {
if (reason == Metrics::CUSTOM_TAB_CLOSED) {
Java_AutofillAssistantUiController_scheduleCloseCustomTab(
AttachCurrentThread(), java_object_);
}
// Capture the debug context, for including into a feedback possibly sent
// later, after the delegate has been possibly destroyed.
DCHECK(ui_delegate_);
AutofillAssistantState final_state = ui_delegate_->GetState();
ui_delegate_ = nullptr;
// Get rid of the UI, either immediately, or with a delay, to give time to
// users to read any message.
if (final_state != AutofillAssistantState::STOPPED) {
DestroySelf();
} else {
base::PostDelayedTaskWithTraits(
FROM_HERE, {content::BrowserThread::UI},
base::BindOnce(&UiControllerAndroid::DestroySelf,
weak_ptr_factory_.GetWeakPtr()),
kGracefulShutdownDelay);
}
}
// Payment request related methods.
base::android::ScopedJavaLocalRef<jobject>
UiControllerAndroid::GetPaymentRequestModel() {
return Java_AssistantModel_getPaymentRequestModel(AttachCurrentThread(),
GetModel());
}
void UiControllerAndroid::OnShippingAddressChanged(
std::unique_ptr<autofill::AutofillProfile> address) {
ui_delegate_->SetShippingAddress(std::move(address));
}
void UiControllerAndroid::OnBillingAddressChanged(
std::unique_ptr<autofill::AutofillProfile> address) {
ui_delegate_->SetBillingAddress(std::move(address));
}
void UiControllerAndroid::OnContactInfoChanged(std::string name,
std::string phone,
std::string email) {
ui_delegate_->SetContactInfo(name, phone, email);
}
void UiControllerAndroid::OnCreditCardChanged(
std::unique_ptr<autofill::CreditCard> card) {
ui_delegate_->SetCreditCard(std::move(card));
}
void UiControllerAndroid::OnTermsAndConditionsChanged(
TermsAndConditionsState state) {
ui_delegate_->SetTermsAndConditions(state);
}
void UiControllerAndroid::OnPaymentRequestOptionsChanged(
const PaymentRequestOptions* payment_options) {
JNIEnv* env = AttachCurrentThread();
auto jmodel = GetPaymentRequestModel();
if (!payment_options) {
Java_AssistantPaymentRequestModel_setVisible(env, jmodel, false);
return;
}
Java_AssistantPaymentRequestModel_setRequestName(
env, jmodel, payment_options->request_payer_name);
Java_AssistantPaymentRequestModel_setRequestEmail(
env, jmodel, payment_options->request_payer_email);
Java_AssistantPaymentRequestModel_setRequestPhone(
env, jmodel, payment_options->request_payer_phone);
Java_AssistantPaymentRequestModel_setRequestShippingAddress(
env, jmodel, payment_options->request_shipping);
Java_AssistantPaymentRequestModel_setRequestPayment(
env, jmodel, payment_options->request_payment_method);
Java_AssistantPaymentRequestModel_setSupportedBasicCardNetworks(
env, jmodel,
base::android::ToJavaArrayOfStrings(
env, payment_options->supported_basic_card_networks));
Java_AssistantPaymentRequestModel_setVisible(env, jmodel, true);
}
void UiControllerAndroid::OnPaymentRequestInformationChanged(
const PaymentInformation* state) {
JNIEnv* env = AttachCurrentThread();
auto jmodel = GetPaymentRequestModel();
if (!state) {
return;
}
// TODO(crbug.com/806868): Add |setContactDetails|, |setShippingAddress| and
// |setPaymentMethod|.
Java_AssistantPaymentRequestModel_setTermsStatus(env, jmodel,
state->terms_and_conditions);
}
// FormProto related methods.
base::android::ScopedJavaLocalRef<jobject> UiControllerAndroid::GetFormModel() {
return Java_AssistantModel_getFormModel(AttachCurrentThread(), GetModel());
}
void UiControllerAndroid::OnFormChanged(const FormProto* form) {
JNIEnv* env = AttachCurrentThread();
if (!form) {
Java_AssistantFormModel_clearInputs(env, GetFormModel());
return;
}
auto jinput_list = Java_AssistantFormModel_createInputList(env);
for (int i = 0; i < form->inputs_size(); i++) {
const FormInputProto input = form->inputs(i);
switch (input.input_type_case()) {
case FormInputProto::InputTypeCase::kCounter: {
CounterInputProto counter_input = input.counter();
auto jcounters = Java_AssistantFormInput_createCounterList(env);
for (const CounterInputProto::Counter counter :
counter_input.counters()) {
Java_AssistantFormInput_addCounter(
env, jcounters,
Java_AssistantFormInput_createCounter(
env,
base::android::ConvertUTF8ToJavaString(env, counter.label()),
base::android::ConvertUTF8ToJavaString(env,
counter.subtext()),
counter.initial_value(), counter.min_value(),
counter.max_value()));
}
Java_AssistantFormModel_addInput(
env, jinput_list,
Java_AssistantFormInput_createCounterInput(
env, i,
base::android::ConvertUTF8ToJavaString(env,
counter_input.label()),
base::android::ConvertUTF8ToJavaString(
env, counter_input.expand_text()),
base::android::ConvertUTF8ToJavaString(
env, counter_input.minimize_text()),
jcounters, counter_input.minimized_count(),
counter_input.min_counters_sum(),
counter_input.max_counters_sum(),
form_delegate_.GetJavaObject()));
break;
}
case FormInputProto::InputTypeCase::kSelection: {
SelectionInputProto selection_input = input.selection();
auto jchoices = Java_AssistantFormInput_createChoiceList(env);
for (const SelectionInputProto::Choice choice :
selection_input.choices()) {
Java_AssistantFormInput_addChoice(
env, jchoices,
Java_AssistantFormInput_createChoice(
env,
base::android::ConvertUTF8ToJavaString(env, choice.label()),
choice.selected()));
}
Java_AssistantFormModel_addInput(
env, jinput_list,
Java_AssistantFormInput_createSelectionInput(
env, i,
base::android::ConvertUTF8ToJavaString(env,
selection_input.label()),
jchoices, selection_input.allow_multiple(),
form_delegate_.GetJavaObject()));
break;
}
case FormInputProto::InputTypeCase::INPUT_TYPE_NOT_SET:
NOTREACHED();
break;
// Intentionally no default case to make compilation fail if a new value
// was added to the enum but not to this list.
}
Java_AssistantFormModel_setInputs(env, GetFormModel(), jinput_list);
}
}
void UiControllerAndroid::OnCounterChanged(int input_index,
int counter_index,
int value) {
ui_delegate_->SetCounterValue(input_index, counter_index, value);
}
void UiControllerAndroid::OnChoiceSelectionChanged(int input_index,
int choice_index,
bool selected) {
ui_delegate_->SetChoiceSelected(input_index, choice_index, selected);
}
// Details related method.
base::android::ScopedJavaLocalRef<jobject>
UiControllerAndroid::GetDetailsModel() {
return Java_AssistantModel_getDetailsModel(AttachCurrentThread(), GetModel());
}
void UiControllerAndroid::OnDetailsChanged(const Details* details) {
JNIEnv* env = AttachCurrentThread();
auto jmodel = GetDetailsModel();
if (!details) {
Java_AssistantDetailsModel_clearDetails(env, jmodel);
return;
}
const DetailsProto& proto = details->detailsProto();
const DetailsChangesProto& changes = details->changes();
auto jdetails = Java_AssistantDetails_create(
env, base::android::ConvertUTF8ToJavaString(env, proto.title()),
base::android::ConvertUTF8ToJavaString(env, proto.image_url()),
proto.image_clickthrough_data().allow_clickthrough(),
base::android::ConvertUTF8ToJavaString(
env, proto.image_clickthrough_data().description()),
base::android::ConvertUTF8ToJavaString(
env, proto.image_clickthrough_data().positive_text()),
base::android::ConvertUTF8ToJavaString(
env, proto.image_clickthrough_data().negative_text()),
base::android::ConvertUTF8ToJavaString(
env, proto.image_clickthrough_data().clickthrough_url()),
proto.show_image_placeholder(),
base::android::ConvertUTF8ToJavaString(env, proto.total_price_label()),
base::android::ConvertUTF8ToJavaString(env, proto.total_price()),
base::android::ConvertUTF8ToJavaString(env, details->GetDatetime()),
proto.datetime().date().year(), proto.datetime().date().month(),
proto.datetime().date().day(), proto.datetime().time().hour(),
proto.datetime().time().minute(), proto.datetime().time().second(),
base::android::ConvertUTF8ToJavaString(env, proto.description_line_1()),
base::android::ConvertUTF8ToJavaString(env, proto.description_line_2()),
base::android::ConvertUTF8ToJavaString(env, proto.description_line_3()),
changes.user_approval_required(), changes.highlight_title(),
changes.highlight_line1(), changes.highlight_line2(),
changes.highlight_line3(), proto.animate_placeholders());
Java_AssistantDetailsModel_setDetails(env, jmodel, jdetails);
}
// InfoBox related method.
base::android::ScopedJavaLocalRef<jobject>
UiControllerAndroid::GetInfoBoxModel() {
return Java_AssistantModel_getInfoBoxModel(AttachCurrentThread(), GetModel());
}
void UiControllerAndroid::OnInfoBoxChanged(const InfoBox* info_box) {
JNIEnv* env = AttachCurrentThread();
auto jmodel = GetInfoBoxModel();
if (!info_box) {
Java_AssistantInfoBoxModel_clearInfoBox(env, jmodel);
return;
}
const InfoBoxProto& proto = info_box->proto().info_box();
auto jinfo_box = Java_AssistantInfoBox_create(
env, base::android::ConvertUTF8ToJavaString(env, proto.image_path()),
base::android::ConvertUTF8ToJavaString(env, proto.explanation()));
Java_AssistantInfoBoxModel_setInfoBox(env, jmodel, jinfo_box);
}
void UiControllerAndroid::Stop(JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj,
int jreason) {
client_->Shutdown(static_cast<Metrics::DropOutReason>(jreason));
}
void UiControllerAndroid::OnFatalError(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj,
const base::android::JavaParamRef<jstring>& jmessage,
int jreason) {
if (!ui_delegate_)
return;
ui_delegate_->OnFatalError(
base::android::ConvertJavaStringToUTF8(env, jmessage),
static_cast<Metrics::DropOutReason>(jreason));
}
} // namespace autofill_assistant.