blob: 80231fe58ec9e8bdf3c79201cac02079ea84ffd9 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/android/modal_dialog_wrapper.h"
#include "base/android/jni_android.h"
#include "base/android/scoped_java_ref.h"
#include "ui/android/modal_dialog_manager_bridge.h"
#include "ui/android/window_android.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/models/dialog_model.h"
#include "ui/base/mojom/dialog_button.mojom.h"
#include "ui/strings/grit/ui_strings.h"
// Must come after all headers that specialize FromJniType() / ToJniType().
#include "ui/android/ui_android_jni_headers/ModalDialogWrapper_jni.h"
using base::android::ConvertUTF16ToJavaString;
using base::android::JavaParamRef;
using base::android::ScopedJavaLocalRef;
namespace ui {
ModalDialogWrapper* g_dialog_ptr_for_testing = nullptr;
// static:
void ModalDialogWrapper::ShowTabModal(
std::unique_ptr<ui::DialogModel> dialog_model,
ui::WindowAndroid* window) {
ModalDialogWrapper* tab_modal =
new ModalDialogWrapper(std::move(dialog_model), window);
g_dialog_ptr_for_testing = tab_modal;
tab_modal->BuildPropertyModel();
auto* dialog_manager = window->GetModalDialogManagerBridge();
CHECK(dialog_manager);
dialog_manager->ShowDialog(tab_modal->java_obj_,
ModalDialogManagerBridge::ModalDialogType::kTab);
// `tab_modal` will delete itself when dialog is dismissed.
}
// static:
ModalDialogWrapper* ModalDialogWrapper::GetDialogForTesting() {
return g_dialog_ptr_for_testing;
}
// private:
ModalDialogWrapper::ModalDialogWrapper(
std::unique_ptr<DialogModel> dialog_model,
ui::WindowAndroid* window_android)
: dialog_model_(std::move(dialog_model)), window_android_(window_android) {
JNIEnv* env = base::android::AttachCurrentThread();
java_obj_ = Java_ModalDialogWrapper_create(
env, reinterpret_cast<uintptr_t>(this), window_android_->GetJavaObject());
}
ModalDialogWrapper::~ModalDialogWrapper() {
dialog_model_->OnDialogDestroying(DialogModelHost::GetPassKey());
}
namespace { // private helper for ModalDialogWrapper::BuildPropertyModel
ScopedJavaLocalRef<jstring> GetButtonLabel(JNIEnv* env,
DialogModel::Button* button,
int default_label_id) {
if (!button) {
return ScopedJavaLocalRef<jstring>();
}
const std::u16string& label_text = button->label();
return ConvertUTF16ToJavaString(
env, label_text.empty() ? l10n_util::GetStringUTF16(default_label_id)
: label_text);
}
std::u16string getMessageParagraph(DialogModelField* field) {
const DialogModelLabel& label = field->AsParagraph()->label();
std::u16string text;
auto replacements = label.replacements();
if (replacements.empty()) {
text = label.GetString();
} else {
std::vector<std::u16string> string_replacements;
for (auto replacement : replacements) {
string_replacements.push_back(replacement.text());
}
text = l10n_util::GetStringFUTF16(label.message_id(), string_replacements,
nullptr);
}
return text;
}
} // anonymous namespace
ModalDialogWrapper::ModalDialogButtonStyles
ModalDialogWrapper::GetButtonStyles() const {
auto* ok_button = dialog_model_->ok_button(DialogModelHost::GetPassKey());
if (!ok_button) {
return ModalDialogButtonStyles::kPrimaryOutlineNegativeOutline;
}
auto* cancel_button =
dialog_model_->cancel_button(DialogModelHost::GetPassKey());
const ButtonStyle ok_button_style =
ok_button->style().value_or(ButtonStyle::kDefault);
const ButtonStyle cancel_button_style =
cancel_button ? cancel_button->style().value_or(ui::ButtonStyle::kDefault)
: ButtonStyle::kDefault;
const std::optional<mojom::DialogButton>& override_default_button =
dialog_model_->override_default_button(DialogModelHost::GetPassKey());
const bool is_ok_prominent =
(override_default_button == mojom::DialogButton::kOk) ||
(ok_button_style == ui::ButtonStyle::kProminent &&
!override_default_button.has_value());
const bool is_cancel_prominent =
(override_default_button == mojom::DialogButton::kCancel) ||
(cancel_button_style == ui::ButtonStyle::kProminent &&
!override_default_button.has_value());
if (is_ok_prominent && is_cancel_prominent) {
NOTREACHED() << "Both buttons cannot be prominent.";
}
if (is_ok_prominent) {
return (cancel_button)
? ModalDialogButtonStyles::kPrimaryFilledNegativeOutline
: ModalDialogButtonStyles::kPrimaryFilledNoNegative;
}
if (is_cancel_prominent) {
return ModalDialogButtonStyles::kPrimaryOutlineNegativeFilled;
}
return ModalDialogButtonStyles::kPrimaryOutlineNegativeOutline;
}
void ModalDialogWrapper::BuildPropertyModel() {
JNIEnv* env = base::android::AttachCurrentThread();
ScopedJavaLocalRef<jstring> title = ConvertUTF16ToJavaString(
env, dialog_model_->title(DialogModelHost::GetPassKey()));
ScopedJavaLocalRef<jstring> ok_button_label = GetButtonLabel(
env, dialog_model_->ok_button(DialogModelHost::GetPassKey()), IDS_APP_OK);
ScopedJavaLocalRef<jstring> cancel_button_label = GetButtonLabel(
env, dialog_model_->cancel_button(DialogModelHost::GetPassKey()),
IDS_APP_CANCEL);
ModalDialogButtonStyles buttonStyles = GetButtonStyles();
Java_ModalDialogWrapper_withTitleAndButtons(
env, java_obj_, title, ok_button_label, cancel_button_label,
(int)buttonStyles);
std::vector<std::u16string> paragraphs;
for (const auto& field :
dialog_model_->fields(DialogModelHost::GetPassKey())) {
switch (field->type()) {
case DialogModelField::kParagraph: {
paragraphs.push_back(getMessageParagraph(field.get()));
break;
}
default:
NOTREACHED() << "Unsupported DialogModel field type " << field->type()
<< ". Support should be added before this dialog is used "
"in android";
}
}
if (paragraphs.size() > 0) {
ScopedJavaLocalRef<jobjectArray> java_paragraphs_array =
base::android::ToJavaArrayOfStrings(env, paragraphs);
Java_ModalDialogWrapper_withMessageParagraphs(env, java_obj_,
java_paragraphs_array);
}
}
void ModalDialogWrapper::PositiveButtonClicked(JNIEnv* env) {
dialog_model_->OnDialogAcceptAction(DialogModelHost::GetPassKey());
}
void ModalDialogWrapper::NegativeButtonClicked(JNIEnv* env) {
dialog_model_->OnDialogCancelAction(DialogModelHost::GetPassKey());
}
void ModalDialogWrapper::Dismissed(JNIEnv* env) {
dialog_model_->OnDialogCloseAction(DialogModelHost::GetPassKey());
}
void ModalDialogWrapper::Destroy(JNIEnv* env) {
delete this;
}
void ModalDialogWrapper::Close() {
auto* dialog_manager = window_android_->GetModalDialogManagerBridge();
CHECK(dialog_manager) << "The destruction of the ModalDialogManager.java "
"should also destroy this dialog wrapper.";
dialog_manager->DismissDialog(java_obj_);
}
void ModalDialogWrapper::OnDialogButtonChanged() {}
} // namespace ui