* Copyright (c) 2022-2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "adapter/android/capability/java/jni/clipboard/clipboard_jni.h"
#include "adapter/android/capability/java/jni/clipboard/clipboard_proxy_impl.h"
#include "adapter/android/entrance/java/jni/jni_environment.h"
#include "base/json/json_util.h"
#include "base/log/log.h"
#include "base/utils/utils.h"
#include "core/common/clipboard/clipboard_proxy.h"
namespace OHOS::Ace::Platform {
namespace {
static const char CLIPBOARD_PLUGIN_CLASS_NAME[] = "ohos/ace/adapter/capability/clipboard/ClipboardPluginBase";
static const JNINativeMethod METHODS[] = {
{ .name = "nativeInit", .signature = "()V", .fnPtr = reinterpret_cast<void*>(ClipboardJni::NativeInit) },
};
static const char METHOD_SET_DATA[] = "setData";
static const char METHOD_GET_DATA[] = "getData";
static const char METHOD_HAS_DATA[] = "hasData";
static const char METHOD_CLEAR[] = "clear";
static const char METHOD_IS_MULTI_TYPE_DATA[] = "isMultiTypeData";
static const char METHOD_SET_MULTI_TYPE_DATA[] = "setMultiTypeData";
static const char METHOD_GET_MULTI_TYPE_DATA[] = "getMultiTypeData";
static const char SIGNATURE_SET_DATA[] = "(Ljava/lang/String;)V";
static const char SIGNATURE_GET_DATA[] = "()Ljava/lang/String;";
static const char SIGNATURE_HAS_DATA[] = "()Z";
static const char SIGNATURE_CLEAR[] = "()V";
static const char SIGNATURE_IS_MULTI_TYPE_DATA[] = "()Z";
static const char SIGNATURE_SET_MULTI_TYPE_DATA[] = "(Ljava/lang/String;)V";
static const char SIGNATURE_GET_MULTI_TYPE_DATA[] = "()Ljava/lang/String;";
JniEnvironment::JavaGlobalRef g_clipboardObj(nullptr, nullptr);
struct {
jmethodID setData;
jmethodID getData;
jmethodID hasData;
jmethodID clear;
jmethodID isMultiTypeData;
jmethodID setMultiTypeData;
jmethodID getMultiTypeData;
} g_pluginMethods;
}
bool ClipboardJni::Register(std::shared_ptr<JNIEnv> env)
{
if (!env) {
LOGW("Clipborad JNI: null env");
return false;
}
jclass clazz = env->FindClass(CLIPBOARD_PLUGIN_CLASS_NAME);
if (clazz == nullptr) {
LOGW("Clipborad JNI: class not found");
return false;
}
bool ret = env->RegisterNatives(clazz, METHODS, ArraySize(METHODS)) == 0;
env->DeleteLocalRef(clazz);
if (!ret) {
LOGE("Clipborad JNI: register failed");
return false;
}
OnJniRegistered();
return true;
}
void ClipboardJni::OnJniRegistered()
{
ClipboardProxy::GetInstance()->SetDelegate(std::make_unique<ClipboardProxyImpl>());
}
void ClipboardJni::NativeInit(JNIEnv* env, jobject object)
{
if (!env) {
LOGW("Clipborad JNI Init: null env");
return;
}
g_clipboardObj = JniEnvironment::MakeJavaGlobalRef(JniEnvironment::GetInstance().GetJniEnv(), object);
jclass clazz = env->GetObjectClass(object);
if (clazz == nullptr) {
LOGE("Clipborad JNI Init: class not found");
return;
}
g_pluginMethods.setData = env->GetMethodID(clazz, METHOD_SET_DATA, SIGNATURE_SET_DATA);
if (!g_pluginMethods.setData) {
LOGW("Clipborad JNI Init: setData method not found");
}
g_pluginMethods.getData = env->GetMethodID(clazz, METHOD_GET_DATA, SIGNATURE_GET_DATA);
if (!g_pluginMethods.getData) {
LOGW("Clipborad JNI Init: getData method not found");
}
g_pluginMethods.hasData = env->GetMethodID(clazz, METHOD_HAS_DATA, SIGNATURE_HAS_DATA);
if (!g_pluginMethods.hasData) {
LOGW("Clipborad JNI Init: hasData method not found");
}
g_pluginMethods.clear = env->GetMethodID(clazz, METHOD_CLEAR, SIGNATURE_CLEAR);
if (!g_pluginMethods.clear) {
LOGW("Clipborad JNI Init: clear method not found");
}
g_pluginMethods.isMultiTypeData = env->GetMethodID(clazz, METHOD_IS_MULTI_TYPE_DATA, SIGNATURE_IS_MULTI_TYPE_DATA);
if (!g_pluginMethods.isMultiTypeData) {
LOGW("Clipborad JNI Init: isMultiTypeData method not found");
}
g_pluginMethods.setMultiTypeData =
env->GetMethodID(clazz, METHOD_SET_MULTI_TYPE_DATA, SIGNATURE_SET_MULTI_TYPE_DATA);
if (!g_pluginMethods.setMultiTypeData) {
LOGW("Clipborad JNI Init: setMultiTypeData method not found");
}
g_pluginMethods.getMultiTypeData =
env->GetMethodID(clazz, METHOD_GET_MULTI_TYPE_DATA, SIGNATURE_GET_MULTI_TYPE_DATA);
if (!g_pluginMethods.getMultiTypeData) {
LOGW("Clipborad JNI Init: getMultiTypeData method not found");
}
env->DeleteLocalRef(clazz);
}
bool ClipboardJni::SetData(const std::string& data)
{
auto env = JniEnvironment::GetInstance().GetJniEnv();
if (!env) {
LOGW("Clipborad JNI: null env");
return false;
}
if (!g_clipboardObj || !g_pluginMethods.setData) {
LOGW("Clipborad JNI: null setData method");
return false;
}
jstring jData = env->NewStringUTF(data.c_str());
env->CallVoidMethod(g_clipboardObj.get(), g_pluginMethods.setData, jData);
if (jData != nullptr) {
env->DeleteLocalRef(jData);
}
if (env->ExceptionCheck()) {
LOGE("Clipborad JNI: call setData has exception");
env->ExceptionDescribe();
env->ExceptionClear();
return false;
}
return true;
}
bool ClipboardJni::GetData(
const std::function<void(const std::string&, bool isFromAutoFill)>& callback, const WeakPtr<TaskExecutor>& taskExecutor)
{
auto env = JniEnvironment::GetInstance().GetJniEnv();
if (!env) {
LOGW("Clipborad JNI: null env");
return false;
}
if (!g_clipboardObj || !g_pluginMethods.getData) {
LOGW("Clipborad JNI: null getData method");
return false;
}
std::string result;
jstring jData = static_cast<jstring>(env->CallObjectMethod(g_clipboardObj.get(), g_pluginMethods.getData));
if (env->ExceptionCheck()) {
LOGE("Clipborad JNI: call getData has exception");
env->ExceptionDescribe();
env->ExceptionClear();
return false;
}
const char* content = env->GetStringUTFChars(jData, nullptr);
if (content != nullptr) {
result.assign(content);
env->ReleaseStringUTFChars(jData, content);
}
if (jData != nullptr) {
env->DeleteLocalRef(jData);
}
auto executor = taskExecutor.Upgrade();
if (executor) {
executor->PostTask(
[callback, result] {
if (callback) {
callback(result, false);
}
},
TaskExecutor::TaskType::UI, "ArkUI-XClipboardJniGetData");
}
return true;
}
bool ClipboardJni::GetData(
const std::function<void(const std::string&)>& callback, const WeakPtr<TaskExecutor>& taskExecutor)
{
auto env = JniEnvironment::GetInstance().GetJniEnv();
if (!env) {
LOGW("Clipborad JNI: null env");
return false;
}
if (!g_clipboardObj || !g_pluginMethods.getData) {
LOGW("Clipborad JNI: null getData method");
return false;
}
std::string result;
jstring jData = static_cast<jstring>(env->CallObjectMethod(g_clipboardObj.get(), g_pluginMethods.getData));
if (env->ExceptionCheck()) {
LOGE("Clipborad JNI: call getData has exception");
env->ExceptionDescribe();
env->ExceptionClear();
return false;
}
const char* content = env->GetStringUTFChars(jData, nullptr);
if (content != nullptr) {
result.assign(content);
env->ReleaseStringUTFChars(jData, content);
}
if (jData != nullptr) {
env->DeleteLocalRef(jData);
}
auto executor = taskExecutor.Upgrade();
if (executor) {
executor->PostTask(
[callback, result] {
if (callback) {
callback(result);
}
},
TaskExecutor::TaskType::UI, "ArkUI-XClipboardJniGetData");
}
return true;
}
std::string ClipboardJni::GetData()
{
auto env = JniEnvironment::GetInstance().GetJniEnv();
CHECK_NULL_RETURN(env, "");
CHECK_NULL_RETURN(g_clipboardObj, "");
CHECK_NULL_RETURN(g_pluginMethods.getData, "");
std::string result = "";
jstring jData = static_cast<jstring>(env->CallObjectMethod(g_clipboardObj.get(), g_pluginMethods.getData));
if (env->ExceptionCheck()) {
LOGE("Clipborad JNI: call getData has exception");
env->ExceptionDescribe();
env->ExceptionClear();
return "";
}
const char* content = env->GetStringUTFChars(jData, nullptr);
if (content != nullptr) {
result.assign(content);
env->ReleaseStringUTFChars(jData, content);
}
if (jData != nullptr) {
env->DeleteLocalRef(jData);
}
return result;
}
bool ClipboardJni::HasData(const std::function<void(bool hasData, bool isAutoFill)>& callback,
const WeakPtr<TaskExecutor>& taskExecutor)
{
auto env = JniEnvironment::GetInstance().GetJniEnv();
if (!env) {
LOGW("Clipborad JNI: null env");
return false;
}
if (!g_clipboardObj || !g_pluginMethods.hasData) {
LOGW("Clipborad JNI: null hasData method");
return false;
}
bool jResult = static_cast<bool>(env->CallBooleanMethod(g_clipboardObj.get(), g_pluginMethods.hasData));
if (env->ExceptionCheck()) {
LOGE("Clipborad JNI: call hasData has exception");
env->ExceptionDescribe();
env->ExceptionClear();
return false;
}
auto executor = taskExecutor.Upgrade();
if (executor) {
executor->PostTask(
[callback, jResult] {
if (callback) {
bool isAutoFill = false;
callback(jResult, isAutoFill);
}
},
TaskExecutor::TaskType::UI, "ArkUI-XClipboardJniHasData");
}
return true;
}
bool ClipboardJni::HasPasteData()
{
auto env = JniEnvironment::GetInstance().GetJniEnv();
CHECK_NULL_RETURN(env, false);
CHECK_NULL_RETURN(g_clipboardObj, false);
CHECK_NULL_RETURN(g_pluginMethods.hasData, false);
bool jResult = static_cast<bool>(env->CallBooleanMethod(g_clipboardObj.get(), g_pluginMethods.hasData));
if (env->ExceptionCheck()) {
LOGE("Clipborad JNI: call HasPasteData has exception");
env->ExceptionDescribe();
env->ExceptionClear();
return false;
}
return jResult;
}
bool ClipboardJni::Clear()
{
auto env = JniEnvironment::GetInstance().GetJniEnv();
if (!env) {
LOGW("Clipborad JNI: null env");
return false;
}
if (!g_clipboardObj || !g_pluginMethods.clear) {
LOGW("Clipborad JNI: null clear method");
return false;
}
env->CallVoidMethod(g_clipboardObj.get(), g_pluginMethods.clear);
if (env->ExceptionCheck()) {
LOGE("Clipborad JNI: call clear has exception");
env->ExceptionDescribe();
env->ExceptionClear();
return false;
}
return true;
}
bool ClipboardJni::IsMultiTypeData()
{
auto env = JniEnvironment::GetInstance().GetJniEnv();
CHECK_NULL_RETURN(env, false);
CHECK_NULL_RETURN(g_clipboardObj, false);
CHECK_NULL_RETURN(g_pluginMethods.isMultiTypeData, false);
bool jResult = static_cast<bool>(env->CallBooleanMethod(g_clipboardObj.get(), g_pluginMethods.isMultiTypeData));
if (env->ExceptionCheck()) {
LOGE("Clipborad JNI: call IsMultiTypeData has exception");
env->ExceptionDescribe();
env->ExceptionClear();
return false;
}
return jResult;
}
std::string ClipboardJni::GetMultiTypeData()
{
auto env = JniEnvironment::GetInstance().GetJniEnv();
CHECK_NULL_RETURN(env, "");
CHECK_NULL_RETURN(g_clipboardObj, "");
CHECK_NULL_RETURN(g_pluginMethods.getMultiTypeData, "");
std::string result = "";
jstring jData = static_cast<jstring>(env->CallObjectMethod(g_clipboardObj.get(), g_pluginMethods.getMultiTypeData));
if (env->ExceptionCheck()) {
LOGE("Clipborad JNI: call GetMultiTypeData has exception");
env->ExceptionDescribe();
env->ExceptionClear();
return "";
}
const char* content = env->GetStringUTFChars(jData, nullptr);
if (content != nullptr) {
result.assign(content);
env->ReleaseStringUTFChars(jData, content);
}
if (jData != nullptr) {
env->DeleteLocalRef(jData);
}
return result;
}
void ClipboardJni::SetMultiTypeData(const std::string& data)
{
auto env = JniEnvironment::GetInstance().GetJniEnv();
CHECK_NULL_VOID(env);
CHECK_NULL_VOID(g_clipboardObj);
CHECK_NULL_VOID(g_pluginMethods.setMultiTypeData);
jstring jData = env->NewStringUTF(data.c_str());
env->CallVoidMethod(g_clipboardObj.get(), g_pluginMethods.setMultiTypeData, jData);
if (jData != nullptr) {
env->DeleteLocalRef(jData);
}
if (env->ExceptionCheck()) {
LOGE("Clipborad JNI: call SetMultiTypeData has exception");
env->ExceptionDescribe();
env->ExceptionClear();
return;
}
}
}