blob: 77d4297c224d56b1d1c888742ce5424e492a2add [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/android/webid/account_selection_view_android.h"
#include <memory>
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/strings/string_piece.h"
#include "chrome/browser/ui/android/webid/jni_headers/AccountSelectionBridge_jni.h"
#include "chrome/browser/ui/android/webid/jni_headers/Account_jni.h"
#include "chrome/browser/ui/android/webid/jni_headers/ClientIdMetadata_jni.h"
#include "chrome/browser/ui/android/webid/jni_headers/IdentityProviderMetadata_jni.h"
#include "chrome/browser/ui/webid/account_selection_view.h"
#include "content/public/browser/identity_request_dialog_controller.h"
#include "ui/android/color_utils_android.h"
#include "ui/android/window_android.h"
#include "url/android/gurl_android.h"
#include "url/gurl.h"
using base::android::AttachCurrentThread;
using base::android::ConvertJavaStringToUTF8;
using base::android::ConvertUTF8ToJavaString;
using base::android::JavaParamRef;
using base::android::ScopedJavaLocalRef;
using DismissReason = content::IdentityRequestDialogController::DismissReason;
namespace {
ScopedJavaLocalRef<jobject> ConvertToJavaAccount(JNIEnv* env,
const Account& account) {
return Java_Account_Constructor(
env, ConvertUTF8ToJavaString(env, account.id),
ConvertUTF8ToJavaString(env, account.email),
ConvertUTF8ToJavaString(env, account.name),
ConvertUTF8ToJavaString(env, account.given_name),
url::GURLAndroid::FromNativeGURL(env, account.picture),
account.login_state == Account::LoginState::kSignIn);
}
ScopedJavaLocalRef<jobject> ConvertToJavaIdentityProviderMetadata(
JNIEnv* env,
const content::IdentityProviderMetadata& metadata) {
ScopedJavaLocalRef<jstring> java_brand_icon_url =
base::android::ConvertUTF8ToJavaString(env,
metadata.brand_icon_url.spec());
return Java_IdentityProviderMetadata_Constructor(
env, ui::OptionalSkColorToJavaColor(metadata.brand_text_color),
ui::OptionalSkColorToJavaColor(metadata.brand_background_color),
java_brand_icon_url,
url::GURLAndroid::FromNativeGURL(env, metadata.config_url));
}
ScopedJavaLocalRef<jobject> ConvertToJavaClientIdMetadata(
JNIEnv* env,
const content::ClientMetadata& metadata) {
return Java_ClientIdMetadata_Constructor(
env, url::GURLAndroid::FromNativeGURL(env, metadata.terms_of_service_url),
url::GURLAndroid::FromNativeGURL(env, metadata.privacy_policy_url));
}
ScopedJavaLocalRef<jobjectArray> ConvertToJavaAccounts(
JNIEnv* env,
const std::vector<Account>& accounts) {
ScopedJavaLocalRef<jclass> account_clazz = base::android::GetClass(
env, "org/chromium/chrome/browser/ui/android/webid/data/Account");
ScopedJavaLocalRef<jobjectArray> array(
env, env->NewObjectArray(accounts.size(), account_clazz.obj(), nullptr));
base::android::CheckException(env);
for (size_t i = 0; i < accounts.size(); ++i) {
ScopedJavaLocalRef<jobject> item = ConvertToJavaAccount(env, accounts[i]);
env->SetObjectArrayElement(array.obj(), i, item.obj());
}
return array;
}
Account ConvertFieldsToAccount(
JNIEnv* env,
const JavaParamRef<jobjectArray>& string_fields_obj,
const JavaParamRef<jobject>& picture_url_obj,
bool is_sign_in) {
std::vector<std::string> string_fields;
AppendJavaStringArrayToStringVector(env, string_fields_obj, &string_fields);
auto account_id = string_fields[0];
auto email = string_fields[1];
auto name = string_fields[2];
auto given_name = string_fields[3];
Account::LoginState login_state =
is_sign_in ? Account::LoginState::kSignIn : Account::LoginState::kSignUp;
GURL picture_url = *url::GURLAndroid::ToNativeGURL(env, picture_url_obj);
return Account(account_id, email, name, given_name, picture_url, login_state);
}
} // namespace
AccountSelectionViewAndroid::AccountSelectionViewAndroid(
AccountSelectionView::Delegate* delegate)
: AccountSelectionView(delegate) {}
AccountSelectionViewAndroid::~AccountSelectionViewAndroid() {
if (java_object_internal_) {
// Don't create an object just for destruction.
Java_AccountSelectionBridge_destroy(AttachCurrentThread(),
java_object_internal_);
}
}
void AccountSelectionViewAndroid::Show(
const std::string& top_frame_for_display,
const absl::optional<std::string>& iframe_url_for_display,
const std::vector<content::IdentityProviderData>& identity_provider_data,
Account::SignInMode sign_in_mode,
bool show_auto_reauthn_checkbox) {
if (!RecreateJavaObject()) {
// It's possible that the constructor cannot access the bottom sheet clank
// component. That case may be temporary but we can't let users in a
// waiting state so report that AccountSelectionView is dismissed instead.
delegate_->OnDismiss(DismissReason::kOther);
return;
}
// Serialize the `identity_provider_data.accounts` into a Java array and
// instruct the bridge to show it together with |url| to the user.
JNIEnv* env = AttachCurrentThread();
// Multi IDP support does not currently work on mobile. Hence, we use the
// first index from the `identity_provider_data` for the IDP-specific
// information.
ScopedJavaLocalRef<jobjectArray> accounts_obj =
ConvertToJavaAccounts(env, identity_provider_data[0].accounts);
ScopedJavaLocalRef<jobject> idp_metadata_obj =
ConvertToJavaIdentityProviderMetadata(
env, identity_provider_data[0].idp_metadata);
ScopedJavaLocalRef<jobject> client_id_metadata_obj =
ConvertToJavaClientIdMetadata(env,
identity_provider_data[0].client_metadata);
Java_AccountSelectionBridge_showAccounts(
env, java_object_internal_,
ConvertUTF8ToJavaString(env, top_frame_for_display),
ConvertUTF8ToJavaString(env, iframe_url_for_display.value_or("")),
ConvertUTF8ToJavaString(env, identity_provider_data[0].idp_for_display),
accounts_obj, idp_metadata_obj, client_id_metadata_obj,
sign_in_mode == Account::SignInMode::kAuto);
}
void AccountSelectionViewAndroid::ShowFailureDialog(
const std::string& top_frame_for_display,
const std::string& idp_for_display,
const content::IdentityProviderMetadata& idp_metadata) {
// TODO(crbug.com/1357790): add support on Android.
}
std::string AccountSelectionViewAndroid::GetTitle() const {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jstring> title =
Java_AccountSelectionBridge_getTitle(env, java_object_internal_);
CHECK(title);
return ConvertJavaStringToUTF8(title);
}
absl::optional<std::string> AccountSelectionViewAndroid::GetSubtitle() const {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jstring> subtitle =
Java_AccountSelectionBridge_getSubtitle(env, java_object_internal_);
if (!subtitle) {
return absl::nullopt;
}
return ConvertJavaStringToUTF8(subtitle);
}
void AccountSelectionViewAndroid::OnAccountSelected(
JNIEnv* env,
const JavaParamRef<jobject>& idp_config_url,
const JavaParamRef<jobjectArray>& account_string_fields,
const JavaParamRef<jobject>& account_picture_url,
bool is_sign_in) {
GURL config_url = *url::GURLAndroid::ToNativeGURL(env, idp_config_url);
delegate_->OnAccountSelected(
config_url, ConvertFieldsToAccount(env, account_string_fields,
account_picture_url, is_sign_in));
// The AccountSelectionViewAndroid may be destroyed.
// AccountSelectionView::Delegate::OnAccountSelected() might delete this.
// See https://crbug.com/1393650 for details.
}
void AccountSelectionViewAndroid::OnDismiss(JNIEnv* env, jint dismiss_reason) {
delegate_->OnDismiss(static_cast<DismissReason>(dismiss_reason));
}
bool AccountSelectionViewAndroid::RecreateJavaObject() {
if (delegate_->GetNativeView() == nullptr ||
delegate_->GetNativeView()->GetWindowAndroid() == nullptr) {
return false; // No window attached (yet or anymore).
}
if (java_object_internal_) {
Java_AccountSelectionBridge_destroy(AttachCurrentThread(),
java_object_internal_);
}
java_object_internal_ = Java_AccountSelectionBridge_create(
AttachCurrentThread(), reinterpret_cast<intptr_t>(this),
delegate_->GetNativeView()->GetWindowAndroid()->GetJavaObject());
return !!java_object_internal_;
}
// static
std::unique_ptr<AccountSelectionView> AccountSelectionView::Create(
AccountSelectionView::Delegate* delegate) {
return std::make_unique<AccountSelectionViewAndroid>(delegate);
}
// static
int AccountSelectionView::GetBrandIconMinimumSize() {
return Java_AccountSelectionBridge_getBrandIconMinimumSize(
base::android::AttachCurrentThread());
}
// static
int AccountSelectionView::GetBrandIconIdealSize() {
return Java_AccountSelectionBridge_getBrandIconIdealSize(
base::android::AttachCurrentThread());
}