diff --git a/shell/platform/tizen/BUILD.gn b/shell/platform/tizen/BUILD.gn index 5cdac760a2ac3..cc3f416dd8d5a 100644 --- a/shell/platform/tizen/BUILD.gn +++ b/shell/platform/tizen/BUILD.gn @@ -87,7 +87,8 @@ source_set("flutter_tizen") { "$custom_sysroot/usr/include/eo-1", "$custom_sysroot/usr/include/evas-1", "$custom_sysroot/usr/include/system", - "$custom_sysroot/usr/include/wayland-extension" + "$custom_sysroot/usr/include/wayland-extension", + "$custom_sysroot/usr/include/cbhm", ] lib_dirs = [ root_out_dir, "$custom_sysroot/usr/lib" ] @@ -114,6 +115,7 @@ source_set("flutter_tizen") { "tbm", "tdm-client", "wayland-client", + "cbhm", ] if (tizen_sdk_4) { diff --git a/shell/platform/tizen/channels/platform_channel.cc b/shell/platform/tizen/channels/platform_channel.cc index b79f780af3a52..86dfdd22b33a3 100644 --- a/shell/platform/tizen/channels/platform_channel.cc +++ b/shell/platform/tizen/channels/platform_channel.cc @@ -4,13 +4,25 @@ #include "platform_channel.h" +#include #include +#include #include "flutter/shell/platform/common/cpp/json_method_codec.h" #include "flutter/shell/platform/tizen/tizen_log.h" static constexpr char kChannelName[] = "flutter/platform"; +// Clipboard.getData constants and variables +std::mutex is_processing_mutex; +static bool is_processing = false; +static constexpr char kTextKey[] = "text"; +static constexpr char kTextPlainFormat[] = "text/plain"; +static constexpr char kUnknownClipboardFormatError[] = + "Unknown clipboard format error"; +static constexpr char kUnknownClipboardError[] = + "Unknown error during clipboard data retrieval"; + PlatformChannel::PlatformChannel(flutter::BinaryMessenger* messenger) : channel_(std::make_unique>( messenger, kChannelName, &flutter::JsonMethodCodec::GetInstance())) { @@ -37,7 +49,99 @@ void PlatformChannel::HandleMethodCall( } else if (method == "HapticFeedback.vibrate") { result->NotImplemented(); } else if (method == "Clipboard.getData") { - result->NotImplemented(); + const rapidjson::Value& format = call.arguments()[0]; + + // https://api.flutter.dev/flutter/services/Clipboard/kTextPlain-constant.html + // API supports only kTextPlain format, however cbhm API supports also other formats + if (strcmp(format.GetString(), kTextPlainFormat) != 0) { + result->Error(kUnknownClipboardFormatError, + "Clipboard API only supports text."); + return; + } + + // Report error on next calls until current will be finished. + // Native API - cbhm_selection_get works on static struct, so accessing clipboard parallelly will end + // with race regarding returning values - cbhm_selection_data_cb will be triggered only for latest call. + // TODO consider some queuing mechnism instead of returning error for next calls + { + std::lock_guard lock(is_processing_mutex); + if (is_processing) { + result->Error(kUnknownClipboardError, "Already processing by other thread."); + return; + } + is_processing = true; + } + + cbhm_sel_type_e selection_type = CBHM_SEL_TYPE_TEXT; + + cbhm_h cbhm_handle = nullptr; + int ret = cbhm_open_service (&cbhm_handle); + if (CBHM_ERROR_NONE != ret) { + result->Error(kUnknownClipboardError, "Failed to initialize cbhm service."); + return; + } + + // additional check if data in clipboard + ret = cbhm_item_count_get(cbhm_handle); + if (ret <= 0) { + result->Error(kUnknownClipboardError, "No clipboard data available."); + // release the data + cbhm_close_service (cbhm_handle); + // unlock guard for further processing + std::lock_guard lock(is_processing_mutex); + is_processing = false; + return; + } + + struct method_data_t { + std::unique_ptr> result; + cbhm_h cbhm_handle; + }; + // invalidates the result pointer + method_data_t* data = new method_data_t{}; + data->result = std::move(result); + data->cbhm_handle = cbhm_handle; + + auto cbhm_selection_data_cb = [](cbhm_h cbhm_handle, const char *buf, size_t len, void *user_data) -> int { + auto data = static_cast(user_data); + // move unique_ptr from method_data_t and then release memory + auto result = std::move(data->result); + cbhm_close_service (data->cbhm_handle); + delete data; + + FT_LOGD("cbhm_selection_get SUCCESS (%d) %s", len, buf); + { + std::lock_guard lock(is_processing_mutex); + is_processing = false; + } + if (buf) { + rapidjson::Document document; + document.SetObject(); + rapidjson::Document::AllocatorType& allocator = document.GetAllocator(); + document.AddMember(rapidjson::Value(kTextKey, allocator), + rapidjson::Value(std::string{buf, len}, allocator), allocator); + result->Success(document); + return CBHM_ERROR_NONE; + } else { + result->Error(kUnknownClipboardError, "Data buffer is null."); + return CBHM_ERROR_NO_DATA; + } + }; + + FT_LOGD("cbhm_selection_get call"); + ret = cbhm_selection_get(cbhm_handle, selection_type, cbhm_selection_data_cb, data); + if (CBHM_ERROR_NONE != ret) { + FT_LOGD("cbhm_selection_get error"); + // return error + data->result->Error(kUnknownClipboardError, "Failed to gather data."); + // release the data + cbhm_close_service (data->cbhm_handle); + delete data; + // unlock guard for further processing + std::lock_guard lock(is_processing_mutex); + is_processing = false; + return; + } } else if (method == "Clipboard.setData") { result->NotImplemented(); } else if (method == "Clipboard.hasStrings") {