// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/contacts/contacts_provider_android.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/android/jni_bytebuffer.h"
#include "base/android/jni_string.h"
#include "base/functional/callback.h"
#include "base/metrics/histogram_functions.h"
#include "components/url_formatter/elide_url.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/public/browser/contacts_picker_properties.h"
#include "content/public/browser/web_contents.h"
#include "url/origin.h"
// Must come after all headers that specialize FromJniType() / ToJniType().
#include "content/public/android/content_jni_headers/ContactsDialogHost_jni.h"
namespace content {
ContactsProviderAndroid::ContactsProviderAndroid(
RenderFrameHostImpl* render_frame_host) {
JNIEnv* env = base::android::AttachCurrentThread();
WebContents* web_contents =
WebContents::FromRenderFrameHost(render_frame_host);
if (!web_contents || !web_contents->GetTopLevelNativeWindow())
return;
formatted_origin_ = url_formatter::FormatUrlForSecurityDisplay(
render_frame_host->GetLastCommittedOrigin().GetURL(),
url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC);
dialog_.Reset(
Java_ContactsDialogHost_create(env, web_contents->GetJavaWebContents(),
reinterpret_cast<intptr_t>(this)));
DCHECK(!dialog_.is_null());
}
ContactsProviderAndroid::~ContactsProviderAndroid() {
JNIEnv* env = base::android::AttachCurrentThread();
Java_ContactsDialogHost_destroy(env, dialog_);
}
void ContactsProviderAndroid::Select(bool multiple,
bool include_names,
bool include_emails,
bool include_tel,
bool include_addresses,
bool include_icons,
ContactsSelectedCallback callback) {
if (!dialog_) {
std::move(callback).Run(std::nullopt, /*percentage_shared=*/-1,
PROPERTIES_NONE);
return;
}
callback_ = std::move(callback);
JNIEnv* env = base::android::AttachCurrentThread();
Java_ContactsDialogHost_showDialog(
env, dialog_, multiple, include_names, include_emails, include_tel,
include_addresses, include_icons,
base::android::ConvertUTF16ToJavaString(env, formatted_origin_));
}
void ContactsProviderAndroid::AddContact(
JNIEnv* env,
const base::android::JavaParamRef<jobjectArray>& names_java,
const base::android::JavaParamRef<jobjectArray>& emails_java,
const base::android::JavaParamRef<jobjectArray>& tel_java,
const base::android::JavaParamRef<jobjectArray>& addresses_java,
const base::android::JavaParamRef<jobjectArray>& icons_java) {
DCHECK(callback_);
std::optional<std::vector<std::string>> names;
if (names_java) {
std::vector<std::string> names_vector;
base::android::AppendJavaStringArrayToStringVector(env, names_java,
&names_vector);
names = std::move(names_vector);
}
std::optional<std::vector<std::string>> emails;
if (emails_java) {
std::vector<std::string> emails_vector;
base::android::AppendJavaStringArrayToStringVector(env, emails_java,
&emails_vector);
emails = std::move(emails_vector);
}
std::optional<std::vector<std::string>> tel;
if (tel_java) {
std::vector<std::string> tel_vector;
base::android::AppendJavaStringArrayToStringVector(env, tel_java,
&tel_vector);
tel = std::move(tel_vector);
}
std::optional<std::vector<payments::mojom::PaymentAddressPtr>> addresses;
if (addresses_java) {
std::vector<payments::mojom::PaymentAddressPtr> addresses_vector;
for (const base::android::JavaRef<jbyteArray>& j_address :
addresses_java.ReadElements<jbyteArray>()) {
payments::mojom::PaymentAddressPtr address;
base::span<const uint8_t> address_bytes =
base::android::JavaByteBufferToSpan(env, j_address.obj());
if (!payments::mojom::PaymentAddress::Deserialize(
address_bytes.data(), address_bytes.size(), &address)) {
continue;
}
addresses_vector.push_back(std::move(address));
}
addresses = std::move(addresses_vector);
}
std::optional<std::vector<blink::mojom::ContactIconBlobPtr>> icons;
if (icons_java) {
std::vector<blink::mojom::ContactIconBlobPtr> icons_vector;
for (const base::android::JavaRef<jbyteArray>& j_icon :
icons_java.ReadElements<jbyteArray>()) {
blink::mojom::ContactIconBlobPtr icon;
base::span<const uint8_t> icon_bytes =
base::android::JavaByteBufferToSpan(env, j_icon.obj());
if (!blink::mojom::ContactIconBlob::Deserialize(
icon_bytes.data(), icon_bytes.size(), &icon)) {
continue;
}
icons_vector.push_back(std::move(icon));
}
icons = std::move(icons_vector);
}
blink::mojom::ContactInfoPtr contact = blink::mojom::ContactInfo::New(
std::move(names), std::move(emails), std::move(tel), std::move(addresses),
std::move(icons));
contacts_.push_back(std::move(contact));
}
void ContactsProviderAndroid::EndContactsList(JNIEnv* env,
jint percentage_shared,
jint properties_requested) {
DCHECK(callback_);
ContactsPickerProperties properties =
static_cast<ContactsPickerProperties>(properties_requested);
std::move(callback_).Run(std::move(contacts_), percentage_shared, properties);
}
void ContactsProviderAndroid::EndWithPermissionDenied(JNIEnv* env) {
DCHECK(callback_);
std::move(callback_).Run(std::nullopt, /*percentage_shared=*/-1,
PROPERTIES_NONE);
}
} // namespace content