// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/offline_items_collection/core/android/offline_content_aggregator_bridge.h"
#include <memory>
#include <utility>
#include <vector>
#include "base/android/callback_android.h"
#include "base/android/jni_string.h"
#include "base/functional/bind.h"
#include "base/memory/ptr_util.h"
#include "components/offline_items_collection/core/android/offline_item_bridge.h"
#include "components/offline_items_collection/core/android/offline_item_share_info_bridge.h"
#include "components/offline_items_collection/core/android/offline_item_visuals_bridge.h"
#include "components/offline_items_collection/core/offline_item.h"
#include "components/offline_items_collection/core/throttled_offline_content_provider.h"
// Must come after all headers that specialize FromJniType() / ToJniType().
#include "components/offline_items_collection/core/jni_headers/OfflineContentAggregatorBridge_jni.h"
using base::android::AttachCurrentThread;
using base::android::ConvertJavaStringToUTF8;
using base::android::ConvertUTF8ToJavaString;
using base::android::JavaParamRef;
using base::android::ScopedJavaGlobalRef;
using base::android::ScopedJavaLocalRef;
namespace offline_items_collection {
using GetVisualsOptions = OfflineContentProvider::GetVisualsOptions;
namespace android {
namespace {
const char kOfflineContentAggregatorBridgeUserDataKey[] = "aggregator_bridge";
ContentId JNI_OfflineContentAggregatorBridge_CreateContentId(
JNIEnv* env,
const JavaParamRef<jstring>& j_namespace,
const JavaParamRef<jstring>& j_id) {
return ContentId(ConvertJavaStringToUTF8(env, j_namespace),
ConvertJavaStringToUTF8(env, j_id));
}
// Helper callback that glues the Java-specific callback logic to the native
// VisualsCallback that is required by the OfflineContentProvider native class.
void GetVisualsForItemHelperCallback(
ScopedJavaGlobalRef<jobject> j_callback,
const ContentId& id,
std::unique_ptr<OfflineItemVisuals> visuals) {
JNIEnv* env = AttachCurrentThread();
Java_OfflineContentAggregatorBridge_onVisualsAvailable(
env, j_callback, ConvertUTF8ToJavaString(env, id.name_space),
ConvertUTF8ToJavaString(env, id.id),
OfflineItemVisualsBridge::CreateOfflineItemVisuals(env,
std::move(visuals)));
}
void ForwardShareInfoToJavaCallback(
ScopedJavaGlobalRef<jobject> j_callback,
const ContentId& id,
std::unique_ptr<OfflineItemShareInfo> shareInfo) {
JNIEnv* env = AttachCurrentThread();
Java_OfflineContentAggregatorBridge_onShareInfoAvailable(
env, j_callback, ConvertUTF8ToJavaString(env, id.name_space),
ConvertUTF8ToJavaString(env, id.id),
OfflineItemShareInfoBridge::CreateOfflineItemShareInfo(
env, std::move(shareInfo)));
}
void RenameItemCallback(ScopedJavaGlobalRef<jobject> j_callback,
RenameResult result) {
base::android::RunIntCallbackAndroid(j_callback,
static_cast<int32_t>(result));
}
void RunGetAllItemsCallback(const base::android::JavaRef<jobject>& j_callback,
const std::vector<OfflineItem>& items) {
JNIEnv* env = AttachCurrentThread();
base::android::RunObjectCallbackAndroid(
j_callback, OfflineItemBridge::CreateOfflineItemList(env, items));
}
void RunGetItemByIdCallback(const base::android::JavaRef<jobject>& j_callback,
const std::optional<OfflineItem>& item) {
JNIEnv* env = AttachCurrentThread();
base::android::RunObjectCallbackAndroid(
j_callback, item.has_value()
? OfflineItemBridge::CreateOfflineItem(env, item.value())
: nullptr);
}
} // namespace
// static
base::android::ScopedJavaLocalRef<jobject>
OfflineContentAggregatorBridge::GetBridgeForOfflineContentAggregator(
OfflineContentAggregator* aggregator) {
if (!aggregator->GetUserData(kOfflineContentAggregatorBridgeUserDataKey)) {
aggregator->SetUserData(
kOfflineContentAggregatorBridgeUserDataKey,
base::WrapUnique(new OfflineContentAggregatorBridge(aggregator)));
}
OfflineContentAggregatorBridge* bridge =
static_cast<OfflineContentAggregatorBridge*>(
aggregator->GetUserData(kOfflineContentAggregatorBridgeUserDataKey));
return ScopedJavaLocalRef<jobject>(bridge->java_ref_);
}
OfflineContentAggregatorBridge::OfflineContentAggregatorBridge(
OfflineContentAggregator* aggregator)
: provider_(std::make_unique<ThrottledOfflineContentProvider>(aggregator)) {
JNIEnv* env = AttachCurrentThread();
java_ref_.Reset(Java_OfflineContentAggregatorBridge_create(
env, reinterpret_cast<intptr_t>(this)));
provider_->AddObserver(this);
}
OfflineContentAggregatorBridge::~OfflineContentAggregatorBridge() {
// TODO(dtrainor): Do not need to unregister because in the destructor of the
// base class of OfflineContentAggregator. Is |observers_| already dead?
provider_->RemoveObserver(this);
Java_OfflineContentAggregatorBridge_onNativeDestroyed(AttachCurrentThread(),
java_ref_);
}
void OfflineContentAggregatorBridge::OpenItem(
JNIEnv* env,
const JavaParamRef<jobject>& jobj,
jint launch_location,
jboolean j_open_in_incognito,
const JavaParamRef<jstring>& j_namespace,
const JavaParamRef<jstring>& j_id) {
OpenParams open_params(static_cast<LaunchLocation>(launch_location));
open_params.open_in_incognito = j_open_in_incognito;
provider_->OpenItem(open_params,
JNI_OfflineContentAggregatorBridge_CreateContentId(
env, j_namespace, j_id));
}
void OfflineContentAggregatorBridge::RemoveItem(
JNIEnv* env,
const JavaParamRef<jobject>& jobj,
const JavaParamRef<jstring>& j_namespace,
const JavaParamRef<jstring>& j_id) {
provider_->RemoveItem(JNI_OfflineContentAggregatorBridge_CreateContentId(
env, j_namespace, j_id));
}
void OfflineContentAggregatorBridge::CancelDownload(
JNIEnv* env,
const JavaParamRef<jobject>& jobj,
const JavaParamRef<jstring>& j_namespace,
const JavaParamRef<jstring>& j_id) {
provider_->CancelDownload(JNI_OfflineContentAggregatorBridge_CreateContentId(
env, j_namespace, j_id));
}
void OfflineContentAggregatorBridge::PauseDownload(
JNIEnv* env,
const JavaParamRef<jobject>& jobj,
const JavaParamRef<jstring>& j_namespace,
const JavaParamRef<jstring>& j_guid) {
provider_->PauseDownload(JNI_OfflineContentAggregatorBridge_CreateContentId(
env, j_namespace, j_guid));
}
void OfflineContentAggregatorBridge::ResumeDownload(
JNIEnv* env,
const JavaParamRef<jobject>& jobj,
const JavaParamRef<jstring>& j_namespace,
const JavaParamRef<jstring>& j_id) {
provider_->ResumeDownload(JNI_OfflineContentAggregatorBridge_CreateContentId(
env, j_namespace, j_id));
}
void OfflineContentAggregatorBridge::GetItemById(
JNIEnv* env,
const JavaParamRef<jobject>& jobj,
const JavaParamRef<jstring>& j_namespace,
const JavaParamRef<jstring>& j_id,
const JavaParamRef<jobject>& jcallback) {
OfflineContentProvider::SingleItemCallback callback =
base::BindOnce(&RunGetItemByIdCallback,
base::android::ScopedJavaGlobalRef<jobject>(jcallback));
provider_->GetItemById(JNI_OfflineContentAggregatorBridge_CreateContentId(
env, j_namespace, j_id),
std::move(callback));
}
void OfflineContentAggregatorBridge::GetAllItems(
JNIEnv* env,
const JavaParamRef<jobject>& jobj,
const JavaParamRef<jobject>& jcallback) {
OfflineContentProvider::MultipleItemCallback callback =
base::BindOnce(&RunGetAllItemsCallback,
base::android::ScopedJavaGlobalRef<jobject>(jcallback));
provider_->GetAllItems(std::move(callback));
}
void OfflineContentAggregatorBridge::GetVisualsForItem(
JNIEnv* env,
const JavaParamRef<jobject>& jobj,
const JavaParamRef<jstring>& j_namespace,
const JavaParamRef<jstring>& j_id,
const JavaParamRef<jobject>& j_callback) {
provider_->GetVisualsForItem(
JNI_OfflineContentAggregatorBridge_CreateContentId(env, j_namespace,
j_id),
GetVisualsOptions::IconOnly(),
base::BindOnce(&GetVisualsForItemHelperCallback,
ScopedJavaGlobalRef<jobject>(env, j_callback)));
}
void OfflineContentAggregatorBridge::GetShareInfoForItem(
JNIEnv* env,
const JavaParamRef<jobject>& jobj,
const JavaParamRef<jstring>& j_namespace,
const JavaParamRef<jstring>& j_id,
const JavaParamRef<jobject>& j_callback) {
provider_->GetShareInfoForItem(
JNI_OfflineContentAggregatorBridge_CreateContentId(env, j_namespace,
j_id),
base::BindOnce(&ForwardShareInfoToJavaCallback,
ScopedJavaGlobalRef<jobject>(env, j_callback)));
}
void OfflineContentAggregatorBridge::RenameItem(
JNIEnv* env,
const JavaParamRef<jobject>& jobj,
const JavaParamRef<jstring>& j_namespace,
const JavaParamRef<jstring>& j_id,
const JavaParamRef<jstring>& j_name,
const JavaParamRef<jobject>& j_callback) {
base::OnceCallback<void(RenameResult)> callback = base::BindOnce(
&RenameItemCallback,
base::android::ScopedJavaGlobalRef<jobject>(env, j_callback));
provider_->RenameItem(JNI_OfflineContentAggregatorBridge_CreateContentId(
env, j_namespace, j_id),
ConvertJavaStringToUTF8(env, j_name),
std::move(callback));
}
void OfflineContentAggregatorBridge::OnItemsAdded(
const OfflineContentProvider::OfflineItemList& items) {
if (java_ref_.is_null())
return;
JNIEnv* env = AttachCurrentThread();
Java_OfflineContentAggregatorBridge_onItemsAdded(
env, java_ref_, OfflineItemBridge::CreateOfflineItemList(env, items));
}
void OfflineContentAggregatorBridge::OnItemRemoved(const ContentId& id) {
if (java_ref_.is_null())
return;
JNIEnv* env = AttachCurrentThread();
Java_OfflineContentAggregatorBridge_onItemRemoved(
env, java_ref_, ConvertUTF8ToJavaString(env, id.name_space),
ConvertUTF8ToJavaString(env, id.id));
}
void OfflineContentAggregatorBridge::OnItemUpdated(
const OfflineItem& item,
const std::optional<UpdateDelta>& update_delta) {
if (java_ref_.is_null())
return;
JNIEnv* env = AttachCurrentThread();
Java_OfflineContentAggregatorBridge_onItemUpdated(
env, java_ref_, OfflineItemBridge::CreateOfflineItem(env, item),
OfflineItemBridge::CreateUpdateDelta(env, update_delta));
}
void OfflineContentAggregatorBridge::OnContentProviderGoingDown() {
// TODO(crbug.com/40168774): This event is only needed for desktop Chrome,
// so we didn't add an onContentProviderGoingDown() method yet. If Java
// observers need to listen for this event in the future, we should add some
// plumbing here.
}
} // namespace android
} // namespace offline_items_collection