diff --git a/attachment/repos/dart.patch3 b/attachment/repos/dart.patch3 new file mode 100644 index 0000000000000000000000000000000000000000..b77a3a71ae1804c66d71a34cb7fa7075ce9a3a7b --- /dev/null +++ b/attachment/repos/dart.patch3 @@ -0,0 +1,58 @@ +diff --git a/runtime/bin/file_ohos.cc b/runtime/bin/file_ohos.cc +index 225b1d62c67..8ac60ad5e64 100644 +--- a/runtime/bin/file_ohos.cc ++++ b/runtime/bin/file_ohos.cc +@@ -92,6 +92,7 @@ MappedMemory* File::Map(MapType type, + // Try to allocate near the VM's binary. + hint = reinterpret_cast(&Dart_Initialize); + prot = PROT_READ | PROT_EXEC; ++ flags |= (MAP_JIT_OHOS | MAP_ANONYMOUS); + break; + case kReadWrite: + prot = PROT_READ | PROT_WRITE; +diff --git a/runtime/bin/virtual_memory_posix.cc b/runtime/bin/virtual_memory_posix.cc +index e061ab47ec9..6d6fd1064a8 100644 +--- a/runtime/bin/virtual_memory_posix.cc ++++ b/runtime/bin/virtual_memory_posix.cc +@@ -46,6 +46,12 @@ VirtualMemory* VirtualMemory::Allocate(intptr_t size, + } + #endif // defined(DART_HOST_OS_MACOS) + ++#if defined(DART_HOST_OS_OHOS) ++ if (is_executable) { ++ map_flags |= MAP_JIT_OHOS; ++ } ++#endif // defined(DART_HOST_OS_OHOS) ++ + // Some 64-bit microarchitectures store only the low 32-bits of targets as + // part of indirect branch prediction, predicting that the target's upper bits + // will be same as the call instruction's address. This leads to misprediction +diff --git a/runtime/platform/globals.h b/runtime/platform/globals.h +index ca859e2b421..7910c90618b 100644 +--- a/runtime/platform/globals.h ++++ b/runtime/platform/globals.h +@@ -111,6 +111,7 @@ + #elif defined(DART_TARGET_OS_OHOS) && defined(DART_RUNTIME_OS_OHOS) + + #define DART_HOST_OS_OHOS 1 ++#define MAP_JIT_OHOS 0x1000 + + #elif defined(__linux__) || defined(__FreeBSD__) + +diff --git a/runtime/vm/virtual_memory_posix.cc b/runtime/vm/virtual_memory_posix.cc +index 93c83c17852..a86d3b91db6 100644 +--- a/runtime/vm/virtual_memory_posix.cc ++++ b/runtime/vm/virtual_memory_posix.cc +@@ -422,6 +422,12 @@ VirtualMemory* VirtualMemory::AllocateAligned(intptr_t size, + } + #endif // defined(DART_HOST_OS_MACOS) + ++#if defined(DART_HOST_OS_OHOS) ++ if (is_executable) { ++ map_flags |= MAP_JIT_OHOS; ++ } ++#endif // defined(DART_HOST_OS_OHOS) ++ + void* hint = nullptr; + // Some 64-bit microarchitectures store only the low 32-bits of targets as + // part of indirect branch prediction, predicting that the target's upper bits diff --git a/attachment/scripts/config.json b/attachment/scripts/config.json index f97d846b1ed4d269addfe90c7304707c41d14e6d..848689d9fa261275f634cc758a5713627d017b85 100644 --- a/attachment/scripts/config.json +++ b/attachment/scripts/config.json @@ -23,6 +23,12 @@ "type": "patch", "file_path": "flutter/attachment/repos/dart.patch2" }, + { + "name": "dart3", + "target": "third_party/dart", + "type": "patch", + "file_path": "flutter/attachment/repos/dart.patch3" + }, { "name": "skia", "target": "third_party/skia", diff --git a/attachment/scripts/config_pre.json b/attachment/scripts/config_pre.json index f97d846b1ed4d269addfe90c7304707c41d14e6d..848689d9fa261275f634cc758a5713627d017b85 100644 --- a/attachment/scripts/config_pre.json +++ b/attachment/scripts/config_pre.json @@ -23,6 +23,12 @@ "type": "patch", "file_path": "flutter/attachment/repos/dart.patch2" }, + { + "name": "dart3", + "target": "third_party/dart", + "type": "patch", + "file_path": "flutter/attachment/repos/dart.patch3" + }, { "name": "skia", "target": "third_party/skia", diff --git a/attachment/scripts/ohos.py b/attachment/scripts/ohos.py index e5b89ec6a2686a10bebbd82248930284562c8c85..49f44d38657caa856a78706731ed16738aaf13d7 100644 --- a/attachment/scripts/ohos.py +++ b/attachment/scripts/ohos.py @@ -222,13 +222,16 @@ def harBuild(buildInfo, args): command += "--build_dir ./src/out/%s/obj/ohos/flutter_embedding " % buildOut command += "--build_type %s " % buildType command += "--output ./src/out/%s/flutter.har " % buildOut - command += "--native_lib ./src/out/%s/libflutter.so " % buildOut + if args.har_unstripped: + command += "--native_lib ./src/out/%s/so.unstripped/libflutter.so " % buildOut + else: + command += "--native_lib ./src/out/%s/libflutter.so " % buildOut if buildType == "profile": command += ( "--native_lib ./src/out/%s/gen/flutter/shell/vmservice/ohos/libs/%s/libvmservice_snapshot.so " % (buildOut, buildInfo.abi) ) - command += "--ohos_abi %s " % "arm64-v8a" + command += "--ohos_abi %s " % buildInfo.abi command += "--ohos_api_int %s " % args.ohos_api_int runCommand(command) @@ -327,6 +330,9 @@ def addParseParam(parser): parser.add_argument( "--ohos_api_int", type=int, choices=[11, 12], default=12, help="Ohos api int." ) + parser.add_argument( + "--har-unstripped", action="store_true", help="Use so.unstripped or not." + ) def updateCode(args): diff --git a/attachment/scripts/ohos_create_flutter_har.py b/attachment/scripts/ohos_create_flutter_har.py index a70f33af01732264a5585865389abe30d0ce15b7..d33cda8c885bae4320bee71b3f08f0a1d2bfb9c0 100644 --- a/attachment/scripts/ohos_create_flutter_har.py +++ b/attachment/scripts/ohos_create_flutter_har.py @@ -16,6 +16,7 @@ """Create a HAR incorporating all the components required to build a Flutter application""" import argparse +import datetime import logging import os import re @@ -89,6 +90,34 @@ def updateConfig(buildDir, apiInt): file.write(HVIGOR_CONFIG) +# 自动更新flutter.har的版本号,把日期加到末尾。如: 1.0.0-20240731 +def updateVersion(buildDir): + filePath = os.path.join(buildDir, "flutter", "oh-package.json5") + + with open(filePath, "r") as sources: + lines = sources.readlines() + + pattern = r"\d+\.(?:\d+\.)*\d+" + with open(filePath, "w") as sources: + for line in lines: + if "version" in line: + matches = re.findall(pattern, line) + print(f'matches = {matches}') + if matches and len(matches) > 0: + result = ''.join(matches[0]) + versionArr = result.split("-") + today = datetime.date.today() + dateStr = today.strftime("%Y%m%d") + list = [versionArr[0], dateStr] + versionStr = "-".join(list) + print(f'versionStr = {versionStr}') + sources.write(re.sub(pattern, versionStr, line)) + else: + sources.write(line) + else: + sources.write(line) + + # 执行命令 def runCommand(command, checkCode=True, timeout=None): logging.info("runCommand start, command = %s" % (command)) @@ -104,6 +133,7 @@ def runCommand(command, checkCode=True, timeout=None): # 编译har文件,通过hvigorw的命令行参数指定编译类型(debug/release/profile) def buildHar(buildDir, apiInt, buildType): updateConfig(buildDir, apiInt) + updateVersion(buildDir) hvigorwCommand = "hvigorw" if apiInt != 11 else (".%shvigorw" % os.sep) runCommand( "cd %s && %s clean --mode module " % (buildDir, hvigorwCommand) diff --git a/fml/BUILD.gn b/fml/BUILD.gn index ddcb7cb64b195e140aa02c5fd69c6cd95fce8966..b8b6c6c5ceaf5c84d22622c15fb963cc2102483f 100644 --- a/fml/BUILD.gn +++ b/fml/BUILD.gn @@ -204,6 +204,7 @@ source_set("fml") { libs += [ "hilog_ndk.z" ] libs += [ "ace_napi.z" ] libs += [ "uv" ] + libs += [ "hitrace_ndk.z" ] }else if (is_linux) { sources += [ "platform/linux/message_loop_linux.cc", diff --git a/fml/platform/ohos/napi_util.cc b/fml/platform/ohos/napi_util.cc index e0439f192bca7d7e067c1b3b5ee447e16a8428e7..5d233ed337d240eeae88f07fec0371ff40b2262d 100644 --- a/fml/platform/ohos/napi_util.cc +++ b/fml/platform/ohos/napi_util.cc @@ -156,10 +156,19 @@ int32_t GetString(napi_env env, napi_value arg, std::string& strValue) { return ERROR_TYPE; } - std::vector buff(STRING_MAX_LENGTH); - size_t copy_lenth; - status = napi_get_value_string_utf8(env, arg, static_cast(buff.data()), - STRING_MAX_LENGTH, ©_lenth); + // 获取字符串长度 + size_t str_len; + status = napi_get_value_string_utf8(env, arg, nullptr, 0, &str_len); + if (status != napi_ok) { + FML_DLOG(ERROR) << "Error get str_len:" << status; + FML_DLOG(ERROR) << "result str_len:" << str_len; + return status; + } + + // 读取字符串 + size_t copy_lenth = str_len + 1; + std::vector buff(copy_lenth); + status = napi_get_value_string_utf8(env, arg, static_cast(buff.data()), copy_lenth, ©_lenth); if (status != napi_ok) { FML_DLOG(ERROR) << "Error get string:" << status; FML_DLOG(ERROR) << "result size:" << copy_lenth; diff --git a/fml/platform/ohos/napi_util.h b/fml/platform/ohos/napi_util.h index b0e264dec76fa8b04722478d5509472c7fb321ef..2acb4c3302f95cef8ca35617f0aec7141823c285 100644 --- a/fml/platform/ohos/napi_util.h +++ b/fml/platform/ohos/napi_util.h @@ -29,7 +29,7 @@ enum { ERROR_TYPE = -100, ERROR_NULL, }; -const int32_t STRING_MAX_LENGTH = 512; + int32_t GetString(napi_env env, napi_value arg, std::string& strValue); int32_t GetArrayString(napi_env env, napi_value arg, diff --git a/fml/trace_event.h b/fml/trace_event.h index d6749e61acb87515d58274214dc49fc4e5eb3ed1..505ad2e89a155f4f0dd481cfdc2c0b33bf9f769b 100644 --- a/fml/trace_event.h +++ b/fml/trace_event.h @@ -37,6 +37,80 @@ #endif // defined(OS_FUCHSIA) + +#if defined(FML_OS_OHOS) + +#include +#include +#include + +class OHFlutterTrace { +public: + explicit OHFlutterTrace(const char *fmt, ...) + { +//#if !defined(_WIN32) && !defined(_APPLE) + char title[1000] = "OHFlutterTrace"; + const char *title_ = title; + if (fmt != nullptr) { + va_list args; + va_start(args, fmt); + int32_t ret = vsnprintf(title, 1000, fmt, args); + va_end(args); + if (ret != -1) { + title_ = title; + } else { + title_ = "OHFlutterTraceFmt Format Error"; + } + } + OH_HiTrace_StartTrace(title_); +//#endif + } + ~OHFlutterTrace() + { +//#if !defined(_WIN32) && !defined(_APPLE) + OH_HiTrace_FinishTrace(); +//#endif + } +}; + +#define TRACE_NAME(x, y) x##y + +#define TRACE_DURATION_LINE(l, a, args...) OHFlutterTrace TRACE_NAME(trace, l)(a, args) + +#define TRACE_DURATION(a, args...) TRACE_DURATION_LINE(__LINE__, a, args) + + +#define FML_TRACE_EVENT(a, b, args...) +#define FML_TRACE_COUNTER(a, b, c, arg1, ...) + +#define TRACE_FLOW_BEGIN(category, name, id) OH_HiTrace_StartAsyncTrace(category#name, id) + +#define TRACE_FLOW_STEP(category, name, id) + +#define TRACE_FLOW_END(category, name, id) OH_HiTrace_FinishAsyncTrace(category#name, id) + +#define TRACE_FMT "%s:%s " +#define TRACE_EVENT0(a, b) TRACE_DURATION(TRACE_FMT, a, b) +#define TRACE_EVENT1(a, b, c, d) TRACE_DURATION(TRACE_FMT TRACE_FMT, a, b, c, d) +#define TRACE_EVENT2(a, b, c, d, e, f) TRACE_DURATION(TRACE_FMT TRACE_FMT TRACE_FMT TRACE_FMT, a, b, c, d, e, f) + +#define TRACE_EVENT_ASYNC_BEGIN0(a, b, c) OH_HiTrace_StartAsyncTrace(a#b, c) +#define TRACE_EVENT_ASYNC_END0(a, b, c) OH_HiTrace_FinishAsyncTrace(a#b, c) + +#define TRACE_EVENT_ASYNC_BEGIN1(a, b, c, d, e) +#define TRACE_EVENT_ASYNC_END1(a, b, c, d, e) + +#define TRACE_INSTANT(category_literal, name_literal, scope, args...) + +#define TRACE_EVENT_INSTANT0(a, b) TRACE_INSTANT(a, b, TRACE_SCOPE_THREAD) +#define TRACE_EVENT_INSTANT1(a, b, k1, v1) \ + TRACE_INSTANT(a, b, TRACE_SCOPE_THREAD, k1, v1) +#define TRACE_EVENT_INSTANT2(a, b, k1, v1, k2, v2) \ + TRACE_INSTANT(a, b, TRACE_SCOPE_THREAD, k1, v1, k2, v2) + +#endif // defined(FML_OS_OHOS) + + #include #include #include @@ -53,7 +127,7 @@ #define FLUTTER_TIMELINE_ENABLED 1 #endif -#if !defined(OS_FUCHSIA) +#if !defined(OS_FUCHSIA) && !defined(FML_OS_OHOS) #ifndef TRACE_EVENT_HIDE_MACROS #define __FML__TOKEN_CAT__(x, y) x##y diff --git a/lib/ui/window/platform_configuration.cc b/lib/ui/window/platform_configuration.cc index d4333231d468b28e8cf140f89519dc287940ed4f..5972d4aee99239553df92eb69c069d6c293b26ac 100644 --- a/lib/ui/window/platform_configuration.cc +++ b/lib/ui/window/platform_configuration.cc @@ -12,6 +12,7 @@ #include "flutter/lib/ui/window/platform_message_response_dart_port.h" #include "flutter/lib/ui/window/viewport_metrics.h" #include "flutter/lib/ui/window/window.h" +#include "flutter/fml/trace_event.h" #include "third_party/tonic/converter/dart_converter.h" #include "third_party/tonic/dart_args.h" #include "third_party/tonic/dart_library_natives.h" @@ -203,6 +204,7 @@ void PlatformConfiguration::DispatchSemanticsAction(int32_t id, void PlatformConfiguration::BeginFrame(fml::TimePoint frameTime, uint64_t frame_number) { + TRACE_EVENT0("flutter", "PlatformConfiguration::BeginFrame"); std::shared_ptr dart_state = begin_frame_.dart_state().lock(); if (!dart_state) { @@ -212,6 +214,7 @@ void PlatformConfiguration::BeginFrame(fml::TimePoint frameTime, int64_t microseconds = (frameTime - fml::TimePoint()).ToMicroseconds(); + TRACE_EVENT0("flutter", "PlatformConfiguration::begin_frame_"); tonic::CheckAndHandleError( tonic::DartInvoke(begin_frame_.Get(), { Dart_NewInteger(microseconds), @@ -220,6 +223,7 @@ void PlatformConfiguration::BeginFrame(fml::TimePoint frameTime, UIDartState::Current()->FlushMicrotasksNow(); + TRACE_EVENT0("flutter", "PlatformConfiguration::draw_frame_"); tonic::CheckAndHandleError(tonic::DartInvokeVoid(draw_frame_.Get())); } diff --git a/runtime/BUILD.gn b/runtime/BUILD.gn index 48ef5882eca4b31758065dc33e8238309a9dd575..0934cc2471aadcdf62371349b242469fe6d47bf6 100644 --- a/runtime/BUILD.gn +++ b/runtime/BUILD.gn @@ -84,7 +84,7 @@ source_set("runtime") { "skia_concurrent_executor.h", ] - if (is_ios && flutter_runtime_mode == "debug") { + if ((is_ios || is_ohos) && flutter_runtime_mode == "debug") { # These contain references to private APIs and this TU must only be compiled in debug runtime modes. sources += [ "ptrace_check.cc" ] } diff --git a/runtime/dart_vm.cc b/runtime/dart_vm.cc index b8b75719b3e4ebbc90f9f3465f8205644c36fee9..6f58b13b7428bd3c8a460a86ab1d708c0d6748af 100644 --- a/runtime/dart_vm.cc +++ b/runtime/dart_vm.cc @@ -342,7 +342,7 @@ DartVM::DartVM(const std::shared_ptr& vm_data, #endif // !OS_FUCHSIA #if (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG) -#if !FML_OS_IOS && !FML_OS_MACOSX +#if !FML_OS_IOS && !FML_OS_MACOSX && !FML_OS_OHOS // Debug mode uses the JIT, disable code page write protection to avoid // memory page protection changes before and after every compilation. PushBackAll(&args, kDartWriteProtectCodeArgs, diff --git a/runtime/ptrace_check.cc b/runtime/ptrace_check.cc index c9baa6d9c5b1ea3e58bcf3b95b63e178cfba0ec5..c0ed2a79c2fc292e447a080f4323c1e489d43631 100644 --- a/runtime/ptrace_check.cc +++ b/runtime/ptrace_check.cc @@ -24,6 +24,7 @@ #if TRACING_CHECKS_NECESSARY +#ifdef FML_OS_IOS #include #include @@ -145,4 +146,58 @@ TracingResult GetTracingResultImpl() { } // namespace flutter +#endif // FML_OS_IOS + + +#ifdef FML_OS_OHOS + +#include +#include + +#include + +#include "flutter/fml/build_config.h" + +// Being extra careful and adding additional landmines that will prevent +// compilation of this TU in an incorrect runtime mode. +static_assert(FML_OS_OHOS, "This translation unit is ohos specific."); +static_assert(FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG, + "This translation unit must only be compiled in the debug " + "runtime mode as it " + "contains private API usage."); + +namespace flutter { + +static bool IsLaunchedByFlutterCLI(const Settings& vm_settings) { + return vm_settings.enable_checked_mode; +} + +static bool EnableTracingIfNecessaryOnce(const Settings& vm_settings) { + if (IsLaunchedByFlutterCLI(vm_settings)) { + return true; + } + return false; +} + +static TracingResult sTracingResult = TracingResult::kNotAttempted; + +bool EnableTracingIfNecessaryImpl(const Settings& vm_settings) { + static std::once_flag tracing_flag; + + std::call_once(tracing_flag, [&vm_settings]() { + sTracingResult = EnableTracingIfNecessaryOnce(vm_settings) + ? TracingResult::kEnabled + : TracingResult::kDisabled; + }); + return sTracingResult != TracingResult::kDisabled; +} + +TracingResult GetTracingResultImpl() { + return sTracingResult; +} + +} // namespace flutter + +#endif // FML_OS_OHOS + #endif // TRACING_CHECKS_NECESSARY diff --git a/runtime/ptrace_check.h b/runtime/ptrace_check.h index 1c023bdb333de94cb7d78f59e11de70aac39024c..a497d3c7bfa32cce5f2d8cd0b5d0ceaf8e5f3ec4 100644 --- a/runtime/ptrace_check.h +++ b/runtime/ptrace_check.h @@ -11,7 +11,7 @@ namespace flutter { #define TRACING_CHECKS_NECESSARY \ - FML_OS_IOS && !TARGET_OS_SIMULATOR && \ + (FML_OS_IOS || FML_OS_OHOS) && !TARGET_OS_SIMULATOR && \ (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG) enum class TracingResult { diff --git a/shell/common/animator.cc b/shell/common/animator.cc index 07963bf190de9d0a801faa7c366eb447ba75c693..a0704c7b8d26f09bd45098b246f4ae9255ade2c4 100644 --- a/shell/common/animator.cc +++ b/shell/common/animator.cc @@ -197,6 +197,7 @@ void Animator::DrawLastLayerTree( } void Animator::RequestFrame(bool regenerate_layer_tree) { + TRACE_EVENT0("flutter", "Animator::RequestFrame"); if (regenerate_layer_tree) { // This event will be closed by BeginFrame. BeginFrame will only be called // if regenerating the layer tree. If a frame has been requested to update @@ -210,6 +211,7 @@ void Animator::RequestFrame(bool regenerate_layer_tree) { if (!pending_frame_semaphore_.TryWait()) { // Multiple calls to Animator::RequestFrame will still result in a // single request to the VsyncWaiter. + TRACE_EVENT0("flutter", "Animator::pending_frame_semaphore"); return; } @@ -231,6 +233,7 @@ void Animator::RequestFrame(bool regenerate_layer_tree) { } void Animator::AwaitVSync() { + TRACE_EVENT0("flutter", "Animator::AwaitVSync"); waiter_->AsyncWaitForVsync( [self = weak_factory_.GetWeakPtr()]( std::unique_ptr frame_timings_recorder) { diff --git a/shell/common/vsync_waiter.cc b/shell/common/vsync_waiter.cc index 5ee04ef530b08cd55726283203b1d7e11db42133..a23904f2d4f2713b8ed291f4ddd181d30470611b 100644 --- a/shell/common/vsync_waiter.cc +++ b/shell/common/vsync_waiter.cc @@ -14,9 +14,9 @@ namespace flutter { -static constexpr const char* kVsyncFlowName = "VsyncFlow"; +const char* kVsyncFlowName = "VsyncFlow"; -static constexpr const char* kVsyncTraceName = "VsyncProcessCallback"; +const char* kVsyncTraceName = "VsyncProcessCallback"; VsyncWaiter::VsyncWaiter(const TaskRunners& task_runners) : task_runners_(task_runners) {} diff --git a/shell/platform/ohos/BUILD.gn b/shell/platform/ohos/BUILD.gn index 02963b06904b8a17b370506102dd5f5bc7cdf9b8..cc00ad30fb137ac38c212d04b7a16fc4fa0238c7 100644 --- a/shell/platform/ohos/BUILD.gn +++ b/shell/platform/ohos/BUILD.gn @@ -204,7 +204,7 @@ shared_library("flutter_shell_native") { ldflags = ["--rtlib=compiler-rt", "-fuse-ld=lld", - "-static-libstdc++", +# "-static-libstdc++", # "-Wl", "--build-id=sha1", # "-Wl,","--warn-shared-textrel", # "-Wl,","--fatal-warnings -lunwind", diff --git a/shell/platform/ohos/flutter_embedding/flutter/build-profile.json5 b/shell/platform/ohos/flutter_embedding/flutter/build-profile.json5 index f19c7d2a8b56810e1ccce7413704307c0bfa0833..0f893a92c387e57ce12b8237fe1b7b7794d80e39 100644 --- a/shell/platform/ohos/flutter_embedding/flutter/build-profile.json5 +++ b/shell/platform/ohos/flutter_embedding/flutter/build-profile.json5 @@ -16,6 +16,11 @@ { "apiType": "stageMode", "buildOption": { + "sourceOption": { + "workers": [ + "./src/main/ets/embedding/engine/workers/PlatformChannelWorker.ets" + ] + } }, "buildOptionSet": [ { @@ -23,7 +28,7 @@ "arkOptions": { "obfuscation": { "ruleOptions": { - "enable": true, + "enable": false, "files": [ "./obfuscation-rules.txt" ] diff --git a/shell/platform/ohos/flutter_embedding/flutter/index.ets b/shell/platform/ohos/flutter_embedding/flutter/index.ets index f5b1578147121ba3675fcc2806aa881cb13081ae..91a7282d728dcf4d0806e9730dd02427945798be 100644 --- a/shell/platform/ohos/flutter_embedding/flutter/index.ets +++ b/shell/platform/ohos/flutter_embedding/flutter/index.ets @@ -68,7 +68,7 @@ export { default as Settings } from './src/main/ets/embedding/ohos/Settings'; export * from './src/main/ets/embedding/ohos/TouchEventTracker'; export { default as WindowInfoRepositoryCallbackAdapterWrapper } from './src/main/ets/embedding/ohos/WindowInfoRepositoryCallbackAdapterWrapper'; export { default as PlatformPlugin } from './src/main/ets/plugin/PlatformPlugin'; -export { default as BasicMessageChannel } from './src/main/ets/plugin/common/BasicMessageChannel'; +export { default as BasicMessageChannel, Reply } from './src/main/ets/plugin/common/BasicMessageChannel'; export { default as BinaryCodec } from './src/main/ets/plugin/common/BinaryCodec'; export * from './src/main/ets/plugin/common/BinaryMessenger'; export { default as EventChannel } from './src/main/ets/plugin/common/EventChannel'; @@ -80,6 +80,17 @@ export { default as MethodCall } from './src/main/ets/plugin/common/MethodCall'; export * from './src/main/ets/plugin/common/MethodChannel'; export { default as MethodChannel } from './src/main/ets/plugin/common/MethodChannel'; export { default as MethodCodec } from './src/main/ets/plugin/common/MethodCodec'; +export { default as BackgroundBasicMessageChannel } from './src/main/ets/plugin/common/BackgroundBasicMessageChannel'; +export { default as BackgroundMethodChannel} from './src/main/ets/plugin/common/BackgroundMethodChannel' +export { default as SendableBinaryCodec} from './src/main/ets/plugin/common/SendableBinaryCodec' +export { default as SendableJSONMessageCodec} from './src/main/ets/plugin/common/SendableJSONMessageCodec' +export { default as SendableJSONMethodCodec} from './src/main/ets/plugin/common/SendableJSONMethodCodec' +export { default as SendableMessageHandler} from './src/main/ets/plugin/common/SendableMessageHandler' +export { default as SendableMethodCallHandler} from './src/main/ets/plugin/common/SendableMethodCallHandler' +export { default as SendableMethodCodec} from './src/main/ets/plugin/common/SendableMethodCodec' +export { default as SendableStandardMessageCodec } from './src/main/ets/plugin/common/SendableStandardMessageCodec'; +export { default as SendableStandardMethodCodec } from './src/main/ets/plugin/common/SendableStandardMethodCodec'; +export { default as SendableStringCodec} from './src/main/ets/plugin/common/SendableStringCodec' export { default as StandardMessageCodec } from './src/main/ets/plugin/common/StandardMessageCodec'; export { default as StandardMethodCodec } from './src/main/ets/plugin/common/StandardMethodCodec'; export { default as StringCodec } from './src/main/ets/plugin/common/StringCodec'; diff --git a/shell/platform/ohos/flutter_embedding/flutter/obfuscation-rules.txt b/shell/platform/ohos/flutter_embedding/flutter/obfuscation-rules.txt index 200e30c063974e2a07707550b1759a0112b59331..eb5c766dfc6374b931f6a57b652b5f7675765da1 100644 --- a/shell/platform/ohos/flutter_embedding/flutter/obfuscation-rules.txt +++ b/shell/platform/ohos/flutter_embedding/flutter/obfuscation-rules.txt @@ -16,5 +16,3 @@ # Keep options: # -keep-property-name: specifies property names that you want to keep # -keep-global-name: specifies names that you want to keep in the global scope - --disable-obfuscation diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/cpp/types/libflutter/index.d.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/cpp/types/libflutter/index.d.ets index ce5ef18b90125bd4602d120b7c88b51d864600c3..f88f0688f8493af61fe5a3e16b141212bab2237e 100644 --- a/shell/platform/ohos/flutter_embedding/flutter/src/main/cpp/types/libflutter/index.d.ets +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/cpp/types/libflutter/index.d.ets @@ -33,6 +33,11 @@ export const nativeUpdateRefreshRate: ( ate: number ) => void; +/* + * 初始化SkFontMgr::RefDefault() + */ +export const nativePrefetchDefaultFontManager: () => void; + /** * 初始化dart vm和flutter engine */ @@ -44,7 +49,7 @@ export const nativeInit: ( engineCachesPath: string, initTimeMillis: number, productModel: string -) => void; +) => number | null; export const nativeAttach: (napi: FlutterNapi) => number; @@ -94,6 +99,16 @@ export const nativeXComponentAttachFlutterEngine: (xcomponentId: string, nativeS export const nativeXComponentDetachFlutterEngine: (xcomponentId: string, nativeShellHolderId: number) => void; +export const nativeXComponentDispatchMouseWheel: (nativeShellHolderId: number, + xcomponentId: string, + eventType: string, + fingerId: number, + globalX: number, + globalY: number, + offsetY: number, + timestamp: number + ) => void; + /** * Detaches flutterNapi和engine之间的关联 * 这个方法执行前提是flutterNapi已经和engine关联 @@ -108,4 +123,4 @@ export const nativeUnregisterTexture: (nativeShellHolderId: number, textureId: n export const nativeRegisterPixelMap: (nativeShellHolderId: number, textureId: number, pixelMap: PixelMap) => void; -export const nativeRegisterTexture: (nativeShellHolderId: number, textureId: number) => number; \ No newline at end of file +export const nativeRegisterTexture: (nativeShellHolderId: number, textureId: number) => number; diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/cpp/types/libflutter/index_actual.d.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/cpp/types/libflutter/index_actual.d.ets deleted file mode 100644 index d3f5c0a65d28cebdd24c8c79f062c64e0d490e65..0000000000000000000000000000000000000000 --- a/shell/platform/ohos/flutter_embedding/flutter/src/main/cpp/types/libflutter/index_actual.d.ets +++ /dev/null @@ -1,268 +0,0 @@ -/* -* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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. -*/ - -import common from '@ohos.app.ability.common'; -import resourceManager from '@ohos.resourceManager'; -import FlutterNapi from '../../../ets/embedding/engine/FlutterNapi'; -import image from '@ohos.multimedia.image'; - -/** - * 设置刷新率 - */ -export const nativeUpdateRefreshRate: ( - ate: number -) => void; - -/** - * 初始化dart vm和flutter engine - */ -export const nativeInit: ( - context: common.Context, - args: Array, - bundlePath: string, - appStoragePath: string, - engineCachesPath: string, - initTimeMillis: number -) => void; - - -/** - * 加载dart工程构建产物 - */ -export const nativeRunBundleAndSnapshotFromLibrary: ( - bundlePath: string, - entrypointFunctionName: string, - pathToEntrypointFunction: string, - assetManager: resourceManager.ResourceManager, - entrypointArgs: Array -) => void; - -/** - * 初始化SkFontMgr::RefDefault(),skia引擎文字管理初始化 - */ -export const nativePrefetchDefaultFontManager: () => void; - -/** - * 返回是否支持软件绘制 - */ -export const nativeGetIsSoftwareRenderingEnabled: () => boolean; - -/** - * attach flutterNapi实例给到 native engine,这个支持rkts到flutter平台的无关引擎之间的通信。 - * attach只需要执行一次 - */ -export const nativeAttach: (flutterNapi: FlutterNapi) => number; - -/** - * 从当前的flutterNapi复制一个新的实例 - */ -export const nativeSpawn: ( - nativeSpawningShellId: number, - entrypointFunctionName: string, - pathToEntrypointFunction: string, - initialRoute: string, - entrypointArgs: Array, - napi: FlutterNapi -) => number; - -/** - * Detaches flutterNapi和engine之间的关联 - * 这个方法执行前提是flutterNapi已经和engine关联 - */ -export const nativeDestroy: ( - nativeShellHolderId: number -) => void; - -// 不需要实现,未使用到 -// export const nativeImageHeaderCallback: ( -// imageGeneratorPointer: number, -// width: number, -// height: number -// ) => void; - -/** - * 不需要实现,c++层已有nativeSurface回调 - */ -// export const nativeSurfaceCreated: ( -// nativeShellHolderId: number -// ) => void; - - -/** - * 不需要实现,c++层已有nativeSurface回调 - */ -// export const nativeSurfaceWindowChanged: ( -// nativeShellHolderId: number -// ) => void; - - -/** - * 不需要实现,c++层已有nativeSurface回调 - */ -// export const nativeSurfaceChanged: ( -// nativeShellHolderId: number, -// width: number, -// height: number -// ) => void; - -/** - * 不需要实现,c++层已有nativeSurface回调 - */ -// export const nativeSurfaceDestroyed: ( -// nativeShellHolderId: number -// ) => void; - -/** - * 把物理屏幕参数通知到native - */ -export const nativeSetViewportMetrics: ( - nativeShellHolderId: number, - devicePixelRatio: number, - physicalWidth: number, - physicalHeight: number, - physicalPaddingTop: number, - physicalPaddingRight: number, - physicalPaddingBottom: number, - physicalPaddingLeft: number, - physicalViewInsetTop: number, - physicalViewInsetRight: number, - physicalViewInsetBottom: number, - physicalViewInsetLeft: number, - systemGestureInsetTop: number, - systemGestureInsetRight: number, - systemGestureInsetBottom: number, - systemGestureInsetLeft: number, - physicalTouchSlop: number, - displayFeaturesBounds: Array, - displayFeaturesType: Array, - displayFeaturesState: Array -) => void; - -/** - * 设置能力参数 - */ -export const nativeSetAccessibilityFeatures: ( - nativeShellHolderId: number, - flags: number -) => void; - -/** - * 清除某个messageData - */ -export const nativeCleanupMessageData: ( - messageData: number -) => void; - -/** - * 发送一个空的PlatformMessage - */ -export const nativeDispatchEmptyPlatformMessage: ( - nativeShellHolderId: number, - channel: string, - responseId: number -) => void; - -/** - * 发送一个PlatformMessage - */ -export const nativeDispatchPlatformMessage: ( - nativeShellHolderId: number, - channel: string, - message: ArrayBuffer, - position: number, - responseId: number -) => void; - -/** - * 空的PlatformMessage响应回调 - */ -export const nativeInvokePlatformMessageEmptyResponseCallback: ( - nativeShellHolderId: number, - responseId: number -) => void; - -/** - * PlatformMessage响应回调 - */ -export const nativeInvokePlatformMessageResponseCallback: ( - nativeShellHolderId: number, - responseId: number, - message: ArrayBuffer, - position: number -) => void; - - -/** - * load一个合法的.so文件到dart vm - */ -export const nativeLoadDartDeferredLibrary: ( - nativeShellHolderId: number, - loadingUnitId: number, - searchPaths: Array -) => void; - -/** - * 设置ResourceManager和assetBundlePath到engine - */ -export const nativeUpdateOhosAssetManager: ( - nativeShellHolderId: number, - resourceManager: resourceManager.ResourceManager, - assetBundlePath: string -) => void; - -/** - * 加载动态库,或者dart库失败时的通知 - */ -export const nativeDeferredComponentInstallFailure: ( - loadingUnitId: number, - error: string, - isTransient: boolean -) => void; - -/** - * 从engine获取当前绘制pixelMap - */ -export const nativeGetPixelMap: () => image.PixelMap; - -/** - * 应用低内存警告 - */ -export const nativeNotifyLowMemoryWarning: ( - nativeShellHolderId: number -) => void; - -// ----- Start FlutterTextUtils Methods ---- -/** - * 下面的方法,从键盘输入中判断当前字符是否是emoji,实现优先级低 - */ -export const nativeFlutterTextUtilsIsEmoji: ( - codePoint: number -) => boolean; - -export const nativeFlutterTextUtilsIsEmojiModifier: ( - codePoint: number -) => boolean; - -export const nativeFlutterTextUtilsIsEmojiModifierBase: ( - codePoint: number -) => boolean; - -export const nativeFlutterTextUtilsIsVariationSelector: ( - codePoint: number -) => boolean; - -export const nativeFlutterTextUtilsIsRegionalIndicator: ( - codePoint: number -) => boolean; \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterEngine.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterEngine.ets index ad6757c2fb6fa3479154e8869ac17753087389ae..cd3cd81923431aedd613734d5a7953624ec63f81 100644 --- a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterEngine.ets +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterEngine.ets @@ -80,9 +80,9 @@ export default class FlutterEngine implements EngineLifecycleListener{ * 5、初始化flutterNapi * 6、engineLifecycleListeners */ - constructor(context: common.Context, flutterLoader: FlutterLoader | null, flutterNapi: FlutterNapi | null, platformViewsController: PlatformViewsController | null) { const injector: FlutterInjector = FlutterInjector.getInstance(); - - if(flutterNapi == null){ + constructor(context: common.Context, flutterLoader: FlutterLoader | null, flutterNapi: FlutterNapi | null, platformViewsController: PlatformViewsController | null) { + const injector: FlutterInjector = FlutterInjector.getInstance(); + if (flutterNapi == null) { flutterNapi = FlutterInjector.getInstance().getFlutterNapi(); } this.flutterNapi = flutterNapi; @@ -237,7 +237,7 @@ export default class FlutterEngine implements EngineLifecycleListener{ } onPreEngineRestart(): void { - + this.engineLifecycleListeners.forEach(listener => listener.onPreEngineRestart()) } onEngineWillDestroy(): void { diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterEngineConnectionRegistry.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterEngineConnectionRegistry.ets index b864a5b3fa64102f84526247b05076e2640c8312..758dcbb6289ad2d6b50ce912b146adf19f33ed81 100644 --- a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterEngineConnectionRegistry.ets +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterEngineConnectionRegistry.ets @@ -73,7 +73,7 @@ export default class FlutterEngineConnectionRegistry implements PluginRegistry, return; } - Log.w(TAG, "Adding plugin: " + plugin); + Log.d(TAG, "Adding plugin: " + plugin); // Add the plugin to our generic set of plugins and notify the plugin // that is has been attached to an engine. this.plugins.set(plugin.getUniqueClassName(), plugin); diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterNapi.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterNapi.ets index 136e0ed1994c9d15a63436d5c832cf020bd2b2b8..ee166a1b3a1f19c567818aca28e1e22b90b029be 100644 --- a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterNapi.ets +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterNapi.ets @@ -28,6 +28,9 @@ import i18n from '@ohos.i18n'; import Any from '../../plugin/common/Any'; import FlutterManager from '../ohos/FlutterManager'; import deviceInfo from '@ohos.deviceInfo'; +import TouchEventProcessor from '../ohos/TouchEventProcessor'; +import { EmbeddingNodeController } from '../ohos/EmbeddingNodeController'; +import BuildProfile from '../../../../../BuildProfile'; const TAG = "FlutterNapi"; @@ -40,7 +43,7 @@ enum ContextType { * 提供arkTs的flutterNAPI接口 */ export default class FlutterNapi { - hasInit: boolean = false; + private static hasInit: boolean = false; //是否已实现 hasImplemented: boolean = false; @@ -51,7 +54,6 @@ export default class FlutterNapi { localizationPlugin: LocalizationPlugin | null = null; isDisplayingFlutterUi: boolean = false; - /** * 更新刷新率 * @param rate @@ -66,17 +68,35 @@ export default class FlutterNapi { appStoragePath: string, engineCachesPath: string, initTimeMillis: number) { - if (this.hasInit) { - throw Error("the engine has init"); + if (FlutterNapi.hasInit) { + Log.e(TAG, "the engine has init"); + return; } - this.hasInit = true; - Log.w(TAG, "init: bundlePath=" + bundlePath + " appStoragePath=" + appStoragePath + " engineCachesPath=" + engineCachesPath + " args=" + JSON.stringify(args)); - flutter.nativeInit(context, args, bundlePath, appStoragePath, engineCachesPath, initTimeMillis, deviceInfo.productModel); + Log.d(TAG, "HAR_VERSION=" + BuildProfile.HAR_VERSION); + Log.d(TAG, JSON.stringify({ + "name": "init, initTimeMillis=" + initTimeMillis, + "bundlePath": bundlePath, + "appStoragePath": appStoragePath, + "engineCachesPath": engineCachesPath, + "args": args, + })); + let code: number | null = flutter.nativeInit(context, args, bundlePath, appStoragePath, + engineCachesPath, initTimeMillis, deviceInfo.productModel); + FlutterNapi.hasInit = code == 0; + Log.d(TAG, "init code=" + code + ", FlutterNapi.hasInit" + FlutterNapi.hasInit); + } + + static prefetchDefaultFontManager(): void { + flutter.nativePrefetchDefaultFontManager(); } attachToNative(): void { + if (!FlutterNapi.hasInit) { + Log.e(TAG, "attachToNative fail, FlutterNapi.hasInit=" + FlutterNapi.hasInit); + return; + } this.nativeShellHolderId = flutter.nativeAttach(this); - Log.w(TAG, "nativeShellHolderId=" + this.nativeShellHolderId); + Log.d(TAG, "nativeShellHolderId=" + this.nativeShellHolderId); } runBundleAndSnapshotFromLibrary( @@ -85,7 +105,11 @@ export default class FlutterNapi { pathToEntrypointFunction: string, assetManager: resourceManager.ResourceManager, entrypointArgs: Array) { - Log.w(TAG, "init: bundlePath=" + bundlePath + " entrypointFunctionName=" + entrypointFunctionName + " pathToEntrypointFunction=" + pathToEntrypointFunction + " entrypointArgs=" + JSON.stringify(entrypointArgs)) + if (!FlutterNapi.hasInit) { + Log.e(TAG, "runBundleAndSnapshotFromLibrary fail, FlutterNapi.hasInit=" + FlutterNapi.hasInit); + return; + } + Log.d(TAG, "init: bundlePath=" + bundlePath + " entrypointFunctionName=" + entrypointFunctionName + " pathToEntrypointFunction=" + pathToEntrypointFunction + " entrypointArgs=" + JSON.stringify(entrypointArgs)) flutter.nativeRunBundleAndSnapshotFromLibrary(this.nativeShellHolderId!, bundlePath, entrypointFunctionName, pathToEntrypointFunction, assetManager, entrypointArgs); }; @@ -152,13 +176,6 @@ export default class FlutterNapi { dispatchPlatformMessage(channel: String, message: ArrayBuffer, position: number, responseId: number): void { this.ensureRunningOnMainThread(); if (this.isAttached()) { - - const uintArrayBuff = new Uint8Array(message) - let text = '' - for (let i = 0; i < uintArrayBuff.byteLength; i++) { - text += uintArrayBuff[i] + ',' - } - Log.w(TAG, "message=" + message.byteLength + ",text=" + text); flutter.nativeDispatchPlatformMessage(this.nativeShellHolderId!, channel, message, position, responseId); } else { Log.w( @@ -240,7 +257,7 @@ export default class FlutterNapi { //Called by native to respond to a platform message that we sent. handlePlatformMessageResponse(replyId: number, reply: ArrayBuffer): void { - Log.w(TAG, "called handlePlatformMessageResponse Response ID: " + replyId); + Log.d(TAG, "called handlePlatformMessageResponse Response ID: " + replyId); if (this.platformMessageHandler != null) { this.platformMessageHandler.handlePlatformMessageResponse(replyId, reply); } @@ -248,7 +265,7 @@ export default class FlutterNapi { // Called by native on any thread. handlePlatformMessage(channel: string, message: ArrayBuffer, replyId: number, messageData: number): void { - Log.w(TAG, "called handlePlatformMessage Channel: " + channel + ". Response ID: " + replyId); + Log.d(TAG, "called handlePlatformMessage Channel: " + channel + ". Response ID: " + replyId); if (this.platformMessageHandler != null) { this.platformMessageHandler.handleMessageFromDart(channel, message, replyId, messageData); } @@ -383,6 +400,33 @@ export default class FlutterNapi { flutter.nativeXComponentDetachFlutterEngine(xcomponentId, this.nativeShellHolderId!); } + /** + * xcomponent send mouseWheel event to flutterEngine + * @param xcomponentId + * @param eventType + * @param event + */ + xComponentDisPatchMouseWheel(xcomponentId: string, eventType: string, event: PanGestureEvent) { + // only mouse + if (event.source !== SourceType.Mouse) { + return; + } + const vaildFinger = event.fingerList?.find(item => item.globalX && item.globalY); + if (!vaildFinger) { + return; + } + flutter.nativeXComponentDispatchMouseWheel( + this.nativeShellHolderId!!, + xcomponentId, + eventType, + vaildFinger?.id, + vaildFinger?.localX, + vaildFinger?.localY, + event.offsetY, + event.timestamp + ); + } + detachFromNativeAndReleaseResources() { flutter.nativeDestroy(this.nativeShellHolderId!!); } @@ -406,10 +450,16 @@ export default class FlutterNapi { Log.d(TAG, "called registerTexture "); return flutter.nativeRegisterTexture(this.nativeShellHolderId!, textureId); } + + /** Dispatch Touch Event */ + onTouchEvent(strings: Array): void { + TouchEventProcessor.getInstance().postTouchEvent(strings); + } + } export interface AccessibilityDelegate { updateCustomAccessibilityActions(buffer: ByteBuffer, strings: string[]): void; updateSemantics(buffer: ByteBuffer, strings: string[], stringAttributeArgs: ByteBuffer[]): void; -} \ No newline at end of file +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/dart/DartExecutor.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/dart/DartExecutor.ets index 383927204c7bc83df203d2e779430c3969762a20..5ccbeb7407a5ef3a8724ed1f07decc782b970ce9 100644 --- a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/dart/DartExecutor.ets +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/dart/DartExecutor.ets @@ -12,7 +12,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - import resourceManager from '@ohos.resourceManager'; import FlutterInjector from '../../../FlutterInjector'; import { BinaryMessageHandler, BinaryReply, TaskQueue, TaskQueueOptions } from '../../../plugin/common/BinaryMessenger'; @@ -23,6 +22,7 @@ import { TraceSection } from '../../../util/TraceSection'; import { FlutterCallbackInformation } from '../../../view/FlutterCallbackInformation'; import FlutterNapi from '../FlutterNapi'; import { DartMessenger } from './DartMessenger'; +import SendableBinaryMessageHandler from '../../../plugin/common/SendableBinaryMessageHandler' const TAG = "DartExecutor"; @@ -163,7 +163,7 @@ export default class DartExecutor implements BinaryMessenger { return this.binaryMessenger; } - makeBackgroundTaskQueue(options: TaskQueueOptions): TaskQueue { + makeBackgroundTaskQueue(options?: TaskQueueOptions): TaskQueue { return this.getBinaryMessenger().makeBackgroundTaskQueue(options); } @@ -172,15 +172,10 @@ export default class DartExecutor implements BinaryMessenger { this.getBinaryMessenger().send(channel, message, callback); } - setMessageHandler(channel: String, handler: BinaryMessageHandler | null, taskQueue?: TaskQueue): void { - this.getBinaryMessenger().setMessageHandler(channel, handler, taskQueue); - } - - enableBufferingIncomingMessages(): void { - this.getBinaryMessenger().enableBufferingIncomingMessages(); + setMessageHandler(channel: String, handler: BinaryMessageHandler | SendableBinaryMessageHandler | null, taskQueue?: TaskQueue, ...args: Object[]): void { + this.getBinaryMessenger().setMessageHandler(channel, handler, taskQueue, ...args); } - /** * Returns the number of pending channel callback replies. * @@ -236,10 +231,6 @@ export default class DartExecutor implements BinaryMessenger { this.flutterNapi.notifyLowMemoryWarning(); } } - - disableBufferingIncomingMessages(): void { - this.getBinaryMessenger().enableBufferingIncomingMessages(); - } } @@ -322,7 +313,7 @@ export class DefaultBinaryMessenger implements BinaryMessenger { this.messenger = messenger; } - makeBackgroundTaskQueue(options: TaskQueueOptions): TaskQueue { + makeBackgroundTaskQueue(options?: TaskQueueOptions): TaskQueue { return this.messenger.makeBackgroundTaskQueue(options); } @@ -348,16 +339,8 @@ export class DefaultBinaryMessenger implements BinaryMessenger { * @param channel the name of the channel. * @param handler a {@link BinaryMessageHandler} to be invoked on incoming messages, or null. */ - setMessageHandler(channel: String, handler: BinaryMessageHandler, taskQueue?: TaskQueue): void { - this.messenger.setMessageHandler(channel, handler); - } - - enableBufferingIncomingMessages(): void { - this.messenger.enableBufferingIncomingMessages(); - } - - disableBufferingIncomingMessages(): void { - this.messenger.disableBufferingIncomingMessages(); + setMessageHandler(channel: String, handler: BinaryMessageHandler | SendableBinaryMessageHandler | null, taskQueue?: TaskQueue, ...args: Object[]): void { + this.messenger.setMessageHandler(channel, handler, taskQueue, ...args); } } diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/dart/DartMessenger.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/dart/DartMessenger.ets index 3567d610f4ddafb1e845baf55a0d7d8028e878a5..2b1178097d32f9a5decacf8d277f60a9a7a07437 100644 --- a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/dart/DartMessenger.ets +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/dart/DartMessenger.ets @@ -12,12 +12,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { ErrorEvent, Queue, taskpool, worker, MessageEvents } from '@kit.ArkTS'; -import Log from '../../../util/Log' -import { BinaryMessageHandler, BinaryMessenger, BinaryReply, TaskQueue, TaskQueueOptions } from '../../../plugin/common/BinaryMessenger'; +import Log from '../../../util/Log'; +import { BinaryMessageHandler, BinaryMessenger, BinaryReply, TaskPriority, TaskQueue, TaskQueueOptions } from '../../../plugin/common/BinaryMessenger'; import FlutterNapi from '../FlutterNapi'; import { PlatformMessageHandler } from './PlatformMessageHandler'; import { TraceSection } from '../../../util/TraceSection'; +import SendableBinaryMessageHandler from '../../../plugin/common/SendableBinaryMessageHandler' /** * Message conduit for 2-way communication between Android and Dart. @@ -36,21 +38,9 @@ export class DartMessenger implements BinaryMessenger, PlatformMessageHandler { /** * Maps a channel name to an object that contains the task queue and the handler associated with * the channel. - * - *

Reads and writes to this map must lock {@code handlersLock}. */ messageHandlers: Map = new Map(); - /** - * Maps a channel name to an object that holds information about the incoming Dart message. - * - *

Reads and writes to this map must lock {@code handlersLock}. - */ - bufferedMessages: Map = new Map(); - - handlersLock: Object = new Object(); - enableBufferingIncomingMessagesFlag: boolean = false; - pendingReplies: Map = new Map(); nextReplyId: number = 1; taskQueueFactory: TaskQueueFactory; @@ -60,22 +50,24 @@ export class DartMessenger implements BinaryMessenger, PlatformMessageHandler { this.flutterNapi = flutterNapi; this.taskQueueFactory = new DefaultTaskQueueFactory(); } - makeBackgroundTaskQueue(options: TaskQueueOptions): TaskQueue { - let taskQueue: DartMessengerTaskQueue = this.taskQueueFactory.makeBackgroundTaskQueue(options); + + makeBackgroundTaskQueue(options?: TaskQueueOptions): TaskQueue { + let taskQueue: DartMessengerTaskQueue = + this.taskQueueFactory.makeBackgroundTaskQueue(options ?? new TaskQueueOptions()); let token: TaskQueueToken = new TaskQueueToken(); this.createdTaskQueues.set(token, taskQueue); return token; } - - setMessageHandler(channel: String, handler: BinaryMessageHandler | null, taskQueue?: TaskQueue): void { + setMessageHandler(channel: String, handler: BinaryMessageHandler | SendableBinaryMessageHandler | null, + taskQueue?: TaskQueue, ...args: Object[]): void { if (handler == null) { Log.d(TAG, "Removing handler for channel '" + channel + "'"); this.messageHandlers.delete(channel); return; } let dartMessengerTaskQueue: DartMessengerTaskQueue | null = null; - if( taskQueue != null) { + if(taskQueue !== null && taskQueue !== undefined) { dartMessengerTaskQueue = this.createdTaskQueues.get(taskQueue) ?? null; if(dartMessengerTaskQueue == null) { throw new Error( @@ -85,18 +77,7 @@ export class DartMessenger implements BinaryMessenger, PlatformMessageHandler { } Log.d(TAG, "Setting handler for channel '" + channel + "'"); - this.messageHandlers.set(channel, new HandlerInfo(handler, dartMessengerTaskQueue)); - this.bufferedMessages.delete(channel); - } - - - enableBufferingIncomingMessages(): void { - this.enableBufferingIncomingMessagesFlag = true; - } - - disableBufferingIncomingMessages(): void { - this.enableBufferingIncomingMessagesFlag = false; - this.bufferedMessages = new Map(); + this.messageHandlers.set(channel, new HandlerInfo(handler, dartMessengerTaskQueue, ...args)); } send(channel: String, message: ArrayBuffer, callback?: BinaryReply): void { @@ -118,28 +99,16 @@ export class DartMessenger implements BinaryMessenger, PlatformMessageHandler { } } - async asyncInvokeHandler(handlerInfo: HandlerInfo | null, message: ArrayBuffer, replyId: number): Promise { - // Called from any thread. - if (handlerInfo != null) { - try { - Log.d(TAG, "Deferring to registered handler to process message."); - handlerInfo.handler.onMessage(message, new Reply(this.flutterNapi, replyId)); - } catch (ex) { - Log.e(TAG, "Uncaught exception in binary message listener", ex); - this.flutterNapi.invokePlatformMessageEmptyResponseCallback(replyId); - } - } else { - Log.d(TAG, "No registered handler for message. Responding to Dart with empty reply message."); - this.flutterNapi.invokePlatformMessageEmptyResponseCallback(replyId); - } + dispatchMessageToQueue(handlerInfo: HandlerInfo, message: ArrayBuffer, replyId: number): void { + let taskState: TaskState = new TaskState(handlerInfo.handler as ESObject, message, ...handlerInfo.args); + handlerInfo.taskQueue?.dispatch(taskState, new Reply(this.flutterNapi, replyId)); } - invokeHandler(handlerInfo: HandlerInfo | null, message: ArrayBuffer, replyId: number): void { - // Called from any thread. - if (handlerInfo != null) { + invokeHandler(handler: BinaryMessageHandler | null, message: ArrayBuffer, replyId: number): void { + if (handler != null) { try { Log.d(TAG, "Deferring to registered handler to process message."); - handlerInfo.handler.onMessage(message, new Reply(this.flutterNapi, replyId)); + handler.onMessage(message, new Reply(this.flutterNapi, replyId)); } catch (ex) { Log.e(TAG, "Uncaught exception in binary message listener", ex); this.flutterNapi.invokePlatformMessageEmptyResponseCallback(replyId); @@ -151,27 +120,12 @@ export class DartMessenger implements BinaryMessenger, PlatformMessageHandler { } handleMessageFromDart(channel: String, message: ArrayBuffer, replyId: number, messageData: number): void { - // Called from any thread. - Log.d(TAG, "Received message from Dart over channel '" + channel + "'"); - - let handlerInfo: HandlerInfo | null = this.messageHandlers.get(channel) ?? null; - let messageDeferred: boolean; - - messageDeferred = (this.enableBufferingIncomingMessagesFlag && handlerInfo == null); - if (messageDeferred) { - if (!this.bufferedMessages.has(channel)) { - this.bufferedMessages.set(channel, []); - } - let buffer: BufferedMessageInfo[] = this.bufferedMessages.get(channel) ?? []; - buffer.push(new BufferedMessageInfo(message, replyId, messageData)); - } - if (!messageDeferred) { - //ArkTS 没有线程池,任务队列使用异步实现 - if(handlerInfo?.taskQueue != null) { - this.asyncInvokeHandler(handlerInfo, message, replyId); - } else { - this.invokeHandler(handlerInfo, message, replyId); - } + Log.d(TAG, "Received message from Dart over channel '" + channel + "'"); + let handlerInfo: HandlerInfo | null = this.messageHandlers.get(channel) ?? null; + if (handlerInfo?.taskQueue != null) { + this.dispatchMessageToQueue(handlerInfo, message, replyId); + } else { + this.invokeHandler(handlerInfo?.handler as BinaryMessageHandler, message, replyId); } } @@ -205,46 +159,24 @@ export class DartMessenger implements BinaryMessenger, PlatformMessageHandler { } } - - - - - /** * Holds information about a platform handler, such as the task queue that processes messages from * Dart. */ class HandlerInfo { - handler: BinaryMessageHandler; + handler: BinaryMessageHandler | SendableBinaryMessageHandler; taskQueue: DartMessengerTaskQueue | null; - constructor(handler: BinaryMessageHandler, taskQueue?:DartMessengerTaskQueue | null) { - this.handler = handler; - this.taskQueue = taskQueue ?? null; - } -} + args: Object[]; -/** - * Holds information that allows to dispatch a Dart message to a platform handler when it becomes - * available. - */ -class BufferedMessageInfo { - message: ArrayBuffer; - replyId: number; - messageData: number; - - constructor(message: ArrayBuffer, - replyId: number, - messageData: number) { - this.message = message; - this.replyId = replyId; - this.messageData = messageData; + constructor(handler: BinaryMessageHandler | SendableBinaryMessageHandler, + taskQueue: DartMessengerTaskQueue | null, + ...args: Object[]) { + this.handler = handler; + this.taskQueue = taskQueue; + this.args = args; } } - - - - class Reply implements BinaryReply { flutterNapi: FlutterNapi; replyId: number; @@ -259,6 +191,7 @@ class Reply implements BinaryReply { if (this.done) { throw new Error("Reply already submitted"); } + if (reply == null) { this.flutterNapi.invokePlatformMessageEmptyResponseCallback(this.replyId); } else { @@ -267,22 +200,151 @@ class Reply implements BinaryReply { } } +export class TaskState { + handler: SendableBinaryMessageHandler; + message: ArrayBuffer; + args: Object[]; + + constructor(handler: SendableBinaryMessageHandler, message: ArrayBuffer, ...args: Object[]) { + this.handler = handler; + this.message = message; + this.args = args; + } +} + interface DartMessengerTaskQueue { - dispatch(): void; + dispatch(taskState: TaskState, callback: Reply): void; +} + +interface SerialTaskQueue extends DartMessengerTaskQueue { } interface TaskQueueFactory { - makeBackgroundTaskQueue(options: TaskQueueOptions): DartMessengerTaskQueue; + makeBackgroundTaskQueue(options: TaskQueueOptions): DartMessengerTaskQueue; } class ConcurrentTaskQueue implements DartMessengerTaskQueue { - dispatch(): void {} + private priority: TaskPriority; + + constructor(priority: TaskPriority) { + this.priority = priority; + } + + dispatch(taskState: TaskState, callback: Reply): void { + let task: taskpool.Task = new taskpool.Task(handleMessageInBackground, + taskState.handler, + taskState.message, + ...taskState.args); + taskpool.execute(task, this.priority as number).then((result: Object) => { + callback.reply(result as ArrayBuffer); + }).catch((err: string) => { + callback.reply(null); + Log.e(TAG, "Oops! Failed to execute task: ", err); + }); + } +} + +const scriptURL: string = '../workers/PlatformChannelWorker.ets'; +class SerialTaskQueueWithWorker implements SerialTaskQueue { + private static workerInstance: worker.ThreadWorker | null = null; + + constructor () { + if (!SerialTaskQueueWithWorker.workerInstance) { + SerialTaskQueueWithWorker.workerInstance = + new worker.ThreadWorker(scriptURL, {name: 'PlatformChannelWorker'}); + } + } + + dispatch(taskState: TaskState, callback: Reply): void { + SerialTaskQueueWithWorker.workerInstance!.onmessage = (e: MessageEvents): void => { + callback.reply(e.data as ArrayBuffer); + } + + SerialTaskQueueWithWorker.workerInstance!.onerror = (err: ErrorEvent) => { + callback.reply(null); + Log.e(TAG, "Oops! Failed to execute task in worker thread: ", err.message); + } + + SerialTaskQueueWithWorker.workerInstance!.postMessageWithSharedSendable(taskState, [taskState.message]); + } +} + +type Runnable = () => Promise; +class SerialTaskQueueWithTaskPool implements SerialTaskQueue { + private priority: TaskPriority; + private queue: Queue = new Queue(); + private isRunning: boolean = false; + + constructor(priority: TaskPriority) { + this.priority = priority; + } + + dispatch(taskState: TaskState, callback: Reply): void { + let task: taskpool.Task = new taskpool.Task(handleMessageInBackground, + taskState.handler, + taskState.message, + ...taskState.args); + const runnable: Runnable = async () => { + try { + const result = await taskpool.execute(task, this.priority as number); + callback.reply(result as ArrayBuffer); + } catch (err) { + callback.reply(null); + Log.e(TAG, "Oops! Failed to execute task: ", err); + } + }; + + this.queue.add(runnable); + + if (!this.isRunning) { + this.runNext(); + } + } + + private async runNext(): Promise { + if (this.queue.length > 0) { + this.isRunning = true; + const task = this.queue.pop(); + try { + await task(); + } finally { + this.isRunning = false; + this.runNext(); // 执行下一个任务 + } + } + } } class DefaultTaskQueueFactory implements TaskQueueFactory { - makeBackgroundTaskQueue(options: TaskQueueOptions): DartMessengerTaskQueue { - return new ConcurrentTaskQueue(); + makeBackgroundTaskQueue(options: TaskQueueOptions): DartMessengerTaskQueue { + if (options.isSingleThreadMode()) { + return new SerialTaskQueueWithWorker(); + } else { + if (options.getIsSerial()) { + return new SerialTaskQueueWithTaskPool(options.getPriority()); + } + return new ConcurrentTaskQueue(options.getPriority()); } + } } -class TaskQueueToken implements TaskQueue {} \ No newline at end of file +class TaskQueueToken implements TaskQueue {} + +@Concurrent +async function handleMessageInBackground(handler: SendableBinaryMessageHandler, + message: ArrayBuffer, + ...args: Object[]): Promise { + const result = await new Promise((resolve, reject) => { + try { + handler.onMessage(message, { + reply: (reply: ArrayBuffer | null): void => { + resolve(reply); + } + }, ...args); + } catch (e) { + reject(null); + Log.e('WARNING', "Oops! Failed to handle message in the background: ", e); + } + }); + return result; +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/loader/FlutterLoader.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/loader/FlutterLoader.ets index 96589b090c499fee11173c172e07e7d7db34a0ad..abf6e96a3ee1ce06d12097d2f13fa64fda18f752 100644 --- a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/loader/FlutterLoader.ets +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/loader/FlutterLoader.ets @@ -55,6 +55,13 @@ const TIMESTAMP_PREFIX = "res_timestamp-"; const OH_ICU_DATA_FILE_PATH = "/system/usr/ohos_icu/" +async function prefetchDefaultFontManager(): Promise { + await new Promise((resolve: Function) => { + FlutterNapi.prefetchDefaultFontManager() + resolve() + }) +} + /** * 定位在hap包中的flutter资源,并且加载flutter native library. */ @@ -87,6 +94,7 @@ export default class FlutterLoader { this.initStartTimestampMillis = Date.now(); this.context = context; this.flutterApplicationInfo = ApplicationInfoLoader.load(context); + await prefetchDefaultFontManager(); if (this.flutterApplicationInfo!.isDebugMode) { await this.copyResource(context) } @@ -171,6 +179,7 @@ export default class FlutterLoader { shellArgs.push("--" + VM_SNAPSHOT_DATA_KEY + "=" + this.flutterApplicationInfo!.vmSnapshotData); shellArgs.push( "--" + ISOLATE_SNAPSHOT_DATA_KEY + "=" + this.flutterApplicationInfo!.isolateSnapshotData); + shellArgs.push('--enable-checked-mode') } else { shellArgs.push( "--" + AOT_SHARED_LIBRARY_NAME + "=" + this.flutterApplicationInfo!.aotSharedLibraryName); @@ -213,7 +222,7 @@ export default class FlutterLoader { // //最终初始化操作 const costTime = Date.now() - this.initStartTimestampMillis; - this.flutterNapi!.init( + this.flutterNapi.init( this.context!, shellArgs, kernelPath, @@ -284,4 +293,4 @@ class InitResult { this.engineCachesPath = engineCachesPath; this.dataDirPath = dataDirPath; } -} \ No newline at end of file +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/PlatformChannel.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/PlatformChannel.ets index 4962397bb91da81aad8d411c8b19257696884292..239ce035500de51c68c568f7654bf80522a349da 100644 --- a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/PlatformChannel.ets +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/PlatformChannel.ets @@ -379,23 +379,13 @@ class PlatformMethodCallback implements MethodCallHandler { this.platform.platformMessageHandler.setClipboardData(clipboardContent, result); break; case "Clipboard.hasStrings": - let hasStrings: boolean = false; let response: Any = new Map().set("value", false); let systemPasteboard = pasteboard.getSystemPasteboard(); systemPasteboard.hasData().then((hasData) => { - if (!hasData) { - response.set("value", hasData); - result.success(response); - } - }).catch((err: Any) => { - Log.e(PlatformMethodCallback.TAG, "systemPasteboard.hasData err: " + JSON.stringify(err)); - }) - systemPasteboard.getData().then((pasteData) => { - hasStrings = pasteData.hasType(pasteboard.MIMETYPE_TEXT_PLAIN); - response.set("value", hasStrings); + response.set("value", hasData); result.success(response); }).catch((err: Any) => { - Log.e(PlatformMethodCallback.TAG, "getData err: " + JSON.stringify(err)); + Log.e(PlatformMethodCallback.TAG, "systemPasteboard.hasData err: " + JSON.stringify(err)); }) break; default: diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/TextInputChannel.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/TextInputChannel.ets index f215947be41565a6b4a3cd13f6cad427c7b8124e..df71b3fb156cc5220a0f76d2213783d509ba8c82 100644 --- a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/TextInputChannel.ets +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/systemchannels/TextInputChannel.ets @@ -25,6 +25,8 @@ import { TextEditingDelta, TextEditingDeltaJson } from '../../../plugin/editing/ import Any from '../../../plugin/common/Any'; const TAG = "TextInputChannel"; +/// 规避换行标识无法显示问题,api修改后再删除 +const NEWLINE_KEY_TYPE: number = 8; export default class TextInputChannel { private static CHANNEL_NAME = "flutter/textinput"; @@ -251,13 +253,12 @@ export class Configuration { case "TextInputAction.next": return inputMethod.EnterKeyType.NEXT case "TextInputAction.newline": - return inputMethod.EnterKeyType.NONE + return NEWLINE_KEY_TYPE case "TextInputAction.done": return inputMethod.EnterKeyType.DONE default: // Present default key if bad input type is given. return inputMethod.EnterKeyType.UNSPECIFIED - } } diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/workers/PlatformChannelWorker.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/workers/PlatformChannelWorker.ets new file mode 100644 index 0000000000000000000000000000000000000000..bc32d2655da245fd7affcef9392cd937c9d19375 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/workers/PlatformChannelWorker.ets @@ -0,0 +1,74 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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. +*/ + +import { ErrorEvent, MessageEvents, ThreadWorkerGlobalScope, worker } from '@kit.ArkTS'; + +import Log from '../../../util/Log'; +import SendableBinaryMessageHandler from '../../../plugin/common/SendableBinaryMessageHandler'; +import { TaskState } from '../dart/DartMessenger'; + + +const TAG: string = 'PlatformChannelWorker'; +const workerPort: ThreadWorkerGlobalScope = worker.workerPort; + +/** + * Defines the event handler to be called when the worker thread receives a message sent by the host thread. + * The event handler is executed in the worker thread. + * + * @param e message data + */ +workerPort.onmessage = async (e: MessageEvents) => { + let data: TaskState = e.data; + let result: ArrayBuffer | null = await handleMessage(data.handler, data.message, data.args); + workerPort.postMessage(result, [result]); +} + +/** + * Defines the event handler to be called when the worker receives a message that cannot be deserialized. + * The event handler is executed in the worker thread. + * + * @param e message data + */ +workerPort.onmessageerror = (e: MessageEvents) => { + Log.e(TAG, '#onmessageerror = ' + e.data); +} + +/** + * Defines the event handler to be called when an exception occurs during worker execution. + * The event handler is executed in the worker thread. + * + * @param e error message + */ +workerPort.onerror = (e: ErrorEvent) => { + Log.e(TAG, '#onerror = ' + e.message); +} + +async function handleMessage(handler: SendableBinaryMessageHandler, + message: ArrayBuffer, + args: Object[]): Promise { + const result = await new Promise((resolve, reject) => { + try { + handler.onMessage(message, { + reply: (reply: ArrayBuffer | null): void => { + resolve(reply); + } + }, ...args); + } catch (e) { + reject(null); + Log.e(TAG, "Oops! Failed to handle message in the background: ", e); + } + }); + return result; +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/EmbeddingNodeController.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/EmbeddingNodeController.ets index 99019c3a82b0639cdcf132aac339df6e0581cead..2ad660b2a8ec7a911c8732031e041285be04c2a8 100644 --- a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/EmbeddingNodeController.ets +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/EmbeddingNodeController.ets @@ -81,8 +81,13 @@ export class EmbeddingNodeController extends NodeController { } } - postEvent(event: TouchEvent | undefined) : boolean { - if (event != undefined) { + postEvent(event: TouchEvent | undefined, isPx: boolean = false): boolean { + if (event == undefined) { + return false; + } + + // change vp to px + if (!isPx) { let changedTouchLen = event.changedTouches.length; for (let i = 0; i< changedTouchLen; i++) { event.changedTouches[i].displayX = vp2px(event.changedTouches[i].displayX); @@ -94,9 +99,8 @@ export class EmbeddingNodeController extends NodeController { event.changedTouches[i].x = vp2px(event.changedTouches[i].x); event.changedTouches[i].y = vp2px(event.changedTouches[i].y); } - return this.rootNode?.postTouchEvent(event) as boolean - } else { - return false; } + + return this.rootNode?.postTouchEvent(event) as boolean } } \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterAbility.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterAbility.ets index 094657055fbf2e2c2104e757597ad5acf843f2b8..37e532808dddf506e4d43c02e2a58c1c0d45ba45 100644 --- a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterAbility.ets +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterAbility.ets @@ -82,6 +82,10 @@ export class FlutterAbility extends UIAbility implements Host { this.delegate?.onCreate(); } + if (this.stillAttachedForEvent("onWindowStageCreate")) { + this?.delegate?.onWindowStageCreate(); + } + console.log('MyAbility onCreate'); this.context.eventHub.on(EVENT_BACK_PRESS, () => { @@ -96,6 +100,12 @@ export class FlutterAbility extends UIAbility implements Host { } } this.errorManagerId = errorManager.on('error', observer); + + let flutterApplicationInfo = ApplicationInfoLoader.load(this.context); + + if (flutterApplicationInfo.isDebugMode) { + this.delegate?.initWindow(); + } } onDestroy() { @@ -125,7 +135,7 @@ export class FlutterAbility extends UIAbility implements Host { */ onWindowStageCreate(windowStage: window.WindowStage) { FlutterManager.getInstance().pushWindowStage(this, windowStage); - this.delegate?.onWindowStageCreate(); + this.delegate?.initWindow(); this.mainWindow = windowStage.getMainWindowSync(); try { windowStage.on('windowStageEvent', (data) => { @@ -165,7 +175,7 @@ export class FlutterAbility extends UIAbility implements Host { Log.i(TAG, 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? ''); }); - this.mainWindow?.setWindowLayoutFullScreen(true); + FlutterManager.getInstance().setUseFullScreen(true); } catch (exception) { Log.e(TAG, 'Failed to enable the listener for window stage event changes. Cause:' + JSON.stringify(exception)); } diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterAbilityAndEntryDelegate.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterAbilityAndEntryDelegate.ets index 0868bd774b6486b3fce31e489f75317f1fef5855..480371d80703aafa8be51b208ed052bb921375bc 100644 --- a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterAbilityAndEntryDelegate.ets +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterAbilityAndEntryDelegate.ets @@ -322,7 +322,6 @@ class FlutterAbilityAndEntryDelegate implements ExclusiveAppComponent onWindowStageCreate() { this.ensureAlive(); - this.initWindow(); this.doInitialFlutterViewRun(); if (this.shouldDispatchAppLifecycleState()) { this.flutterEngine?.getLifecycleChannel()?.appIsResumed(); @@ -347,8 +346,8 @@ class FlutterAbilityAndEntryDelegate implements ExclusiveAppComponent onForeground() { this.ensureAlive(); if (this.shouldDispatchAppLifecycleState()) { - this.flutterEngine?.getLifecycleChannel()?.aWindowIsFocused(); this.flutterEngine?.getLifecycleChannel()?.appIsResumed(); + this.flutterEngine?.getLifecycleChannel()?.aWindowIsFocused(); } } diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterEntry.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterEntry.ets index ac65018832eda4a40e923a2536b90b58ee8a6362..88e714f241bbf8b4981af55f00608e21042d8ab7 100644 --- a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterEntry.ets +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterEntry.ets @@ -26,6 +26,7 @@ import { FlutterView } from '../../view/FlutterView'; import FlutterManager from './FlutterManager'; import window from '@ohos.window'; import FlutterEngineConfigurator from './FlutterEngineConfigurator'; +import { FlutterPlugin } from '../engine/plugins/FlutterPlugin'; const TAG = "FlutterEntry"; @@ -244,6 +245,14 @@ export default class FlutterEntry implements Host { } popSystemNavigator(): boolean { - return false; + return true; + } + + addPlugin(plugin: FlutterPlugin): void { + this.delegate?.addPlugin(plugin) + } + + removePlugin(plugin: FlutterPlugin): void { + this.delegate?.removePlugin(plugin) } } \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterManager.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterManager.ets index 3a35bc798e255513869f52a4d59ad8d390ca7725..80896cf99e11d134ffc63bd435fafc8c8609d261 100644 --- a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterManager.ets +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterManager.ets @@ -57,7 +57,10 @@ export default class FlutterManager { return this.windowStageList.get(uiAbility)!! } - getUIAbility(context: Context): UIAbility { + getUIAbility(context?: Context): UIAbility { + if (!context && this.uiAbilityList.length > 0) { + return this.uiAbilityList[0]; + } return this.uiAbilityList.find((item: UIAbility) => item.context == context)!! } diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterPage.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterPage.ets index df00af2cf2c0c37c234ad791768242c3c6aab029..3c7c84be0866443ff12b5be576751059a31faabe 100644 --- a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterPage.ets +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterPage.ets @@ -17,6 +17,7 @@ import Log from '../../util/Log'; import { FlutterView } from '../../view/FlutterView'; import { EmbeddingNodeController } from './EmbeddingNodeController'; import FlutterManager from './FlutterManager'; +import { DVModel, DVModelChildren, DynamicView } from '../../view/DynamicView/dynamicView'; const TAG = "FlutterPage"; @@ -39,17 +40,23 @@ export struct FlutterPage { @StorageLink('nodeWidth') storageLinkWidth: number = 0; @StorageLink('nodeHeight') storageLinkHeight: number = 0; + @State rootDvModel: DVModelChildren | undefined = undefined + + private flutterView?: FlutterView | null - private nodeController: EmbeddingNodeController | undefined = undefined private lastArea?: Area; + private panOption: PanGestureOptions = new PanGestureOptions({ direction: PanDirection.Up | PanDirection.Down }); aboutToAppear() { this.flutterView = FlutterManager.getInstance().getFlutterView(this.viewId); - this.nodeController = this.flutterView!!.getEmbeddingNodeController(); this.flutterView?.addFirstFrameListener(this) + this.flutterView?.setCheckFullScreen(this.checkFullScreen) this.flutterView?.setCheckKeyboard(this.checkKeyboard) this.flutterView?.setCheckGesture(this.checkGesture) + + this.rootDvModel = this.flutterView!!.getDVModel().children + } aboutToDisappear() { @@ -62,9 +69,17 @@ export struct FlutterPage { build() { Stack() { - NodeContainer(this.nodeController) - .width(this.storageLinkWidth) - .height(this.storageLinkHeight) + ForEach(this.rootDvModel!!, (child: ESObject) => { + DynamicView({ + model: child as DVModel, + params: child.params, + events: child.events, + children: child.children, + customBuilder: child.builder + }) + .position({x: (child.params as Record)['left'] as number, y: (child.params as Record)['top'] as number}) + }, (child: ESObject) => `${child.id_}`) + Text('') .id('emptyFocusText' + this.viewId) @@ -74,7 +89,6 @@ export struct FlutterPage { XComponent({ id: this.viewId, type: this.xComponentType, libraryname: 'flutter' }) .focusable(true) - .focusOnTouch(true) .onLoad((context) => { this.flutterView?.onSurfaceCreated() Log.d(TAG, "XComponent onLoad "); @@ -102,5 +116,17 @@ export struct FlutterPage { Log.d(TAG, "onKeyEvent " + event.type); this.flutterView?.onKeyEvent(event) }) + .gesture( + PanGesture(this.panOption) + .onActionStart((event: GestureEvent) => { + this.flutterView?.onMouseWheel("actionStart", event); + }) + .onActionUpdate((event: GestureEvent) => { + this.flutterView?.onMouseWheel("actionUpdate", event); + }) + .onActionEnd((event: GestureEvent) => { + this.flutterView?.onMouseWheel("actionEnd", event); + }) + ) } } diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/PlatformViewInfo.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/PlatformViewInfo.ets index 16dbf64d61c26553776ff2f5360dad42f75d24ce..3afb4a54b6fad1b278b81d77a43edef8941b9189 100644 --- a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/PlatformViewInfo.ets +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/PlatformViewInfo.ets @@ -20,13 +20,17 @@ export class PlatformViewInfo { public surfaceId: string; public width: number; public height: number; + public top: number; + public left: number; public direction: Direction; - constructor(platformView: PlatformView, surfaceId: string, width: number, height: number, direction: Direction) { + constructor(platformView: PlatformView, surfaceId: string, width: number, height: number, top: number, left: number, direction: Direction) { this.platformView = platformView; this.surfaceId = surfaceId; this.width = width; this.height = height; + this.top = top; + this.left = left; this.direction = direction; } } diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/TouchEventProcessor.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/TouchEventProcessor.ets new file mode 100644 index 0000000000000000000000000000000000000000..00fab960360e3b7e7a143df134bfff4219c1b5e7 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/TouchEventProcessor.ets @@ -0,0 +1,287 @@ +/* +* Copyright (c) 2024 Hunan OpenValley Digital Industry Development 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. +*/ +/** Handle the motion events received by the FlutterNapi. */ +// import PlainArray from '@ohos.util.PlainArray'; +// import { TouchEvent } from '@ohos.multimodalInput.touchEvent'; +// import Queue from '@ohos.util.Queue'; + +import { CustomTouchEvent, CustomTouchObject } from '../../plugin/platform/CustomTouchEvent'; +import display from '@ohos.display'; +import FlutterManager from './FlutterManager'; +import { EmbeddingNodeController } from './EmbeddingNodeController'; + + +const OH_NATIVEXCOMPONENT_UNKNOWN = 4; +const OH_NATIVEXCOMPONENT_TOOL_TYPE_UNKNOWN = 0; + +class OH_NativeXComponent_TouchPoint { + id: number = 0; + screenX: number = 0.0; + screenY: number = 0.0; + x: number = 0.0; + y: number = 0.0; + type: number = OH_NATIVEXCOMPONENT_UNKNOWN; + size: number = 0; + force: number = 0; + timeStamp: number = 0; + isPressed: boolean = false; + + constructor(id: number, + screenX: number, + screenY: number, + x: number, + y: number, + type: number, + size: number, + force: number, + timeStamp: number, + isPressed: boolean) { + this.id = id; + this.screenX = screenX; + this.screenY = screenY; + this.x = x; + this.y = y; + this.type = type; + this.size = size; + this.force = force; + this.timeStamp = timeStamp; + this.isPressed = isPressed; + } +} + +class OH_NativeXComponent_TouchEvent { + id: number = 0; + screenX: number = 0.0; + screenY: number = 0.0; + x: number = 0.0; + y: number = 0.0; + type: number = OH_NATIVEXCOMPONENT_UNKNOWN; + size: number = 0; + force: number = 0; + deviceId: number = 0; + timeStamp: number = 0; + touchPoints: OH_NativeXComponent_TouchPoint[] = []; + numPoints: number = 0; + + constructor(id: number, + screenX: number, + screenY: number, + x: number, + y: number, + type: number, + size: number, + force: number, + deviceId: number, + timeStamp: number, + touchPoints: OH_NativeXComponent_TouchPoint[], + numPoints: number) { + this.id = id; + this.screenX = screenX; + this.screenY = screenY; + this.x = x; + this.y = y; + this.type = type; + this.size = size; + this.force = force; + this.deviceId = deviceId; + this.timeStamp = timeStamp; + this.touchPoints = touchPoints; + this.numPoints = numPoints; + } +} + +class TouchPacket { + touchEvent: OH_NativeXComponent_TouchEvent; + toolType: number = OH_NATIVEXCOMPONENT_TOOL_TYPE_UNKNOWN; + tiltX: number = 0; + tiltY: number = 0; + + constructor(touchEvent: OH_NativeXComponent_TouchEvent, + toolType: number, + tiltX: number, + tiltY: number) { + this.touchEvent = touchEvent; + this.toolType = toolType; + this.tiltX = tiltX; + this.tiltY = tiltY; + } +} + +export default class TouchEventProcessor { + private static instance: TouchEventProcessor; + + static getInstance(): TouchEventProcessor { + if (TouchEventProcessor.instance == null) { + TouchEventProcessor.instance = new TouchEventProcessor(); + } + return TouchEventProcessor.instance; + } + + private decodeTouchPacket(strings: Array, densityPixels: number, top: number, left: number): TouchPacket { + let offset: number = 0; + let numPoint: number = parseInt(strings[offset++]); + let changesId: number = parseInt(strings[offset++]); + let changesscreenX: number = (parseFloat(strings[offset++]) / densityPixels); + let changesscreenY: number = (parseFloat(strings[offset++]) / densityPixels); + let changesX: number = ((parseFloat(strings[offset++]) / densityPixels) - left); + let changesY: number = ((parseFloat(strings[offset++]) / densityPixels) - top); + let changesType: number = parseInt(strings[offset++]); + let changesSize: number = parseFloat(strings[offset++]); + let changesForce: number = parseFloat(strings[offset++]); + let changesDeviceId: number = parseInt(strings[offset++]); + let changesTimeStamp: number = parseInt(strings[offset++]); + + const touchPoints: OH_NativeXComponent_TouchPoint[] = []; + for (let i = 0; i < numPoint; i++) { + const touchPoint: OH_NativeXComponent_TouchPoint = new OH_NativeXComponent_TouchPoint( + parseInt(strings[offset++]), + (parseFloat(strings[offset++]) / densityPixels), + (parseFloat(strings[offset++]) / densityPixels), + ((parseFloat(strings[offset++]) / densityPixels) - left), + ((parseFloat(strings[offset++]) / densityPixels) - top), + parseInt(strings[offset++]), + parseFloat(strings[offset++]), + parseFloat(strings[offset++]), + parseInt(strings[offset++]), + parseInt(strings[offset++]) === 1 ? true : false + ); + touchPoints.push(touchPoint); + } + + const touchEventInput: OH_NativeXComponent_TouchEvent = new OH_NativeXComponent_TouchEvent( + changesId, + changesscreenX, + changesscreenY, + changesX, + changesY, + changesType, + changesSize, + changesForce, + changesDeviceId, + changesTimeStamp, + touchPoints, + numPoint + ); + + let toolTypeInput: number = parseInt(strings[offset++]); + let tiltXTouch: number = parseInt(strings[offset++]); + let tiltYTouch: number = parseInt(strings[offset++]); + + const touchpointeventpacket: TouchPacket = new TouchPacket( + touchEventInput, + toolTypeInput, + tiltXTouch, + tiltYTouch + ); + return touchpointeventpacket; + } + + private constureCustomTouchEventImpl(touchPacket: TouchPacket): CustomTouchEvent { + let changes1: CustomTouchObject = new CustomTouchObject( + touchPacket.touchEvent.type, + touchPacket.touchEvent.id, + touchPacket.touchEvent.screenX, + touchPacket.touchEvent.screenY, + touchPacket.touchEvent.screenX, + touchPacket.touchEvent.screenY, + touchPacket.touchEvent.screenX, + touchPacket.touchEvent.screenY, + touchPacket.touchEvent.x, + touchPacket.touchEvent.y + ); + + let touches: CustomTouchObject[] = []; + let touchPointer:number = touchPacket.touchEvent.numPoints; + for (let i = 0; i < touchPointer; i++) { + let touchesItem: CustomTouchObject = new CustomTouchObject( + touchPacket.touchEvent.touchPoints[i].type, + touchPacket.touchEvent.touchPoints[i].id, + touchPacket.touchEvent.touchPoints[i].screenX, + touchPacket.touchEvent.touchPoints[i].screenY, + touchPacket.touchEvent.touchPoints[i].screenX, + touchPacket.touchEvent.touchPoints[i].screenY, + touchPacket.touchEvent.touchPoints[i].screenX, + touchPacket.touchEvent.touchPoints[i].screenY, + touchPacket.touchEvent.touchPoints[i].x, + touchPacket.touchEvent.touchPoints[i].y + ); + touches.push(touchesItem); + } + + let customTouchEvent1: CustomTouchEvent = new CustomTouchEvent( + touchPacket.touchEvent.type, + touches, + [changes1], + touchPacket.touchEvent.timeStamp, + SourceType.TouchScreen, + touchPacket.touchEvent.force, + touchPacket.tiltX, + touchPacket.tiltY, + touchPacket.toolType + ); + + return customTouchEvent1; + } + + /** Construct the CustomTouchEvent and return. */ + public constureCustomTouchEvent(strings: Array, top: number, left: number) : CustomTouchEvent { + let densityPixels:number = display.getDefaultDisplaySync().densityPixels; + + let touchPacket: TouchPacket = this.decodeTouchPacket(strings, densityPixels, top, left); + let customTouchEvent: CustomTouchEvent = this.constureCustomTouchEventImpl(touchPacket); + return customTouchEvent; + } + + public postTouchEvent(strings: Array) { + FlutterManager.getInstance().getFlutterViewList().forEach((value) => { + let length = value.getDVModel().children.length + for (let index = length - 1; index >= 0; index--) { + let dvModel = value.getDVModel().children[index] + let params = dvModel.getLayoutParams() as Record; + let left = params['left'] as number ?? 0; + let top = params['top'] as number ?? 0; + let down = params['down'] as boolean ?? false; + if (down) { + //如果flutter端判断当前platformView是可点击的,则将事件分发出去 + let touchEvent = TouchEventProcessor.getInstance().constureCustomTouchEvent(strings, top, left); + let nodeController = params['nodeController'] as EmbeddingNodeController; + nodeController.postEvent(touchEvent) + } else if (strings[6] == '0' && !down) { + //如果触摸事件为OH_NATIVEXCOMPONENT_DOWN=0,且只有一个手指,说明是下一次点击了,这时候需要清空上一次的数据 + if (strings[6] == '0' && strings[0] == '1') { + params['touchEvent'] = undefined + } + //如果触摸事件为OH_NATIVEXCOMPONENT_DOWN=0类型,且在flutter端还没判断当前view是否处于点击区域内,则 + //将点击事件存储在list列表中。 + let touchEvent = TouchEventProcessor.getInstance().constureCustomTouchEvent(strings, top, left); + let array: Array | undefined = params['touchEvent'] as Array + if (array == undefined) { + array = [] + params['touchEvent'] = array + } + array.push(touchEvent) + } + } + }); + } + + public checkHitPlatformView(left: number, top: number, width: number, height: number, x: number, y: number): boolean { + if (x >= left && x <= (left + width) && y >= top && y <= (top + height)) { + return true; + } else { + return false; + } + } +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/PlatformPlugin.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/PlatformPlugin.ets index c837388f4655a7ccba308ab907359db9fc1eb4fc..c2e129244bc803f97324bf5057c47ad68d42d06f 100644 --- a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/PlatformPlugin.ets +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/PlatformPlugin.ets @@ -33,6 +33,7 @@ import window from '@ohos.window'; import common from '@ohos.app.ability.common'; import { MethodResult } from './common/MethodChannel'; import Any from './common/Any'; +import router from '@ohos.router'; /** * ohos实现platform plugin @@ -198,6 +199,7 @@ export class PlatformPluginCallback implements PlatformMessageHandler { popSystemNavigator() { if (this.platformPluginDelegate != null && this.platformPluginDelegate?.popSystemNavigator()) { + router.back(); return; } if (this.uiAbilityContext != null) { @@ -280,21 +282,17 @@ export class PlatformPluginCallback implements PlatformMessageHandler { let uiConfig: ('status' | 'navigation')[] = []; if (mode == SystemUiMode.LEAN_BACK) { //全屏显示,通过点击显示器上的任何位置都可以显示状态和导航栏 - this.lastWindow?.setWindowLayoutFullScreen(false); - + FlutterManager.getInstance().setUseFullScreen(false); } else if (mode == SystemUiMode.IMMERSIVE) { //全屏显示,通过在显示器边缘的滑动手势可以显示状态和导航栏,应用程序不会接收到此手势 - this.lastWindow?.setWindowLayoutFullScreen(true); - + FlutterManager.getInstance().setUseFullScreen(true); } else if (mode == SystemUiMode.IMMERSIVE_STICKY) { //全屏显示,通过在显示器边缘的滑动手势可以显示状态和导航栏,此手势由应用程序接收 - this.lastWindow?.setWindowLayoutFullScreen(true); - + FlutterManager.getInstance().setUseFullScreen(true); } else if (mode == SystemUiMode.EDGE_TO_EDGE) { //全屏显示,在应用程序上呈现状态和导航元素 - this.lastWindow?.setWindowLayoutFullScreen(false); + FlutterManager.getInstance().setUseFullScreen(false); uiConfig = ['status', 'navigation']; - } else { return; } diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/BackgroundBasicMessageChannel.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/BackgroundBasicMessageChannel.ets new file mode 100644 index 0000000000000000000000000000000000000000..786222f770063d062de065373020955b3c2ad8cb --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/BackgroundBasicMessageChannel.ets @@ -0,0 +1,137 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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. +*/ +import MessageChannelUtils from '../../util/MessageChannelUtils'; +import Log from '../../util/Log'; +import { BinaryReply } from './BinaryMessenger'; +import { TaskQueue } from './BinaryMessenger'; +import MessageCodec from './MessageCodec'; +import { BinaryMessenger } from './BinaryMessenger'; +import SendableBinaryMessageHandler from './SendableBinaryMessageHandler' +import SendableMessageCodec from './SendableMessageCodec'; +import SendableMessageHandler from './SendableMessageHandler'; +import StringUtils from '../../util/StringUtils'; + +/** + * A named channel for communicating with the Flutter application using basic, asynchronous message + * passing. + * + *

Messages are encoded into binary before being sent, and binary messages received are decoded + * into Java objects. The {@link MessageCodec} used must be compatible with the one used by the + * Flutter application. This can be achieved by creating a BasicMessageChannel + * counterpart of this channel on the Dart side. The static Java type of messages sent and received + * is {@code Object}, but only values supported by the specified {@link MessageCodec} can be used. + * + *

The logical identity of the channel is given by its name. Identically named channels will + * interfere with each other's communication. + */ +export default class BackgroundBasicMessageChannel { + public static TAG = "BackgroundBasicMessageChannel#"; + public static CHANNEL_BUFFERS_CHANNEL = "dev.flutter/channel-buffers"; + private messenger: BinaryMessenger; + private name: string; + private codec: SendableMessageCodec; + private taskQueue: TaskQueue; + + constructor(messenger: BinaryMessenger, name: string, codec: SendableMessageCodec, taskQueue?: TaskQueue) { + this.messenger = messenger + this.name = name + this.codec = codec + this.taskQueue = taskQueue ?? messenger.makeBackgroundTaskQueue() + } + + /** + * Sends the specified message to the Flutter application, optionally expecting a reply. + * + *

Any uncaught exception thrown by the reply callback will be caught and logged. + * + * @param message the message, possibly null. + * @param callback a {@link Reply} callback, possibly null. + */ + send(message: T, callback?: (reply: T)=>void): void { + this.messenger.send(this.name, this.codec.encodeMessage(message), + callback == null ? null : new IncomingReplyHandler(callback, this.codec)); + } + + /** + * Registers a message handler on this channel for receiving messages sent from the Flutter + * application. + * + *

Overrides any existing handler registration for (the name of) this channel. + * + *

If no handler has been registered, any incoming message on this channel will be handled + * silently by sending a null reply. + * + * @param handler a {@link MessageHandler}, or null to deregister. + */ + setMessageHandler(handler: SendableMessageHandler | null): void { + this.messenger.setMessageHandler(this.name, + handler == null ? null : new IncomingSendableMessageHandler(handler, this.codec), this.taskQueue); + } + + /** + * Adjusts the number of messages that will get buffered when sending messages to channels that + * aren't fully set up yet. For example, the engine isn't running yet or the channel's message + * handler isn't set up on the Dart side yet. + */ + resizeChannelBuffer(newSize: number): void { + MessageChannelUtils.resizeChannelBuffer(this.messenger, this.name, newSize); + } +} + + +class IncomingReplyHandler implements BinaryReply { + private callback: (reply: T)=>void; + private codec: SendableMessageCodec + + constructor(callback:(reply: T)=>void, codec: SendableMessageCodec) { + this.callback = callback + this.codec = codec + } + + reply(reply: ArrayBuffer | null) { + try { + this.callback(this.codec.decodeMessage(reply)); + } catch (e) { + Log.e(BackgroundBasicMessageChannel.TAG, "Failed to handle message reply", e); + } + } +} + +@Sendable +class IncomingSendableMessageHandler implements SendableBinaryMessageHandler { + private handler: SendableMessageHandler + private codec: SendableMessageCodec + + constructor(handler: SendableMessageHandler, codec: SendableMessageCodec) { + this.handler = handler; + this.codec = codec + } + + onMessage(message: ArrayBuffer, callback: BinaryReply) { + try { + this.handler.onMessage( + this.codec.decodeMessage(message), + { + reply: (reply: T): void => { + callback.reply(this.codec.encodeMessage(reply)); + } + }); + } catch (e) { + Log.e('WARNNING', "Failed to handle message: ", e); + callback.reply(StringUtils.stringToArrayBuffer("")); + } + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/BackgroundMethodChannel.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/BackgroundMethodChannel.ets new file mode 100644 index 0000000000000000000000000000000000000000..912d525917913c4e0b841584d0e6712b2f0d4de7 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/BackgroundMethodChannel.ets @@ -0,0 +1,162 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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. +*/ +import Log from '../../util/Log'; +import MessageChannelUtils from '../../util/MessageChannelUtils'; +import StringUtils from '../../util/StringUtils'; +import { BinaryMessenger, BinaryReply, TaskQueue } from './BinaryMessenger'; +import Any from './Any'; +import MethodCall from './MethodCall'; +import MethodCodec from './MethodCodec'; +import { MethodResult } from './MethodChannel' +import SendableStandardMethodCodec from './SendableStandardMethodCodec'; +import SendableMethodCallHandler from './SendableMethodCallHandler' +import SendableMethodCodec from './SendableMethodCodec' +import SendableBinaryMessageHandler from './SendableBinaryMessageHandler' + +/** + * A named channel for communicating with the Flutter application using asynchronous method calls. + * + *

Incoming method calls are decoded from binary on receipt, and Java results are encoded into + * binary before being transmitted back to Flutter. The {@link MethodCodec} used must be compatible + * with the one used by the Flutter application. This can be achieved by creating a MethodChannel + * counterpart of this channel on the Dart side. The Java type of method call arguments and results + * is {@code Object}, but only values supported by the specified {@link MethodCodec} can be used. + * + *

The logical identity of the channel is given by its name. Identically named channels will + * interfere with each other's communication. + */ + +export default class BackgroundMethodChannel { + static TAG = "BackgroundMethodChannel#"; + private messenger: BinaryMessenger; + private name: string; + private codec: SendableMethodCodec; + private taskQueue: TaskQueue; + private args: Object[]; + + constructor(messenger: BinaryMessenger, + name: string, + codec: SendableMethodCodec = SendableStandardMethodCodec.INSTANCE, + taskQueue?: TaskQueue, + ...args: Object[]) { + this.messenger = messenger + this.name = name + this.codec = codec + this.taskQueue = taskQueue ?? messenger.makeBackgroundTaskQueue() + this.args = args + } + + /** + * Invokes a method on this channel, optionally expecting a result. + * + *

Any uncaught exception thrown by the result callback will be caught and logged. + * + * @param method the name String of the method. + * @param arguments the arguments for the invocation, possibly null. + * @param callback a {@link Result} callback for the invocation result, or null. + */ + invokeMethod(method: string, args: Any, callback?: MethodResult): void { + this.messenger.send(this.name, + this.codec.encodeMethodCall(new MethodCall(method, args)), + callback == null ? null : new IncomingSendableResultHandler(callback, this.codec)); + } + + /** + * Registers a method call handler on this channel. + * + *

Overrides any existing handler registration for (the name of) this channel. + * + *

If no handler has been registered, any incoming method call on this channel will be handled + * silently by sending a null reply. This results in a MissingPluginException + * on the Dart side, unless an OptionalMethodChannel + * is used. + * + * @param handler a {@link MethodCallHandler}, or null to deregister. + */ + setMethodCallHandler(handler: SendableMethodCallHandler | null): void { + this.messenger.setMessageHandler(this.name, + handler == null ? null : new IncomingSendableMethodCallHandler(handler, this.codec), + this.taskQueue, ...this.args); + } + + /** + * Adjusts the number of messages that will get buffered when sending messages to channels that + * aren't fully set up yet. For example, the engine isn't running yet or the channel's message + * handler isn't set up on the Dart side yet. + */ + resizeChannelBuffer(newSize: number): void { + MessageChannelUtils.resizeChannelBuffer(this.messenger, this.name, newSize); + } +} + +export class IncomingSendableResultHandler implements BinaryReply { + private callback: MethodResult; + private codec: SendableMethodCodec; + + constructor(callback: MethodResult, codec: SendableMethodCodec) { + this.callback = callback; + this.codec = codec + } + + reply(reply: ArrayBuffer | null): void { + try { + if (reply == null) { + this.callback.notImplemented(); + } else { + try { + this.callback.success(this.codec.decodeEnvelope(reply)); + } catch (e) { + this.callback.error(e.code, e.getMessage(), e.details); + } + } + } catch (e) { + Log.e(BackgroundMethodChannel.TAG, "Failed to handle method call result", e); + } + } +} + +@Sendable +export class IncomingSendableMethodCallHandler implements SendableBinaryMessageHandler { + private handler: SendableMethodCallHandler; + private codec: SendableMethodCodec; + + constructor(handler: SendableMethodCallHandler, codec: SendableMethodCodec) { + this.handler = handler; + this.codec = codec; + } + + onMessage(message: ArrayBuffer, reply: BinaryReply, ...args: Object[]): void { + try { + this.handler.onMethodCall( + this.codec.decodeMethodCall(message), + { + success: (result: Any): void => { + reply.reply(this.codec.encodeSuccessEnvelope(result)); + }, + error: (errorCode: string, errorMessage: string, errorDetails: Any): void => { + reply.reply(this.codec.encodeErrorEnvelope(errorCode, errorMessage, errorDetails)); + }, + notImplemented: (): void => { + reply.reply(StringUtils.stringToArrayBuffer("")); + } + }, ...args); + } catch (e) { + reply.reply(this.codec.encodeErrorEnvelopeWithStacktrace("error", e.getMessage(), null, e)); + } + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/BasicMessageChannel.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/BasicMessageChannel.ets index 5a58b8c3ca0daa6bd51f0281aa968904b1e68411..01a4a194f644701f130c5718324567ff4e3b27a0 100644 --- a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/BasicMessageChannel.ets +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/BasicMessageChannel.ets @@ -12,7 +12,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - import MessageChannelUtils from '../../util/MessageChannelUtils'; import { BinaryMessageHandler } from './BinaryMessenger'; import Log from '../../util/Log'; @@ -42,13 +41,11 @@ export default class BasicMessageChannel { private messenger: BinaryMessenger; private name: string; private codec: MessageCodec; - private taskQueue?: TaskQueue; - constructor(messenger: BinaryMessenger, name: string, codec: MessageCodec, taskQueue?: TaskQueue) { + constructor(messenger: BinaryMessenger, name: string, codec: MessageCodec) { this.messenger = messenger this.name = name this.codec = codec - this.taskQueue = taskQueue } /** @@ -75,14 +72,7 @@ export default class BasicMessageChannel { * @param handler a {@link MessageHandler}, or null to deregister. */ setMessageHandler(handler: MessageHandler | null): void { - // We call the 2 parameter variant specifically to avoid breaking changes in - // mock verify calls. - // See https://github.com/flutter/flutter/issues/92582. - if (this.taskQueue != null) { - this.messenger.setMessageHandler(this.name, new IncomingMessageHandler(handler, this.codec), this.taskQueue); - } else { - this.messenger.setMessageHandler(this.name, new IncomingMessageHandler(handler, this.codec)); - } + this.messenger.setMessageHandler(this.name, handler == null ? null : new IncomingMessageHandler(handler, this.codec)); } /** @@ -146,17 +136,17 @@ class IncomingReplyHandler implements BinaryReply { } class IncomingMessageHandler implements BinaryMessageHandler { - private handler: MessageHandler | null + private handler: MessageHandler private codec: MessageCodec - constructor(handler: MessageHandler | null, codec: MessageCodec) { + constructor(handler: MessageHandler, codec: MessageCodec) { this.handler = handler; this.codec = codec } onMessage(message: ArrayBuffer, callback: BinaryReply) { try { - this.handler?.onMessage( + this.handler.onMessage( this.codec.decodeMessage(message), { reply: (reply: T): void => { @@ -168,4 +158,4 @@ class IncomingMessageHandler implements BinaryMessageHandler { callback.reply(StringUtils.stringToArrayBuffer("")); } } -} \ No newline at end of file +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/BinaryMessenger.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/BinaryMessenger.ets index 68925a8b1abcaf1f99964f67e52da6e99cc8ba8c..19e9a35f6757844f1d9231f791bd7cf7b89bb615 100644 --- a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/BinaryMessenger.ets +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/BinaryMessenger.ets @@ -21,20 +21,57 @@ * channels' constructors to control the threading policy for handling platform channels' * messages. */ + +import SendableBinaryMessageHandler from './SendableBinaryMessageHandler' + export interface TaskQueue {} +/** + * The priority of task execution + * + * This priority is guaranteed to be compatible with + * https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-taskpool-V5#priority. + * + */ +export enum TaskPriority { + HIGH = 0, + MEDIUM = 1, + LOW = 2, + IDLE = 3 +} + /** Options that control how a TaskQueue should operate and be created. */ export class TaskQueueOptions { - private isSerial = true; + private isSerial: boolean = true; + private isSingleThread: boolean = false; + private priority: TaskPriority = TaskPriority.MEDIUM; - getIsSerial() { - return this.isSerial; - } + getIsSerial():boolean { + return this.isSerial; + } - setIsSerial(isSerial: boolean): TaskQueueOptions { - this.isSerial = isSerial; - return this; - } + setIsSerial(isSerial: boolean): TaskQueueOptions { + this.isSerial = isSerial; + return this; + } + + getPriority(): TaskPriority { + return this.priority; + } + + setPriority(priority: TaskPriority): TaskQueueOptions { + this.priority = priority; + return this; + } + + isSingleThreadMode(): boolean { + return this.isSingleThread; + } + + setSingleThreadMode(isSingleThread: boolean): TaskQueueOptions { + this.isSingleThread = isSingleThread; + return this; + } } /** @@ -42,32 +79,32 @@ export class TaskQueueOptions { * used in the dual capacity to handle a reply received from Flutter after sending a message. */ export interface BinaryReply { - /** - * Handles the specified reply. - * - * @param reply the reply payload, a direct-allocated {@link ByteBuffer} or null. Senders of - * outgoing replies must place the reply bytes between position zero and current position. - * Reply receivers can read from the buffer directly. - */ - reply(reply: ArrayBuffer | null): void; + /** + * Handles the specified reply. + * + * @param reply the reply payload, a direct-allocated {@link ByteBuffer} or null. Senders of + * outgoing replies must place the reply bytes between position zero and current position. + * Reply receivers can read from the buffer directly. + */ + reply: (reply: ArrayBuffer | null) => void; } /** Handler for incoming binary messages from Flutter. */ export interface BinaryMessageHandler { - /** - * Handles the specified message. - * - *

Handler implementations must reply to all incoming messages, by submitting a single reply - * message to the given {@link BinaryReply}. Failure to do so will result in lingering Flutter - * reply handlers. The reply may be submitted asynchronously. - * - *

Any uncaught exception thrown by this method will be caught by the messenger - * implementation and logged, and a null reply message will be sent back to Flutter. - * - * @param message the message {@link ByteBuffer} payload, possibly null. - * @param reply A {@link BinaryReply} used for submitting a reply back to Flutter. - */ - onMessage(message: ArrayBuffer, reply: BinaryReply): void; + /** + * Handles the specified message. + * + *

Handler implementations must reply to all incoming messages, by submitting a single reply + * message to the given {@link BinaryReply}. Failure to do so will result in lingering Flutter + * reply handlers. The reply may be submitted asynchronously. + * + *

Any uncaught exception thrown by this method will be caught by the messenger + * implementation and logged, and a null reply message will be sent back to Flutter. + * + * @param message the message {@link ByteBuffer} payload, possibly null. + * @param reply A {@link BinaryReply} used for submitting a reply back to Flutter. + */ + onMessage(message: ArrayBuffer, reply: BinaryReply): void; } /** @@ -86,73 +123,46 @@ export interface BinaryMessageHandler { * @see MethodChannel , which supports communication using asynchronous method invocation. * @see EventChannel , which supports communication using event streams. */ + export interface BinaryMessenger { - makeBackgroundTaskQueue(options?: TaskQueueOptions): TaskQueue; - - /** - * Sends a binary message to the Flutter application. - * - * @param channel the name {@link String} of the logical channel used for the message. - * @param message the message payload, a direct-allocated {@link ByteBuffer} with the message - * bytes between position zero and current position, or null. - */ - send(channel: String, message: ArrayBuffer | null): void; - - /** - * Sends a binary message to the Flutter application, optionally expecting a reply. - * - *

Any uncaught exception thrown by the reply callback will be caught and logged. - * - * @param channel the name {@link String} of the logical channel used for the message. - * @param message the message payload, a direct-allocated {@link ByteBuffer} with the message - * bytes between position zero and current position, or null. - * @param callback a {@link BinaryReply} callback invoked when the Flutter application responds to - * the message, possibly null. - */ - send(channel: String, message: ArrayBuffer, callback?: BinaryReply | null): void; - - /** - * Registers a handler to be invoked when the Flutter application sends a message to its host - * platform. - * - *

Registration overwrites any previous registration for the same channel name. Use a null - * handler to deregister. - * - *

If no handler has been registered for a particular channel, any incoming message on that - * channel will be handled silently by sending a null reply. - * - * @param channel the name {@link String} of the channel. - * @param handler a {@link BinaryMessageHandler} to be invoked on incoming messages, or null. - * @param taskQueue a {@link BinaryMessenger.TaskQueue} that specifies what thread will execute - * the handler. Specifying null means execute on the platform thread. - */ - //setMessageHandler(channel: String, handler: BinaryMessageHandler) - setMessageHandler(channel: String, handler: BinaryMessageHandler | null, taskQueue?: TaskQueue): void; - // { - // if (taskQueue != null) { - // throw new Error("setMessageHandler called with nonnull taskQueue is not supported.") - // } - // } - - /** - * Enables the ability to queue messages received from Dart. - * - *

This is useful when there are pending channel handler registrations. For example, Dart may - * be initialized concurrently, and prior to the registration of the channel handlers. This - * implies that Dart may start sending messages while plugins are being registered. - */ - enableBufferingIncomingMessages(): void; - // { - // throw new Error("enableBufferingIncomingMessages not implemented."); - // } - - /** - * Disables the ability to queue messages received from Dart. - * - *

This can be used after all pending channel handlers have been registered. - */ - disableBufferingIncomingMessages(): void; - // { - // throw new Error("disableBufferingIncomingMessages not implemented."); - // } + makeBackgroundTaskQueue(options?: TaskQueueOptions): TaskQueue; + + /** + * Sends a binary message to the Flutter application. + * + * @param channel the name {@link String} of the logical channel used for the message. + * @param message the message payload, a direct-allocated {@link ByteBuffer} with the message + * bytes between position zero and current position, or null. + */ + send(channel: String, message: ArrayBuffer | null): void; + + /** + * Sends a binary message to the Flutter application, optionally expecting a reply. + * + *

Any uncaught exception thrown by the reply callback will be caught and logged. + * + * @param channel the name {@link String} of the logical channel used for the message. + * @param message the message payload, a direct-allocated {@link ByteBuffer} with the message + * bytes between position zero and current position, or null. + * @param callback a {@link BinaryReply} callback invoked when the Flutter application responds to + * the message, possibly null. + */ + send(channel: String, message: ArrayBuffer, callback?: BinaryReply | null): void; + + /** + * Registers a handler to be invoked when the Flutter application sends a message to its host + * platform. + * + *

Registration overwrites any previous registration for the same channel name. Use a null + * handler to deregister. + * + *

If no handler has been registered for a particular channel, any incoming message on that + * channel will be handled silently by sending a null reply. + * + * @param channel the name {@link String} of the channel. + * @param handler a {@link BinaryMessageHandler} to be invoked on incoming messages, or null. + * @param taskQueue a {@link BinaryMessenger.TaskQueue} that specifies what thread will execute + * the handler. Specifying null means execute on the platform thread. + */ + setMessageHandler(channel: String, handler: BinaryMessageHandler | SendableBinaryMessageHandler | null, taskQueue?: TaskQueue, ...args: Object[]): void; } diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/EventChannel.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/EventChannel.ets index f0aacdd8453e6f418fea0b57507b71283f453193..0affd6b72708e69ff22a6e8b774bbf19de237f7a 100644 --- a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/EventChannel.ets +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/EventChannel.ets @@ -47,7 +47,9 @@ export default class EventChannel { this.messenger = messenger this.name = name this.codec = codec ? codec : StandardMethodCodec.INSTANCE - this.taskQueue = taskQueue ?? null + // TODO:(0xZOne): 实现后台处理 + // this.taskQueue = taskQueue ?? null + this.taskQueue = null } diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/JSONMessageCodec.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/JSONMessageCodec.ets index 451d3d026921232464734dba63dfd08dd41779b2..4f0e7ab2bd8f68a0587cc16e1b10d5a1ac61c63b 100644 --- a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/JSONMessageCodec.ets +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/JSONMessageCodec.ets @@ -15,7 +15,6 @@ import StringUtils from '../../util/StringUtils'; import MessageCodec from './MessageCodec'; -import MethodCodec from './MethodCodec'; import StringCodec from './StringCodec'; import TreeMap from '@ohos.util.TreeMap'; import HashMap from '@ohos.util.HashMap'; @@ -26,7 +25,7 @@ import LinkedList from '@ohos.util.LinkedList'; import Any from './Any'; /** - * A {@link MethodCodec} using UTF-8 encoded JSON method calls and result envelopes. + * A {@link MessageCodec} using UTF-8 encoded JSON method calls and result envelopes. * *

This codec is guaranteed to be compatible with the corresponding JSONMethodCodec on diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/MessageCodec.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/MessageCodec.ets index 6cb2c033b63e20323e89aaecec9893bdfbadb92e..4fdd52011b4ecbd0e6509864581000e5d8c0ad15 100644 --- a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/MessageCodec.ets +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/MessageCodec.ets @@ -27,4 +27,4 @@ export default interface MessageCodec { * */ decodeMessage(message: ArrayBuffer | null): T; -} \ No newline at end of file +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/MethodChannel.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/MethodChannel.ets index a5af095a62b5c2f65e37c65dad4224e8a5aad8d7..07eeb637cd9132db4bec808be340db56fd874fea 100644 --- a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/MethodChannel.ets +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/MethodChannel.ets @@ -16,11 +16,12 @@ import Log from '../../util/Log'; import MessageChannelUtils from '../../util/MessageChannelUtils'; import StringUtils from '../../util/StringUtils'; -import { BinaryMessageHandler, BinaryMessenger, BinaryReply, TaskQueue } from './BinaryMessenger'; +import { BinaryMessageHandler, BinaryMessenger, BinaryReply } from './BinaryMessenger'; import Any from './Any'; import MethodCall from './MethodCall'; import MethodCodec from './MethodCodec'; import StandardMethodCodec from './StandardMethodCodec'; + /** * A named channel for communicating with the Flutter application using asynchronous method calls. * @@ -40,13 +41,11 @@ export default class MethodChannel { private messenger: BinaryMessenger; private name: string; private codec: MethodCodec; - private taskQueue?: TaskQueue; - constructor(messenger: BinaryMessenger, name: string, codec: MethodCodec = StandardMethodCodec.INSTANCE, taskQueue?: TaskQueue) { + constructor(messenger: BinaryMessenger, name: string, codec: MethodCodec = StandardMethodCodec.INSTANCE) { this.messenger = messenger this.name = name this.codec = codec - this.taskQueue = taskQueue } /** @@ -76,17 +75,8 @@ export default class MethodChannel { * * @param handler a {@link MethodCallHandler}, or null to deregister. */ - setMethodCallHandler(handler: MethodCallHandler): void { - // We call the 2 parameter variant specifically to avoid breaking changes in - // mock verify calls. - // See https://github.com/flutter/flutter/issues/92582. - if (this.taskQueue != null) { - this.messenger.setMessageHandler( - this.name, new IncomingMethodCallHandler(handler, this.codec), this.taskQueue); - } else { - this.messenger.setMessageHandler( - this.name, new IncomingMethodCallHandler(handler, this.codec)); - } + setMethodCallHandler(handler: MethodCallHandler | null): void { + this.messenger.setMessageHandler(this.name, handler == null ? null : new IncomingMethodCallHandler(handler, this.codec)); } /** @@ -215,4 +205,4 @@ export class IncomingMethodCallHandler implements BinaryMessageHandler { reply.reply(this.codec.encodeErrorEnvelopeWithStacktrace("error", e.getMessage(), null, e)); } } -} \ No newline at end of file +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableBinaryCodec.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableBinaryCodec.ets new file mode 100644 index 0000000000000000000000000000000000000000..3874e37028199fd8a68cf04b4a0ad25820491b97 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableBinaryCodec.ets @@ -0,0 +1,50 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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. +*/ + +import SendableMessageCodec from './SendableMessageCodec'; + +/** + * A {@link MessageCodec} using unencoded binary messages, represented as {@link ByteBuffer}s. + * + *

This codec is guaranteed to be compatible with the corresponding BinaryCodec on the + * Dart side. These parts of the Flutter SDK are evolved synchronously. + * + *

On the Dart side, messages are represented using {@code ByteData}. + */ + +@Sendable +export default class SendableBinaryCodec implements SendableMessageCodec { + private returnsDirectByteBufferFromDecoding: boolean = false; + static readonly INSTANCE_DIRECT: SendableBinaryCodec = new SendableBinaryCodec(true); + + constructor(returnsDirectByteBufferFromDecoding: boolean) { + this.returnsDirectByteBufferFromDecoding = returnsDirectByteBufferFromDecoding; + } + + encodeMessage(message: ArrayBuffer): ArrayBuffer { + return message + } + + decodeMessage(message: ArrayBuffer | null): ArrayBuffer { + if (message == null) { + return new ArrayBuffer(0); + } else if (this.returnsDirectByteBufferFromDecoding) { + return message; + } else { + return message.slice(0, message.byteLength); + } + } +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableBinaryMessageHandler.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableBinaryMessageHandler.ets new file mode 100644 index 0000000000000000000000000000000000000000..d59ef697569d175e7c048b789a3dc02effb55503 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableBinaryMessageHandler.ets @@ -0,0 +1,21 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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. +*/ +import { lang } from '@kit.ArkTS'; +import { BinaryReply } from './BinaryMessenger'; + +type ISendable = lang.ISendable; +export default interface SendableBinaryMessageHandler extends ISendable { + onMessage(message: ArrayBuffer, reply: BinaryReply, ...args: Object[]): void; +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableJSONMessageCodec.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableJSONMessageCodec.ets new file mode 100644 index 0000000000000000000000000000000000000000..44abb977cf85359b885c5795ecd95706bc36d13e --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableJSONMessageCodec.ets @@ -0,0 +1,102 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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. +*/ +import StringUtils from '../../util/StringUtils'; + +import SendableMessageCodec from './SendableMessageCodec'; +import StringCodec from './StringCodec'; +import TreeMap from '@ohos.util.TreeMap'; +import HashMap from '@ohos.util.HashMap'; +import LightWeightMap from '@ohos.util.LightWeightMap'; +import PlainArray from '@ohos.util.PlainArray'; +import List from '@ohos.util.List'; +import LinkedList from '@ohos.util.LinkedList'; +import Any from './Any'; + +/** + * A {@link MessageCodec} using UTF-8 encoded JSON method calls and result envelopes. + * + *

This codec is guaranteed to be compatible with the corresponding JSONMethodCodec on + * the Dart side. These parts of the Flutter SDK are evolved synchronously. + * + *

On the Dart side, JSON messages are handled by the JSON facilities of the dart:convert package. + */ +@Sendable +export default class SendableJSONMessageCodec implements SendableMessageCodec { + static INSTANCE: SendableJSONMessageCodec = new SendableJSONMessageCodec(); + + encodeMessage(message: Any): ArrayBuffer { + if (message == null) { + return StringUtils.stringToArrayBuffer(""); + } + return StringCodec.INSTANCE.encodeMessage(JSON.stringify(this.toBaseData(message))); + } + + decodeMessage(message: ArrayBuffer | null): Any { + if (message == null) { + return StringUtils.stringToArrayBuffer(""); + } + try { + const jsonStr = StringCodec.INSTANCE.decodeMessage(message); + let jsonObj: Record = JSON.parse(jsonStr); + if (jsonObj instanceof Object) { + const list = Object.keys(jsonObj); + if (list.includes('args')) { + let args: Any = jsonObj['args']; + if (args instanceof Object && !(args instanceof Array)) { + let argsMap: Map = new Map(); + Object.keys(args).forEach(key => { + argsMap.set(key, args[key]); + }) + jsonObj['args'] = argsMap; + } + } + } + return jsonObj; + } catch (e) { + throw new Error("Invalid JSON"); + } + } + + toBaseData(message: Any): Any { + if (message == null || message == undefined) { + return ""; + } else if (message instanceof List || message instanceof LinkedList) { + return this.toBaseData(message.convertToArray()); + } else if (message instanceof Map || message instanceof HashMap || message instanceof TreeMap + || message instanceof LightWeightMap || message instanceof PlainArray) { + let messageObj: Any = {}; + message.forEach((value: Any, key: Any) => { + messageObj[this.toBaseData(key)] = this.toBaseData(value); + }); + return messageObj; + } else if (message instanceof Array) { + let messageArr:Array = []; + message.forEach((value:Any)=>{ + messageArr.push(this.toBaseData(value)); + }) + return messageArr; + } else if (message instanceof Object) { + let messageObj: Any = {}; + Object.keys(message).forEach((key:Any)=>{ + messageObj[this.toBaseData(key)] = this.toBaseData(message[key]); + }) + return messageObj; + } else { + return message; + } + } +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableJSONMethodCodec.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableJSONMethodCodec.ets new file mode 100644 index 0000000000000000000000000000000000000000..55c96b523a490c673462bdaf07313199166b094d --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableJSONMethodCodec.ets @@ -0,0 +1,97 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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. +*/ +import ToolUtils from '../../util/ToolUtils'; +import FlutterException from './FlutterException'; +import Any from './Any'; +import SendableJSONMessageCodec from './SendableJSONMessageCodec'; +import MethodCall from './MethodCall'; +import SendableMethodCodec from './SendableMethodCodec'; + +/** + * A {@link SendableMethodCodec} using UTF-8 encoded JSON method calls and result envelopes. + * + *

This codec is guaranteed to be compatible with the corresponding JSONMethodCodec on + * the Dart side. These parts of the Flutter SDK are evolved synchronously. + * + *

Values supported as methods arguments and result payloads are those supported by {@link + * SendableJSONMessageCodec}. + */ +@Sendable +export default class SendableJSONMethodCodec implements SendableMethodCodec { + static INSTANCE: SendableJSONMethodCodec = new SendableJSONMethodCodec(); + + encodeMethodCall(methodCall: MethodCall): ArrayBuffer { + try { + const map: Record = { + "method": methodCall.method, "args": methodCall.args + } + + return SendableJSONMessageCodec.INSTANCE.encodeMessage(map); + } catch (e) { + throw new Error("Invalid JSON"); + } + } + + decodeMethodCall(message: ArrayBuffer): MethodCall { + try { + const json: Any = SendableJSONMessageCodec.INSTANCE.decodeMessage(message); + if (ToolUtils.isObj(json)) { + const method: string = json["method"]; + const args: Any = json["args"]; + if (typeof method == 'string') { + return new MethodCall(method, args); + } + } + throw new Error("Invalid method call: " + json); + } catch (e) { + throw new Error("Invalid JSON:" + JSON.stringify(e)); + } + } + + encodeSuccessEnvelope(result: Any): ArrayBuffer { + return SendableJSONMessageCodec.INSTANCE.encodeMessage([result]); + } + + encodeErrorEnvelope(errorCode: Any, errorMessage: string, errorDetails: Any) { + return SendableJSONMessageCodec.INSTANCE.encodeMessage([errorCode, errorMessage, errorDetails]); + } + + encodeErrorEnvelopeWithStacktrace(errorCode: string, errorMessage: string, errorDetails: Any, errorStacktrace: string): ArrayBuffer { + return SendableJSONMessageCodec.INSTANCE.encodeMessage([errorCode, errorMessage, errorDetails, errorStacktrace]) + } + + decodeEnvelope(envelope: ArrayBuffer): Any { + try { + const json: Any = SendableJSONMessageCodec.INSTANCE.decodeMessage(envelope); + if (json instanceof Array) { + if (json.length == 1) { + return json[0]; + } + if (json.length == 3) { + const code: string = json[0]; + const message: string = json[1]; + const details: Any = json[2]; + if (typeof code == 'string' && (message == null || typeof message == 'string')) { + throw new FlutterException(code, message, details); + } + } + } + throw new Error("Invalid envelope: " + json); + } catch (e) { + throw new Error("Invalid JSON"); + } + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableMessageCodec.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableMessageCodec.ets new file mode 100644 index 0000000000000000000000000000000000000000..d58ffa6e27e6322a03a002297fba7e7a5bfe6a5a --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableMessageCodec.ets @@ -0,0 +1,29 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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. +*/ +import { lang } from '@kit.ArkTS'; + +type ISendable = lang.ISendable; +export default interface SendableMessageCodec extends ISendable { + /** + * Encodes the specified message into binary. + */ + encodeMessage(message: T) : ArrayBuffer; + + /** + * Decodes the specified message from binary. + * + */ + decodeMessage(message: ArrayBuffer | null): T; +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableMessageHandler.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableMessageHandler.ets new file mode 100644 index 0000000000000000000000000000000000000000..8322b882c90c29650e536d6eff9a2f7aaf382f26 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableMessageHandler.ets @@ -0,0 +1,21 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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. +*/ +import { lang } from '@kit.ArkTS'; +import { Reply} from './BasicMessageChannel'; + +type ISendable = lang.ISendable; +export default interface SendableMessageHandler extends ISendable { + onMessage(message: T, reply: Reply): void; +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableMethodCallHandler.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableMethodCallHandler.ets new file mode 100644 index 0000000000000000000000000000000000000000..ae80345181e09eb36d2ae8533a8f0f7d8e1cb0a7 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableMethodCallHandler.ets @@ -0,0 +1,45 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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. +*/ +import { lang } from '@kit.ArkTS'; + +import MethodCall from './MethodCall'; +import { MethodResult } from './MethodChannel'; + +/** A handler of incoming method calls. */ +type ISendable = lang.ISendable; +export default interface SendableMethodCallHandler extends ISendable { + // isSendableMethodCallHandler: () => boolean; + /** + * Handles the specified method call received from Flutter. + * + *

Handler implementations must submit a result for all incoming calls, by making a single + * call on the given {@link Result} callback. Failure to do so will result in lingering Flutter + * result handlers. The result may be submitted asynchronously and on any thread. Calls to + * unknown or unimplemented methods should be handled using {@link Result#notImplemented()}. + * + *

Any uncaught exception thrown by this method will be caught by the channel implementation + * and logged, and an error result will be sent back to Flutter. + * + *

The handler is called on the platform thread (Android main thread) by default, or + * otherwise on the thread specified by the {@link BinaryMessenger.TaskQueue} provided to the + * associated {@link MethodChannel} when it was created. See also Threading in + * the Flutter Engine. + * + * @param call A {@link MethodCall}. + * @param result A {@link Result} used for submitting the result of the call. + */ + onMethodCall(call: MethodCall, result: MethodResult, ...args: Object[]): void; +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableMethodCodec.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableMethodCodec.ets new file mode 100644 index 0000000000000000000000000000000000000000..05bdcc403f9975e5fce7efc8cfc44120275dfb3d --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableMethodCodec.ets @@ -0,0 +1,90 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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. +*/ +import { lang } from '@kit.ArkTS'; + +import Any from './Any'; +import MethodCall from './MethodCall'; +/** + * A codec for method calls and enveloped results. + * + *

Method calls are encoded as binary messages with enough structure that the codec can extract a + * method name String and an arguments Object. These data items are used to populate a {@link + * MethodCall}. + * + *

All operations throw {@link IllegalArgumentException}, if conversion fails. + */ +type ISendable = lang.ISendable; +export default interface SendableMethodCodec extends ISendable { + /** + * Encodes a message call into binary. + * + * @param methodCall a {@link MethodCall}. + * @return a {@link ByteBuffer} containing the encoding between position 0 and the current + * position. + */ + encodeMethodCall(methodCall: MethodCall): ArrayBuffer; + + /** + * Decodes a message call from binary. + * + * @param methodCall the binary encoding of the method call as a {@link ByteBuffer}. + * @return a {@link MethodCall} representation of the bytes between the given buffer's current + * position and its limit. + */ + decodeMethodCall(methodCall: ArrayBuffer): MethodCall; + + /** + * Encodes a successful result into a binary envelope message. + * + * @param result The result value, possibly null. + * @return a {@link ByteBuffer} containing the encoding between position 0 and the current + * position. + */ + encodeSuccessEnvelope(result: Any): ArrayBuffer; + + /** + * Encodes an error result into a binary envelope message. + * + * @param errorCode An error code String. + * @param errorMessage An error message String, possibly null. + * @param errorDetails Error details, possibly null. Consider supporting {@link Throwable} in your + * codec. This is the most common value passed to this field. + * @return a {@link ByteBuffer} containing the encoding between position 0 and the current + * position. + */ + encodeErrorEnvelope(errorCode: string, errorMessage: string, errorDetails: Any): ArrayBuffer; + + /** + * Encodes an error result into a binary envelope message with the native stacktrace. + * + * @param errorCode An error code String. + * @param errorMessage An error message String, possibly null. + * @param errorDetails Error details, possibly null. Consider supporting {@link Throwable} in your + * codec. This is the most common value passed to this field. + * @param errorStacktrace Platform stacktrace for the error. possibly null. + * @return a {@link ByteBuffer} containing the encoding between position 0 and the current + * position. + */ + encodeErrorEnvelopeWithStacktrace(errorCode: string, errorMessage: string, errorDetails: Any, errorStacktrace: string): ArrayBuffer + + /** + * Decodes a result envelope from binary. + * + * @param envelope the binary encoding of a result envelope as a {@link ByteBuffer}. + * @return the enveloped result Object. + * @throws FlutterException if the envelope was an error envelope. + */ + decodeEnvelope(envelope: ArrayBuffer): Any +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableStandardMessageCodec.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableStandardMessageCodec.ets new file mode 100644 index 0000000000000000000000000000000000000000..dac0917a1fc7e110e98e4f62603e6dc886e5bc75 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableStandardMessageCodec.ets @@ -0,0 +1,365 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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. +*/ + +import Any from './Any'; + +import { ByteBuffer } from '../../util/ByteBuffer'; +import SendableMessageCodec from './SendableMessageCodec'; +import StringUtils from '../../util/StringUtils'; +import TreeMap from '@ohos.util.TreeMap'; +import HashMap from '@ohos.util.HashMap'; +import LightWeightMap from '@ohos.util.LightWeightMap'; +import PlainArray from '@ohos.util.PlainArray'; +import List from '@ohos.util.List'; +import LinkedList from '@ohos.util.LinkedList'; + +/** + * MessageCodec using the Flutter standard binary encoding. + * + *

This codec is guaranteed to be compatible with the corresponding SendableStandardMessageCodec + * on the Dart side. These parts of the Flutter SDK are evolved synchronously. + * + *

Supported messages are acyclic values of these forms: + * + *

    + *
  • null + *
  • Booleans + *
  • number + *
  • BigIntegers (see below) + *
  • Int8Array, Int32Array, Float32Array, Float64Array + *
  • Strings + *
  • Array[] + *
  • Lists of supported values + *
  • Maps with supported keys and values + *
+ * + *

On the Dart side, these values are represented as follows: + * + *

    + *
  • null: null + *
  • Boolean: bool + *
  • Byte, Short, Integer, Long: int + *
  • Float, Double: double + *
  • String: String + *
  • byte[]: Uint8List + *
  • int[]: Int32List + *
  • long[]: Int64List + *
  • float[]: Float32List + *
  • double[]: Float64List + *
  • List: List + *
  • Map: Map + *
+ * + *

BigIntegers are represented in Dart as strings with the hexadecimal representation of the + * integer's value. + * + *

To extend the codec, overwrite the writeValue and readValueOfType methods. + */ +@Sendable +export default class SendableStandardMessageCodec implements SendableMessageCodec { + static INSTANCE: SendableStandardMessageCodec = new SendableStandardMessageCodec(); + + encodeMessage(message: Any): ArrayBuffer { + const stream = ByteBuffer.from(new ArrayBuffer(1024)) + this.writeValue(stream, message); + return stream.buffer + } + + decodeMessage(message: ArrayBuffer | null): Any { + if (message == null) { + return null + } + const buffer = ByteBuffer.from(message) + return this.readValue(buffer) + } + + private static NULL: number = 0; + private static TRUE: number = 1; + private static FALSE: number = 2; + private static INT32: number = 3; + private static INT64: number = 4; + private static BIGINT: number = 5; + private static FLOAT64: number = 6; + private static STRING: number = 7; + private static UINT8_ARRAY: number = 8; + private static INT32_ARRAY: number = 9; + private static INT64_ARRAY: number = 10; + private static FLOAT64_ARRAY: number = 11; + private static LIST: number = 12; + private static MAP: number = 13; + private static FLOAT32_ARRAY: number = 14; + + + writeValue(stream: ByteBuffer, value: Any): Any { + if (value == null || value == undefined) { + stream.writeInt8(SendableStandardMessageCodec.NULL); + } else if (typeof value === "boolean") { + stream.writeInt8(value ? SendableStandardMessageCodec.TRUE : SendableStandardMessageCodec.FALSE) + } else if (typeof value === "number") { + if (Number.isInteger(value)) { //整型 + if (-0x7fffffff - 1 <= value && value <= 0x7fffffff) { //int32 + stream.writeInt8(SendableStandardMessageCodec.INT32); + stream.writeInt32(value, true); + } else if (Number.MIN_SAFE_INTEGER <= value && value <= Number.MAX_SAFE_INTEGER) { //int64 number整型取值范围 + stream.writeInt8(SendableStandardMessageCodec.INT64); + stream.writeInt64(value, true); + } else { //被判为整型的double型 + stream.writeInt8(SendableStandardMessageCodec.FLOAT64); + this.writeAlignment(stream, 8); + stream.writeFloat64(value, true); + } + } else { //浮点型 + stream.writeInt8(SendableStandardMessageCodec.FLOAT64); + this.writeAlignment(stream, 8); + stream.writeFloat64(value, true); + } + } else if (typeof value === "bigint") { + // https://api.flutter.dev/flutter/services/SendableStandardMessageCodec/writeValue.html + // + // The format is first the type byte (0x05), then the actual number + // as an ASCII string giving the hexadecimal representation of the + // integer, with the string's length as encoded by writeSize + // followed by the string bytes. + stream.writeInt8(SendableStandardMessageCodec.BIGINT); + // Convert bigint to a hexadecimal string + const hexString = value.toString(16); + // Map each character in the hexadecimal string to its ASCII code + const asciiString = hexString.split('').map(char => char.charCodeAt(0)); + this.writeBytes(stream, Uint8Array.from(asciiString)); + } else if (typeof value === "string") { + stream.writeInt8(SendableStandardMessageCodec.STRING); + let stringBuff = StringUtils.stringToArrayBuffer(value); + this.writeBytes(stream, new Uint8Array(stringBuff)); + } else if (value instanceof Uint8Array) { + stream.writeInt8(SendableStandardMessageCodec.UINT8_ARRAY); + this.writeBytes(stream, value) + } else if (value instanceof Int32Array) { + stream.writeInt8(SendableStandardMessageCodec.INT32_ARRAY); + this.writeSize(stream, value.length); + this.writeAlignment(stream, 4); + value.forEach(item => stream.writeInt32(item, true)); + } else if(value instanceof BigInt64Array) { + stream.writeInt8(SendableStandardMessageCodec.INT64_ARRAY); + this.writeSize(stream, value.length); + this.writeAlignment(stream, 8); + value.forEach(item => stream.writeBigInt64(item, true)); + } else if (value instanceof Float32Array) { + stream.writeInt8(SendableStandardMessageCodec.FLOAT32_ARRAY); + this.writeSize(stream, value.length); + this.writeAlignment(stream, 4); + value.forEach(item => stream.writeFloat32(item, true)); + } else if (value instanceof Float64Array) { + stream.writeInt8(SendableStandardMessageCodec.FLOAT64_ARRAY); + this.writeSize(stream, value.length); + this.writeAlignment(stream, 8); + value.forEach(item => stream.writeFloat64(item, true)); + } else if (value instanceof Array || value instanceof Int8Array || value instanceof Int16Array + || value instanceof Uint16Array || value instanceof Uint32Array || value instanceof List + || value instanceof LinkedList) { + stream.writeInt8(SendableStandardMessageCodec.LIST) + this.writeSize(stream, value.length); + value.forEach((item: Any): void => this.writeValue(stream, item)); + } else if (value instanceof Map) { + stream.writeInt8(SendableStandardMessageCodec.MAP); + this.writeSize(stream, value.size); + value.forEach((value: Any, key: Any) => { + this.writeValue(stream, key); + this.writeValue(stream, value); + }); + } else if(value instanceof HashMap || value instanceof TreeMap || value instanceof LightWeightMap + || value instanceof PlainArray) { + stream.writeInt8(SendableStandardMessageCodec.MAP); + this.writeSize(stream, value.length); + value.forEach((value: Any, key: Any) => { + this.writeValue(stream, key); + this.writeValue(stream, value); + }); + } else if (typeof value == 'object') { + let map: Map = new Map(); + Object.keys(value).forEach(key => { + map.set(key, value[key]); + }); + this.writeValue(stream, map); + } else { + throw new Error("Unsupported value: " + value); + stream.writeInt8(SendableStandardMessageCodec.NULL); + } + return stream; + } + + writeAlignment(stream: ByteBuffer, alignment: number) { + let mod: number = stream.byteOffset % alignment; + if (mod != 0) { + for (let i = 0; i < alignment - mod; i++) { + stream.writeInt8(0); + } + } + } + + writeSize(stream: ByteBuffer, value: number) { + if (value < 254) { + stream.writeInt8(value); + } else if (value <= 0xffff) { + stream.writeInt8(254); + stream.writeInt16(value, true); + } else { + stream.writeInt8(255); + stream.writeInt32(value, true); + } + } + + writeBytes(stream: ByteBuffer, bytes: Uint8Array) { + this.writeSize(stream, bytes.length) + stream.writeUint8Array(bytes); + } + + readSize(buffer: ByteBuffer) { + let value = buffer.readInt8() & 0xff; + if (value < 254) { + return value; + } else if (value == 254) { + return buffer.readUint16(true); + } else { + return buffer.readInt32(true); + } + } + + readAlignment(buffer: ByteBuffer, alignment: number) { + let mod = buffer.byteOffset % alignment; + if (mod != 0) { + buffer.skip(alignment - mod); + } + } + + readValue(buffer: ByteBuffer): Any { + let type = buffer.readUint8() + return this.readValueOfType(type, buffer); + } + + readBytes(buffer: ByteBuffer): Uint8Array { + let length = this.readSize(buffer); + let bytes = new Uint8Array(length) + for (let i = 0; i < length; i++) { + bytes[i] = buffer.readUint8() + } + return bytes; + } + + readValueOfType(type: number, buffer: ByteBuffer): Any { + let result: Any; + switch (type) { + case SendableStandardMessageCodec.NULL: + result = null; + break; + case SendableStandardMessageCodec.TRUE: + result = true; + break; + case SendableStandardMessageCodec.FALSE: + result = false; + break; + case SendableStandardMessageCodec.INT32: + result = buffer.readInt32(true); + break; + case SendableStandardMessageCodec.INT64: + result = buffer.readInt64(true); + if (Number.MIN_SAFE_INTEGER <= result && result <= Number.MAX_SAFE_INTEGER) { + result = Number(result); + } + break; + case SendableStandardMessageCodec.BIGINT: + let bytes: Uint8Array = this.readBytes(buffer); + // Convert the byte array to a UTF-8 encoded string + const hexString: string = String.fromCharCode(...bytes); + // Parse the string as a hexadecimal BigInt + result = BigInt(`0x${hexString}`); + break; + case SendableStandardMessageCodec.FLOAT64: + this.readAlignment(buffer, 8); + result = buffer.readFloat64(true) + break; + case SendableStandardMessageCodec.STRING: { + let bytes: Uint8Array = this.readBytes(buffer); + result = StringUtils.uint8ArrayToString(bytes); + break; + } + case SendableStandardMessageCodec.UINT8_ARRAY: { + result = this.readBytes(buffer); + break; + } + case SendableStandardMessageCodec.INT32_ARRAY: { + let length = this.readSize(buffer); + let array = new Int32Array(length) + this.readAlignment(buffer, 4); + for (let i = 0; i < length; i++) { + array[i] = buffer.readInt32(true) + } + result = array; + break; + } + case SendableStandardMessageCodec.INT64_ARRAY: { + let length = this.readSize(buffer); + let array = new BigInt64Array(length) + this.readAlignment(buffer, 8); + for (let i = 0; i < length; i++) { + array[i] = buffer.readBigInt64(true) + } + result = array; + break; + } + case SendableStandardMessageCodec.FLOAT64_ARRAY: { + let length = this.readSize(buffer); + let array = new Float64Array(length) + this.readAlignment(buffer, 8); + for (let i = 0; i < length; i++) { + array[i] = buffer.readFloat64(true) + } + result = array; + break; + } + case SendableStandardMessageCodec.LIST: { + let length = this.readSize(buffer); + let array: Array = new Array(length) + for (let i = 0; i < length; i++) { + array[i] = this.readValue(buffer) + } + result = array; + break; + } + case SendableStandardMessageCodec.MAP: { + let size = this.readSize(buffer); + let map: Map = new Map() + for (let i = 0; i < size; i++) { + map.set(this.readValue(buffer), this.readValue(buffer)); + } + result = map; + break; + } + case SendableStandardMessageCodec.FLOAT32_ARRAY: { + let length = this.readSize(buffer); + let array = new Float32Array(length); + this.readAlignment(buffer, 4); + for (let i = 0; i < length; i++) { + array[i] = buffer.readFloat32(true) + } + result = array; + break; + } + default: + throw new Error("Message corrupted, type=" + type); + } + return result; + } +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableStandardMethodCodec.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableStandardMethodCodec.ets new file mode 100644 index 0000000000000000000000000000000000000000..c2af05f39d3f9e32e67a52c1cae23b36381a2a38 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableStandardMethodCodec.ets @@ -0,0 +1,118 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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. +*/ + +import { ByteBuffer } from '../../util/ByteBuffer'; +import FlutterException from './FlutterException'; +import Any from './Any'; +import MethodCall from './MethodCall'; +import SendableMethodCodec from './SendableMethodCodec'; +import SendableStandardMessageCodec from './SendableStandardMessageCodec'; + +/** + * A {@link SendableMethodCodec} using the Flutter standard binary encoding. + * + *

This codec is guaranteed to be compatible with the corresponding StandardMethodCodec + * on the Dart side. These parts of the Flutter SDK are evolved synchronously. + * + *

Values supported as method arguments and result payloads are those supported by {@link + * StandardMessageCodec}. + */ +@Sendable +export default class SendableStandardMethodCodec implements SendableMethodCodec { + private static TAG: string = "SendableStandardMethodCodec"; + public static INSTANCE: SendableStandardMethodCodec = new SendableStandardMethodCodec(SendableStandardMessageCodec.INSTANCE); + + private messageCodec: SendableStandardMessageCodec; + + /** Creates a new method codec based on the specified message codec. */ + constructor(messageCodec: SendableStandardMessageCodec) { + this.messageCodec = messageCodec; + } + + encodeMethodCall(methodCall: MethodCall): ArrayBuffer { + const stream = ByteBuffer.from(new ArrayBuffer(1024)); + this.messageCodec.writeValue(stream, methodCall.method); + this.messageCodec.writeValue(stream, methodCall.args); + return stream.buffer; + } + + decodeMethodCall(methodCall: ArrayBuffer): MethodCall { + const buffer = ByteBuffer.from(methodCall); + const method: Any = this.messageCodec.readValue(buffer); + const args: Any = this.messageCodec.readValue(buffer); + if (typeof method == 'string' && !buffer.hasRemaining()) { + return new MethodCall(method, args); + } + throw new Error("Method call corrupted"); + } + + encodeSuccessEnvelope(result: Any): ArrayBuffer { + const stream = ByteBuffer.from(new ArrayBuffer(1024)); + stream.writeInt8(0); + this.messageCodec.writeValue(stream, result); + return stream.buffer; + } + + encodeErrorEnvelope(errorCode: string, errorMessage: string, errorDetails: Any): ArrayBuffer { + const stream = ByteBuffer.from(new ArrayBuffer(1024)); + stream.writeInt8(1); + this.messageCodec.writeValue(stream, errorCode); + this.messageCodec.writeValue(stream, errorMessage); + if (errorDetails instanceof Error) { + this.messageCodec.writeValue(stream, errorDetails.stack); + } else { + this.messageCodec.writeValue(stream, errorDetails); + } + return stream.buffer; + } + + encodeErrorEnvelopeWithStacktrace(errorCode: string, errorMessage: string, errorDetails: Any, errorStacktrace: string): ArrayBuffer { + const stream = ByteBuffer.from(new ArrayBuffer(1024)); + stream.writeInt8(1); + this.messageCodec.writeValue(stream, errorCode); + this.messageCodec.writeValue(stream, errorMessage); + if (errorDetails instanceof Error) { + this.messageCodec.writeValue(stream, errorDetails.stack); + } else { + this.messageCodec.writeValue(stream, errorDetails); + } + this.messageCodec.writeValue(stream, errorStacktrace); + return stream.buffer; + } + + decodeEnvelope(envelope: ArrayBuffer): Any { + const buffer = ByteBuffer.from(envelope); + const flag = buffer.readInt8(); + switch (flag) { + case 0: { + const result: Any = this.messageCodec.readValue(buffer); + if (!buffer.hasRemaining()) { + return result; + } + // Falls through intentionally. + } + case 1: { + const code: Any = this.messageCodec.readValue(buffer); + const message: Any = this.messageCodec.readValue(buffer); + const details: Any = this.messageCodec.readValue(buffer); + if (typeof code == 'string' && (message == null || typeof message == 'string') && !buffer.hasRemaining()) { + throw new FlutterException(code, message, details); + } + } + } + throw new Error("Envelope corrupted"); + } +} \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableStringCodec.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableStringCodec.ets new file mode 100644 index 0000000000000000000000000000000000000000..23466ca64ae0cdeccf49a7475a8a2f96c7b1cf54 --- /dev/null +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/SendableStringCodec.ets @@ -0,0 +1,43 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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. +*/ + +import SendableMessageCodec from './SendableMessageCodec'; +import StringUtils from '../../util/StringUtils'; + +/** + * A {@link MessageCodec} using UTF-8 encoded String messages. + * + *

This codec is guaranteed to be compatible with the corresponding StringCodec on the + * Dart side. These parts of the Flutter SDK are evolved synchronously. + */ +@Sendable +export default class SendableStringCodec implements SendableMessageCodec { + static readonly INSTANCE: SendableStringCodec = new SendableStringCodec(); + + encodeMessage(message: string): ArrayBuffer { + if (message == null) { + return StringUtils.stringToArrayBuffer(""); + } + return StringUtils.stringToArrayBuffer(message); + } + + decodeMessage(message: ArrayBuffer | null): string { + if (message == null) { + return ""; + } + return StringUtils.arrayBufferToString(message); + } +} diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/StandardMessageCodec.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/StandardMessageCodec.ets index 5101b512135a4accff5d7ff08fd1acbb2a1700a8..33ad31db904f7f1a00c5c3984a12785effc31689 100644 --- a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/StandardMessageCodec.ets +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/common/StandardMessageCodec.ets @@ -19,7 +19,7 @@ import MessageCodec from './MessageCodec'; import TreeMap from '@ohos.util.TreeMap'; import HashMap from '@ohos.util.HashMap'; import LightWeightMap from '@ohos.util.LightWeightMap'; -import PlainArray from '@ohos.util.PlainArray'; +import PlainArray from '@ohos.util.PlainArray'; import List from '@ohos.util.List'; import LinkedList from '@ohos.util.LinkedList'; import Any from './Any'; @@ -100,6 +100,8 @@ export default class StandardMessageCodec implements MessageCodec { private static LIST = 12; private static MAP = 13; private static FLOAT32_ARRAY = 14; + private INT64_MAX = 9223372036854775807; + private INT64_MIN = -9223372036854775808; writeValue(stream: ByteBuffer, value: Any): Any { @@ -112,9 +114,9 @@ export default class StandardMessageCodec implements MessageCodec { if (-0x7fffffff - 1 <= value && value <= 0x7fffffff) { //int32 stream.writeInt8(StandardMessageCodec.INT32); stream.writeInt32(value, true); - } else if(Number.MIN_SAFE_INTEGER <= value && value <= Number.MAX_SAFE_INTEGER) { //int64 number整型取值范围 + } else if (Number.MIN_SAFE_INTEGER <= value && value <= Number.MAX_SAFE_INTEGER) { //int64 number整型取值范围 stream.writeInt8(StandardMessageCodec.INT64); - stream.writeBigInt64(BigInt(value), true); + stream.writeInt64(value, true); } else { //被判为整型的double型 stream.writeInt8(StandardMessageCodec.FLOAT64); this.writeAlignment(stream, 8); @@ -125,9 +127,24 @@ export default class StandardMessageCodec implements MessageCodec { this.writeAlignment(stream, 8); stream.writeFloat64(value, true); } - }else if (typeof value === "bigint") { - stream.writeInt8(StandardMessageCodec.INT64); - stream.writeBigInt64(value, true); + } else if (typeof value === "bigint") { + // https://api.flutter.dev/flutter/services/StandardMessageCodec/writeValue.html + // + // The format is first the type byte (0x05), then the actual number + // as an ASCII string giving the hexadecimal representation of the + // integer, with the string's length as encoded by writeSize + // followed by the string bytes. + if (value >= this.INT64_MIN && value <= this.INT64_MAX) { + stream.writeInt8(StandardMessageCodec.INT64); + stream.writeBigInt64(value, true); + } else { + // Convert bigint to a hexadecimal string + stream.writeInt8(StandardMessageCodec.BIGINT); + const hexString = value.toString(16); + // Map each character in the hexadecimal string to its ASCII code + const asciiString = hexString.split('').map(char => char.charCodeAt(0)); + this.writeBytes(stream, Uint8Array.from(asciiString)); + } } else if (typeof value === "string") { stream.writeInt8(StandardMessageCodec.STRING); let stringBuff = StringUtils.stringToArrayBuffer(value); @@ -264,9 +281,16 @@ export default class StandardMessageCodec implements MessageCodec { break; case StandardMessageCodec.INT64: result = buffer.readInt64(true); + if (Number.MIN_SAFE_INTEGER <= result && result <= Number.MAX_SAFE_INTEGER) { + result = Number(result); + } break; case StandardMessageCodec.BIGINT: - result = buffer.readBigInt64(true) + let bytes: Uint8Array = this.readBytes(buffer); + // Convert the byte array to a UTF-8 encoded string + const hexString: string = String.fromCharCode(...bytes); + // Parse the string as a hexadecimal BigInt + result = BigInt(`0x${hexString}`); break; case StandardMessageCodec.FLOAT64: this.readAlignment(buffer, 8); diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/editing/TextInputPlugin.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/editing/TextInputPlugin.ets index 637c9856fdc3ae61b26c3e77f9fbaa18958cd617..2cd92fd9786b13e27315dcd6f70db08c446e70ec 100644 --- a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/editing/TextInputPlugin.ets +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/editing/TextInputPlugin.ets @@ -24,6 +24,9 @@ import Log from '../../util/Log'; import { EditingStateWatcher, ListenableEditingState } from './ListenableEditingState'; import Any from '../common/Any'; +/// 临时规避缺少newline对应枚举问题 +const NEWLINE_KEY_TYPE: number = 8; + export default class TextInputPlugin implements EditingStateWatcher { private static TAG = "TextInputPlugin"; private textInputChannel: TextInputChannel; @@ -157,6 +160,20 @@ class TextInputMethodHandlerImpl implements TextInputMethodHandler { } } + async resetAttach(): Promise { + try { + let textConfig: inputMethod.TextConfig = { + inputAttribute: { + textInputType: 0, + enterKeyType: 1 + } + }; + await this.inputMethodController.attach(false, textConfig); + } catch (err) { + Log.e(TextInputMethodHandlerImpl.TAG, "Failed to attach:" + JSON.stringify(err)); + } + } + setTextInputClient(client: number, configuration: Configuration | null): void { const INPUT_TYPE_NAME = ['NONE', 'TEXT', 'MULTILINE', 'NUMBER', 'PHONE', 'DATETIME', 'EMAIL_ADDRESS', 'URL', 'VISIBLE_PASSWORD'] if (configuration) { @@ -220,7 +237,8 @@ class TextInputMethodHandlerImpl implements TextInputMethodHandler { try { this.inputMethodController.on('sendFunctionKey', (functionKey) => { - if(functionKey.enterKeyType == inputMethod.EnterKeyType.NONE) { + /// 临时规避缺少newline对应枚举类型问题 + if(functionKey.enterKeyType == NEWLINE_KEY_TYPE) { this.mEditable.handleNewlineEvent(); } this.mEditable.handleFunctionKey(functionKey); @@ -235,8 +253,7 @@ class TextInputMethodHandlerImpl implements TextInputMethodHandler { this.inputMethodController.on('sendKeyboardStatus', (state) => { if (state == inputMethod.KeyboardStatus.HIDE) { this.plugin.textInputChannel.onConnectionClosed(this.inputTarget.id); - this.inputMethodController.detach(); - this.cancelListenKeyBoardEvent(); + this.resetAttach(); } }) } catch (err) { @@ -275,6 +292,7 @@ class TextInputMethodHandlerImpl implements TextInputMethodHandler { this.mEditable.removeEditingStateListener(this.plugin); this.configuration = null; this.inputTarget = new InputTarget(Type.NO_TARGET, 0); + this.resetAttach(); } } diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/localization/LocalizationPlugin.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/localization/LocalizationPlugin.ets index e2426593cc271f951fcb47fa8e577f094ec5c0f4..93ce6e6d25e3b34055586a43cc64d24b9eac6212 100644 --- a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/localization/LocalizationPlugin.ets +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/localization/LocalizationPlugin.ets @@ -55,16 +55,10 @@ export default class LocalizationPlugin { } sendLocaleToFlutter(): void { - let currentLanguage = i18n.System.getSystemLanguage(); - let systemLanguages = i18n.System.getSystemLanguages(); - let index = systemLanguages.indexOf(currentLanguage); - - if (index != 0 && index != -1) { - let languages = systemLanguages[0]; - systemLanguages[0] = systemLanguages[index]; - systemLanguages[index] = languages; - } - this.localizationChannel.sendLocales(systemLanguages); + let systemLocale: string = i18n.System.getSystemLocale(); + let data: Array = []; + data.push(systemLocale); + this.localizationChannel.sendLocales(data); } } class enterGetStringResource{ diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/platform/PlatformViewWrapper.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/platform/PlatformViewWrapper.ets index 209971fbe679de4272627763bb6adb5c07094360..8dc1a1909eff3d884bf305738b550bb8fcaf0a4d 100644 --- a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/platform/PlatformViewWrapper.ets +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/platform/PlatformViewWrapper.ets @@ -30,9 +30,7 @@ export class PlatformViewWrapper { private bufferWidth: number = 0; private bufferHeight: number = 0; private touchProcessor: OhosTouchProcessor | null = null; - - - private model : DVModel = createDVModelFromJson( new DVModelParam("Column", [])); + private model: DVModel | undefined; public setTouchProcessor(newTouchProcessor: OhosTouchProcessor): void { this.touchProcessor = newTouchProcessor; @@ -42,7 +40,7 @@ export class PlatformViewWrapper { } public getDvModel(): DVModel { - return this.model; + return this.model!; } setParams: (params: DVModelParameters, key: string, element: Any ) => void = (params: DVModelParameters, key: string, element: Any): void => { @@ -80,7 +78,7 @@ export class PlatformViewWrapper { } public addDvModel(model: DVModel): void { - this.model?.children.push(model); + this.model = model } } diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/platform/PlatformViewsController.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/platform/PlatformViewsController.ets index 46d9ab54f3e41745fbbf9a1ae53eb084c1d3a4c2..5f56d928120ecc3e1523d41d3d0c2b94070678ec 100644 --- a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/platform/PlatformViewsController.ets +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/plugin/platform/PlatformViewsController.ets @@ -21,7 +21,8 @@ import PlatformViewsChannel, { PlatformViewsHandler, PlatformViewTouch, PlatformViewBufferSize } from '../../../ets/embedding/engine/systemchannels/PlatformViewsChannel'; import PlatformView, { Params } from './PlatformView'; -import { DVModel, DVModelContainer, DVModelParameters, DynamicView } from '../../view/DynamicView/dynamicView'; +import { DVModelParameters, } from '../../view/DynamicView/dynamicView'; +import { createDVModelFromJson } from '../../view/DynamicView/dynamicViewJson'; import display from '@ohos.display'; import { FlutterView } from '../../view/FlutterView'; import { TextureRegistry } from '../../view/TextureRegistry'; @@ -49,9 +50,24 @@ import { NodeRenderType } from '@kit.ArkUI'; import { PlatformViewInfo } from '../../embedding/ohos/PlatformViewInfo'; import { EmbeddingNodeController } from '../../embedding/ohos/EmbeddingNodeController'; +class DVModelJson { + compType: string + children: Array + attributes: ESObject + events: ESObject + build: ESObject + + constructor(compType: string, children: Array, attributes: ESObject, events: ESObject, build?: ESObject) { + this.compType = compType + this.children = children + this.attributes = attributes + this.events = events; + this.build = build; + } +} const TAG = "PlatformViewsController" -let timeStamp: number = 52086377781000 + export default class PlatformViewsController implements PlatformViewsAccessibilityDelegate, PlatformViewsHandler { private registry: PlatformViewRegistryImpl; private context: Context | null = null; @@ -71,7 +87,7 @@ export default class PlatformViewsController implements PlatformViewsAccessibili private currentFrameUsedOverlayLayerIds: HashSet; private currentFrameUsedPlatformViewIds: HashSet; private platformViewParent: Map; - private nodeControllers: Stack; + private nodeControllers: Stack; constructor() { this.registry = new PlatformViewRegistryImpl(); @@ -84,7 +100,7 @@ export default class PlatformViewsController implements PlatformViewsAccessibili this.viewIdWithTextureId = new Map(); this.viewIdWithNodeController = new Map(); this.platformViewParent = new Map(); - this.nodeControllers = new Stack(); + this.nodeControllers = new Stack(); } @@ -132,7 +148,14 @@ export default class PlatformViewsController implements PlatformViewsAccessibili Log.e(TAG, "Disposing platform view threw an exception", err); } - + let viewWrapper: PlatformViewWrapper | null = this.viewWrappers.get(viewId) || null; + if (viewWrapper != null) { + if (this.flutterView) { + let index = this.flutterView.getDVModel().children.indexOf(viewWrapper.getDvModel()!); + this.flutterView.getDVModel().children.splice(index, 1); + } + this.viewWrappers.delete(viewId); + } } setParams: (params: DVModelParameters, key: string, element: Any ) => void = (params: DVModelParameters, key: string, element: Any): void => { @@ -145,53 +168,62 @@ export default class PlatformViewsController implements PlatformViewsAccessibili let physicalHeight: number = this.toPhysicalPixels(request.newLogicalHeight); let viewId: number = request.viewId; Log.i(TAG, `Resize viewId ${viewId}, pw:${physicalWidth}, ph:${physicalHeight},lw:${request.newLogicalWidth}, lh:${request.newLogicalHeight}`); - - let nodeWidthLink: SubscribedAbstractProperty = AppStorage.link('nodeWidth'); - let nodeHeightLink: SubscribedAbstractProperty = AppStorage.link('nodeHeight'); - let oldNodeWidth: number = AppStorage.get('nodeWidth')! - let oldNodeHeight: number = AppStorage.get('nodeHeight')! - if (oldNodeWidth == physicalWidth) { - onComplete.run(new PlatformViewBufferSize(oldNodeWidth, oldNodeHeight)); - return; - } - nodeWidthLink.set(physicalWidth); - nodeHeightLink.set(physicalHeight); - this.flutterView!.getEmbeddingNodeController().rebuild(); + + let viewWrapper = this.viewWrappers.get(request.viewId) + let params: DVModelParameters | undefined = viewWrapper?.getDvModel()!.params + + this.setParams(params!, "width", physicalWidth); + this.setParams(params!, "height", physicalHeight); onComplete.run(new PlatformViewBufferSize(physicalWidth, physicalHeight)); } offset(viewId: number, top: number, left: number): void { Log.i(TAG, `Offset is id${viewId}, t:${top}, l:${left}`); + + let viewWrapper=this.viewWrappers.get(viewId) + if(viewWrapper!=undefined){ + let params: DVModelParameters | undefined = viewWrapper?.getDvModel()!.params + this.setParams(params!, "left", left); + this.setParams(params!, "top", top); + } } onTouch(touch: PlatformViewTouch): void { - console.log("nodeController onTouch:") - let pointX = 0; - let pointY = 0; - let rawPointerCoords1: Array = touch.rawPointerCoords as Array ; - rawPointerCoords1.forEach((item: ESObject) => { - let rawPoints: ArrayList = item as ArrayList; - let length = rawPoints.length - pointX = rawPoints[length - 2]; - pointY = rawPoints[length - 1]; - }) - - - timeStamp = timeStamp + 10000; - let touches1: CustomTouchObject = new CustomTouchObject(touch.action, 0, pointX, pointY, pointX, pointY, pointX, pointY, pointX, pointY); - let changedTouches1: CustomTouchObject = new CustomTouchObject(touch.action, 0, pointX, pointY, pointX, pointY, pointX, pointY, pointX, pointY); - let customTouchEvent1: CustomTouchEvent = new CustomTouchEvent(touch.action, [touches1], [changedTouches1], timeStamp, 2, 1, 0, 0, 1) - - if (this.flutterView == null) { - Log.e(TAG, "flutterView is null") + let viewWrapper: undefined | PlatformViewWrapper = this.viewWrappers.get(touch.viewId) + if (viewWrapper != undefined) { + let dvModel = viewWrapper.getDvModel() + let params = dvModel.getLayoutParams() as Record; + //接收到点击类型为down的时候 + if (touch.action == 0) { + //将当前点击状态设置为true + params['down'] = true + //首次收到触控点击类型为 OH_NATIVEXCOMPONENT_DOWN ,则将存到列表中的事件分发出去 + let touchEventArray: Array | undefined = params['touchEvent'] as Array + if (touchEventArray != undefined) { + let nodeController = params['nodeController'] as EmbeddingNodeController; + for (let it of touchEventArray) { + nodeController.postEvent(it) + } + //首次执行完之后,将列表数据置空 + params['touchEvent'] = undefined + } + //当前接收的事件类型为up的时候 + } else if (touch.action == 1) { + //手指抬起之后,将当前点击状态设置为false。测试了一下,多个手指突然抬起,最后返回的状态也是1 + //所以,这边就以状态抬起,代表当前用户不点击platformview了 + params['down'] = false + } } - this.flutterView!.getEmbeddingNodeController().postEvent(customTouchEvent1); } setDirection(viewId: number, direction: Direction): void { - this.flutterView!.getEmbeddingNodeController().setRenderOption(this.flutterView!.getPlatformView()!, this.flutterView!.getSurfaceId(), NodeRenderType.RENDER_TYPE_TEXTURE, direction); - this.flutterView!.getEmbeddingNodeController().rebuild(); + let nodeController = this.viewIdWithNodeController.get(viewId) + if (nodeController != undefined) { + nodeController?.setRenderOption(this.flutterView!.getPlatformView()!, this.flutterView!.getSurfaceId(), + NodeRenderType.RENDER_TYPE_TEXTURE, direction) + nodeController?.rebuild() + } } validateDirection(direction:number):boolean { @@ -282,19 +314,30 @@ export default class PlatformViewsController implements PlatformViewsAccessibili let wrappedBuilder: WrappedBuilder<[Params]> = platformView.getView(); this.flutterView?.setWrappedBuilder(wrappedBuilder); this.flutterView?.setPlatformView(platformView); - let nodeWidthLink: SubscribedAbstractProperty = AppStorage.link('nodeWidth'); - let nodeHeightLink: SubscribedAbstractProperty = AppStorage.link('nodeHeight'); let physicalWidth: number = this.toPhysicalPixels(request.logicalWidth); let physicalHeight: number = this.toPhysicalPixels(request.logicalHeight); - nodeWidthLink.set(physicalWidth); - nodeHeightLink.set(physicalHeight); - this.flutterView!.getEmbeddingNodeController().setRenderOption(platformView, surfaceId, NodeRenderType.RENDER_TYPE_TEXTURE, request.direction); - this.flutterView!.getEmbeddingNodeController().rebuild(); - let platformViewInfo: PlatformViewInfo = new PlatformViewInfo(platformView, surfaceId, physicalWidth, physicalHeight, request.direction); - this.nodeControllers.push(platformViewInfo); - Log.i(TAG, "Create platform view success"); + let nodeController = new EmbeddingNodeController(); + nodeController.setRenderOption(platformView, surfaceId, NodeRenderType.RENDER_TYPE_TEXTURE, request.direction); + this.viewIdWithNodeController.set(request.viewId, nodeController); + this.nodeControllers.push(nodeController); + let dvModel = createDVModelFromJson(new DVModelJson("NodeContainer", + [], + { + "width": physicalWidth, + "height": physicalHeight, + "nodeController": nodeController, + "left": request.logicalLeft, + "top": request.logicalTop + }, + {}, + undefined)); + let viewWrapper: PlatformViewWrapper = new PlatformViewWrapper(); + viewWrapper.addDvModel(dvModel); + this.viewWrappers.set(request.viewId, viewWrapper); + this.flutterView?.getDVModel().children.push(viewWrapper.getDvModel()) + Log.i(TAG, "Create platform view success"); return textureId; } @@ -363,18 +406,7 @@ export default class PlatformViewsController implements PlatformViewsAccessibili return this.registry; } - public setBackNodeControllers(): void { - this.nodeControllers.pop(); - let nodeWidthLink: SubscribedAbstractProperty = AppStorage.link('nodeWidth'); - let nodeHeightLink: SubscribedAbstractProperty = AppStorage.link('nodeHeight'); - let platformViewInfo: PlatformViewInfo = this.nodeControllers.peek(); - if (platformViewInfo != undefined && platformViewInfo.platformView != undefined) { - nodeWidthLink.set(platformViewInfo.width); - nodeHeightLink.set(platformViewInfo.height); - this.flutterView!.getEmbeddingNodeController().setRenderOption(platformViewInfo.platformView, platformViewInfo.surfaceId, NodeRenderType.RENDER_TYPE_TEXTURE, platformViewInfo.direction); - this.flutterView!.getEmbeddingNodeController().rebuild(); - } - } + public setBackNodeControllers(): void {} public onDetachedFromNapi(): void { this.diposeAllViews(); @@ -438,4 +470,35 @@ export default class PlatformViewsController implements PlatformViewsAccessibili return; } } + + public render(surfaceId: number, platformView: PlatformView, + width: number, height: number, left: number, top: number) { + this.flutterView!.setSurfaceId(surfaceId.toString()); + let wrappedBuilder: WrappedBuilder<[Params]> = platformView.getView(); + this.flutterView?.setWrappedBuilder(wrappedBuilder); + this.flutterView?.setPlatformView(platformView); + + let nodeController = new EmbeddingNodeController(); + + nodeController.setRenderOption(platformView, surfaceId.toString(), NodeRenderType.RENDER_TYPE_TEXTURE, Direction.Auto); + + this.nodeControllers.push(nodeController); + + let dvModel = createDVModelFromJson(new DVModelJson("NodeContainer", + [], + { + "width": width, + "height": height, + "nodeController": nodeController, + "left": left, + "top": top + }, + {}, + undefined)); + + let viewWrapper: PlatformViewWrapper = new PlatformViewWrapper(); + viewWrapper.addDvModel(dvModel); + this.viewWrappers.set(surfaceId, viewWrapper); + this.flutterView?.getDVModel().children.push(viewWrapper.getDvModel()); + } } \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/view/DynamicView/dynamicView.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/view/DynamicView/dynamicView.ets index 4dd2cf3edd8e7f00d2f7e1f04442cdfe2795568d..63af526e5a30562a07806bc88117dda1d75a8fc2 100644 --- a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/view/DynamicView/dynamicView.ets +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/view/DynamicView/dynamicView.ets @@ -239,7 +239,13 @@ export struct DynamicView { .common_attrs() } - @Builder buildCustom() { + @Builder buildNodeContainer() { + NodeContainer(this.getParams(this.params, "nodeController")) + .common_attrs() + } + + @Builder + buildCustom() { if (this.customBuilder) { this.customBuilder(new BuilderParams(this.params)); } @@ -258,6 +264,8 @@ export struct DynamicView { this.buildImage() } else if (this.model.compType == "Button") { this.buildButton() + } else if (this.model.compType == "NodeContainer") { + this.buildNodeContainer() } else { this.buildCustom() } diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/view/FlutterView.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/view/FlutterView.ets index 947208011c79ccdaa549ba88d5b3d6da4e2c11dc..3f23984aeed09048c5851df89d4745b72d2cd97e 100644 --- a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/view/FlutterView.ets +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/view/FlutterView.ets @@ -48,10 +48,25 @@ class ViewportMetrics { } export class PlatformViewParas { - width: number = 0; - height: number = 0; + width: number = 0.0; + height: number = 0.0; + top: number = 0.0; + left: number = 0.0; direction: Direction = Direction.Auto; + + setValue(width: number, height: number, top: number, left: number): void { + this.width = width; + this.height = height; + this.top = top; + this.left = left; + } + + setOffset(top: number, left: number): void { + this.top = top; + this.left = left; + } } + export class FlutterView { private flutterEngine: FlutterEngine | null = null private id: string = "" @@ -321,6 +336,10 @@ export class FlutterView { this.keyboardManager?.handleKeyEvent(event) } + onMouseWheel(eventType: string, event: PanGestureEvent) { + this.flutterEngine?.getFlutterNapi()?.xComponentDisPatchMouseWheel(this.id, eventType, event); + } + addFirstFrameListener(listener: FirstFrameListener) { this.mFirstFrameListeners.add(listener); } diff --git a/shell/platform/ohos/library_loader.cpp b/shell/platform/ohos/library_loader.cpp index 415bf46a65e371659b21f32fab0a1553b11a090e..231723d337abab42a477ee2cedee1e0113ba67f8 100644 --- a/shell/platform/ohos/library_loader.cpp +++ b/shell/platform/ohos/library_loader.cpp @@ -107,6 +107,9 @@ static napi_value Init(napi_env env, napi_value exports) { DECLARE_NAPI_FUNCTION( "nativeXComponentDetachFlutterEngine", flutter::PlatformViewOHOSNapi::nativeXComponentDetachFlutterEngine), + DECLARE_NAPI_FUNCTION( + "nativeXComponentDispatchMouseWheel", + flutter::PlatformViewOHOSNapi::nativeXComponentDispatchMouseWheel), DECLARE_NAPI_FUNCTION( "nativeInitNativeImage", flutter::PlatformViewOHOSNapi::nativeInitNativeImage), diff --git a/shell/platform/ohos/napi/platform_view_ohos_napi.cpp b/shell/platform/ohos/napi/platform_view_ohos_napi.cpp index c5461f205c29310f221d219da3cf6127eddaa60b..52fa7d7b52cbdc6b63ac15b12535ee833e078a1b 100644 --- a/shell/platform/ohos/napi/platform_view_ohos_napi.cpp +++ b/shell/platform/ohos/napi/platform_view_ohos_napi.cpp @@ -434,6 +434,26 @@ void PlatformViewOHOSNapi::DecodeImage(int64_t imageGeneratorAddress, })); } +void PlatformViewOHOSNapi::FlutterViewOnTouchEvent(std::shared_ptr touchPacketString, int size) { + if (touchPacketString == nullptr) { + FML_LOG(ERROR) << "Input parameter error"; + return; + } + napi_value arrayString; + napi_create_array(env_, &arrayString); + + for (int i = 0; i < size; ++i) { + napi_value stringItem; + napi_create_string_utf8(env_, touchPacketString[i].c_str(), -1, &stringItem); + napi_set_element(env_, arrayString, i, stringItem); + } + + napi_status status = fml::napi::InvokeJsMethod(env_, ref_napi_obj_, "onTouchEvent", 1, &arrayString); + if (status != napi_ok) { + FML_LOG(ERROR) << "InvokeJsMethod onTouchEvent fail"; + } +} + /** * attach flutterNapi实例给到 native * engine,这个支持rkts到flutter平台的无关引擎之间的通信 attach只需要执行一次 @@ -1606,4 +1626,87 @@ napi_value PlatformViewOHOSNapi::nativeXComponentDetachFlutterEngine( return nullptr; } +/** + * @brief flutterEngine get mouseWheel event from ets + * @note + * @param nativeShellHolderId: number + * @param xcomponentId: number + * @param eventType: string + * @param fingerId: number + * @param globalX: number + * @param globalY: number + * @param offsetY: number + * @param timestamp: number + * @return napi_value + */ +napi_value PlatformViewOHOSNapi::nativeXComponentDispatchMouseWheel(napi_env env, napi_callback_info info) +{ + napi_status ret; + size_t argc = 8; + napi_value args[8] = {nullptr}; + int64_t shellHolder; + std::string xcomponentId; + std::string eventType; + int64_t fingerId; + double globalX; + double globalY; + double offsetY; + int64_t timestamp; + ret = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + if (ret != napi_ok) { + FML_DLOG(ERROR) << "nativeXComponentDispatchMouseWheel napi_get_cb_info error:" + << ret; + return nullptr; + } + ret = napi_get_value_int64(env, args[0], &shellHolder); + if (ret != napi_ok) { + LOGE("nativeXComponentDispatchMouseWheel shellHolder napi_get_value_int64 error"); + return nullptr; + } + if (fml::napi::GetString(env, args[1], xcomponentId) != 0) { + FML_DLOG(ERROR) << "nativeXComponentDispatchMouseWheel xcomponentId GetString error"; + return nullptr; + } + if (fml::napi::GetString(env, args[2], eventType) != 0) { + FML_DLOG(ERROR) << "nativeXComponentDispatchMouseWheel eventType GetString error"; + return nullptr; + } + ret = napi_get_value_int64(env, args[3], &fingerId); + if (ret != napi_ok) { + LOGE("nativeXComponentDispatchMouseWheel fingerId napi_get_value_int64 error"); + return nullptr; + } + ret = napi_get_value_double(env, args[4], &globalX); + if (ret != napi_ok) { + LOGE("nativeXComponentDispatchMouseWheel globalX napi_get_value_double error"); + return nullptr; + } + ret = napi_get_value_double(env, args[5], &globalY); + if (ret != napi_ok) { + LOGE("nativeXComponentDispatchMouseWheel globalY napi_get_value_double error"); + return nullptr; + } + ret = napi_get_value_double(env, args[6], &offsetY); + if (ret != napi_ok) { + LOGE("nativeXComponentDispatchMouseWheel offsetY napi_get_value_double error"); + return nullptr; + } + ret = napi_get_value_int64(env, args[7], ×tamp); + if (ret != napi_ok) { + LOGE("nativeXComponentDispatchMouseWheel timestamp napi_get_value_int64 error"); + return nullptr; + } + flutter::mouseWheelEvent event { + eventType, + shellHolder, + fingerId, + globalX, + globalY, + offsetY, + timestamp + }; + XComponentAdapter::GetInstance()->OnMouseWheel(xcomponentId, event); + return nullptr; +} + } // namespace flutter \ No newline at end of file diff --git a/shell/platform/ohos/napi/platform_view_ohos_napi.h b/shell/platform/ohos/napi/platform_view_ohos_napi.h index f998fbf553c2809abb2c5da737685c68009a8ce0..b91367ae88f7442984bbe5db6e3fedfbb91401e6 100644 --- a/shell/platform/ohos/napi/platform_view_ohos_napi.h +++ b/shell/platform/ohos/napi/platform_view_ohos_napi.h @@ -37,6 +37,16 @@ struct locale { std::string region; }; +struct mouseWheelEvent { + std::string eventType; + int64_t shellHolder; + int64_t fingerId; + double globalX; + double globalY; + double offsetY; + int64_t timestamp; +}; + class PlatformViewOHOSNapi { public: static napi_value nativeDispatchEmptyPlatformMessage( @@ -72,6 +82,8 @@ class PlatformViewOHOSNapi { void* inputData, size_t dataSize); + void FlutterViewOnTouchEvent(std::shared_ptr touchPacketString, int size); + static napi_value nativeUpdateRefreshRate( napi_env env, napi_callback_info info); // 设置刷新率 @@ -179,6 +191,9 @@ class PlatformViewOHOSNapi { static napi_value nativeXComponentDetachFlutterEngine( napi_env env, napi_callback_info info); + static napi_value nativeXComponentDispatchMouseWheel( + napi_env env, + napi_callback_info info); private: static napi_env env_; diff --git a/shell/platform/ohos/ohos_external_texture_gl.cpp b/shell/platform/ohos/ohos_external_texture_gl.cpp index ad1c4e2d7019e30341d9807c126de637a39df61a..254323f68c3b2f56a736be3628a2b5af49871d21 100755 --- a/shell/platform/ohos/ohos_external_texture_gl.cpp +++ b/shell/platform/ohos/ohos_external_texture_gl.cpp @@ -373,6 +373,11 @@ void OHOSExternalTextureGL::ProducePixelMapToNativeImage() if (ret != 0) { FML_DLOG(ERROR) << "OHOSExternalTextureGL OH_NativeWindow_NativeWindowHandleOpt err:" << ret; } + + int32_t usage = 0; + OH_NativeWindow_NativeWindowHandleOpt(nativeWindow_, GET_USAGE, &usage); + usage |= NATIVEBUFFER_USAGE_CPU_READ; + OH_NativeWindow_NativeWindowHandleOpt(nativeWindow_, SET_USAGE, usage); if (buffer_ != nullptr) { OH_NativeWindow_NativeWindowAbortBuffer(nativeWindow_, buffer_); diff --git a/shell/platform/ohos/ohos_image_generator.cpp b/shell/platform/ohos/ohos_image_generator.cpp index c8bf63579e474d90b571ee868161a15b7e6e0b87..8962dd80b80f1540a926398a478ffb8f2bd26ff8 100755 --- a/shell/platform/ohos/ohos_image_generator.cpp +++ b/shell/platform/ohos/ohos_image_generator.cpp @@ -25,8 +25,11 @@ namespace flutter { OHOSImageGenerator::OHOSImageGenerator( sk_sp buffer, + const fml::RefPtr& task_runner, std::shared_ptr napi_facade) - : data_(std::move(buffer)), image_info_(SkImageInfo::MakeUnknown(-1, -1)) { + : data_(std::move(buffer)), + task_runner_(std::move(task_runner)), + image_info_(SkImageInfo::MakeUnknown(-1, -1)) { napi_facade_ = napi_facade; } @@ -106,6 +109,10 @@ bool OHOSImageGenerator::GetPixels(const SkImageInfo& info, return true; } +fml::RefPtr OHOSImageGenerator::GetTaskRunner() const { + return task_runner_; +} + void OHOSImageGenerator::DecodeImage() { DoDecodeImage(); @@ -115,7 +122,7 @@ void OHOSImageGenerator::DecodeImage() { std::shared_ptr OHOSImageGenerator::MakeFromData( sk_sp data, - const fml::RefPtr& task_runner, + const TaskRunners& task_runners, std::shared_ptr napi_facade) { // Return directly if the image data is empty. // https://gitee.com/openharmony-sig/flutter_engine/issues/I9NX5N @@ -124,11 +131,11 @@ std::shared_ptr OHOSImageGenerator::MakeFromData( } // contructer is private, - std::shared_ptr generator( - new OHOSImageGenerator(std::move(data), napi_facade)); + std::shared_ptr generator(new OHOSImageGenerator( + std::move(data), task_runners.GetPlatformTaskRunner(), napi_facade)); fml::TaskRunner::RunNowOrPostTask( - task_runner, [generator]() { generator->DecodeImage(); }); + task_runners.GetIOTaskRunner(), [generator]() { generator->DecodeImage(); }); if (generator->IsValidImageData()) { return generator; @@ -150,6 +157,24 @@ bool OHOSImageGenerator::IsValidImageData() { return GetInfo().height() != -1; } +struct ReleaseCtx { + napi_env env; + napi_ref ref = nullptr; + char* buf; + fml::RefPtr task_runner; +}; + +void on_release(const void* ptr, void* context) { + auto release_ctx = static_cast(context); + fml::TaskRunner::RunNowOrPostTask(release_ctx->task_runner, [release_ctx]() { + napi_value res = nullptr; + napi_get_reference_value(release_ctx->env, release_ctx->ref, &res); + OHOS::Media::OH_UnAccessPixels(release_ctx->env, res); + napi_delete_reference(release_ctx->env, release_ctx->ref); + delete release_ctx; // 删除 ReleaseCtx 对象以避免内存泄漏 + }); +} + napi_value OHOSImageGenerator::NativeImageDecodeCallback( napi_env env, napi_callback_info info) { @@ -206,25 +231,11 @@ napi_value OHOSImageGenerator::NativeImageDecodeCallback( } // pixel_lock, g_env_ =g_env , resultout - struct ReleaseCtx { - napi_env env_; - napi_ref ref = nullptr; - char* buf; - }; - ReleaseCtx* ctx = new ReleaseCtx(); - ctx->env_ = env; - ctx->buf = (char*)pixel_lock; + ctx->env = env; + ctx->buf = static_cast(pixel_lock); napi_create_reference(env, args[3], 1, &(ctx->ref)); - - SkData::ReleaseProc on_release = [](const void* ptr, void* context) -> void { - ReleaseCtx* ctx = reinterpret_cast(context); - napi_value res = nullptr; - napi_get_reference_value(ctx->env_, ctx->ref, &res); - OHOS::Media::OH_UnAccessPixels(ctx->env_, res); - napi_delete_reference(ctx->env_, ctx->ref); - return; - }; + ctx->task_runner = generator->GetTaskRunner(); // get software_decode_data by call back, bitmap buffer will unlock in // callback diff --git a/shell/platform/ohos/ohos_image_generator.h b/shell/platform/ohos/ohos_image_generator.h index d2762a74e80319ba419ff1807bf2e3952a67dd06..74b38a70806437a81b6a34d0f435f27aed76b97b 100755 --- a/shell/platform/ohos/ohos_image_generator.h +++ b/shell/platform/ohos/ohos_image_generator.h @@ -16,6 +16,7 @@ #ifndef FLUTTER_IMAGE_GENERATOR_H #define FLUTTER_IMAGE_GENERATOR_H +#include "flutter/common/task_runners.h" #include "flutter/fml/memory/ref_ptr.h" #include "flutter/fml/synchronization/waitable_event.h" #include "flutter/fml/task_runner.h" @@ -31,6 +32,7 @@ class OHOSImageGenerator : public ImageGenerator { private: explicit OHOSImageGenerator( sk_sp buffer, + const fml::RefPtr& task_runner, std::shared_ptr napi_facade); static napi_env g_env; @@ -70,13 +72,15 @@ class OHOSImageGenerator : public ImageGenerator { static std::shared_ptr MakeFromData( sk_sp data, - const fml::RefPtr& task_runner, + const TaskRunners& task_runners, std::shared_ptr napi_facade); + fml::RefPtr GetTaskRunner() const; + private: sk_sp data_; sk_sp software_decoded_data_; - + const fml::RefPtr task_runner_; SkImageInfo image_info_; std::shared_ptr napi_facade_; diff --git a/shell/platform/ohos/ohos_main.cpp b/shell/platform/ohos/ohos_main.cpp index 8da929b5665572ae794fa603d351482b30fc8d97..246dc4a778a9529e90a3d84cebfaf5f7ccb6c173 100644 --- a/shell/platform/ohos/ohos_main.cpp +++ b/shell/platform/ohos/ohos_main.cpp @@ -27,6 +27,7 @@ #include "flutter/fml/size.h" #include "flutter/lib/ui/plugins/callback_cache.h" #include "flutter/runtime/dart_vm.h" +#include "flutter/runtime/ptrace_check.h" #include "flutter/shell/common/shell.h" #include "flutter/shell/common/switches.h" #include "third_party/dart/runtime/include/dart_tools_api.h" @@ -91,9 +92,9 @@ const flutter::Settings& OhosMain::GetSettings() const { * @note * @param context: common.Context, args: Array, bundlePath: string, * appStoragePath: string, engineCachesPath: string, initTimeMillis: number - * @return void + * @return napi_value */ -void OhosMain::Init(napi_env env, napi_callback_info info) { +napi_value OhosMain::Init(napi_env env, napi_callback_info info) { size_t argc = 7; napi_value param[7]; std::string kernelPath, appStoragePath, engineCachesPath; @@ -147,14 +148,27 @@ void OhosMain::Init(napi_env env, napi_callback_info info) { LOGI("%{public}s %{public}s", tag.c_str(), message.c_str()); }; + if (!EnableTracingIfNecessary(settings)) { + LOGE( + "Cannot create a FlutterEngine instance in debug mode without Flutter tooling.\n\n" + "To Launch in debug mode, run 'flutter run' from Flutter tools, run from an IDE with a" + "Flutter IDE plugin.\nAlternatively profile and release mode apps canbe launched from " + "the home screen."); + return nullptr; + } + g_flutter_main.reset(new OhosMain(settings)); // TODO : g_flutter_main->SetupObservatoryUriCallback(env); + LOGI("OhosMain::Init finished."); + napi_value result; + napi_create_int64(env, 0, &result); + return result; } napi_value OhosMain::NativeInit(napi_env env, napi_callback_info info) { - OhosMain::Init(env, info); + napi_value result = OhosMain::Init(env, info); OHOSImageGenerator::ImageNativeInit(env, info); - return nullptr; + return result; } bool OhosMain::IsEmulator() diff --git a/shell/platform/ohos/ohos_main.h b/shell/platform/ohos/ohos_main.h index 25120f48e7509e865c7f0f95d6d679843a75ab73..3e8f7355977a4a51e4894bd076741a9dd8fa877b 100644 --- a/shell/platform/ohos/ohos_main.h +++ b/shell/platform/ohos/ohos_main.h @@ -38,7 +38,7 @@ class OhosMain { explicit OhosMain(const flutter::Settings& settings); - static void Init(napi_env env, napi_callback_info info); + static napi_value Init(napi_env env, napi_callback_info info); void SetupObservatoryUriCallback(napi_env env, napi_callback_info info); FML_DISALLOW_COPY_AND_ASSIGN(OhosMain); diff --git a/shell/platform/ohos/ohos_shell_holder.cpp b/shell/platform/ohos/ohos_shell_holder.cpp index c995a841934c750f627ae5709d1b0b827c18ce2e..b70310845d4b1f6ec213ddba0f35cb7b694721c5 100644 --- a/shell/platform/ohos/ohos_shell_holder.cpp +++ b/shell/platform/ohos/ohos_shell_holder.cpp @@ -163,9 +163,9 @@ OHOSShellHolder::OHOSShellHolder( LOGI("shell_ end"); shell_->RegisterImageDecoder( - [runner = task_runners.GetIOTaskRunner(), + [task_runners, napi_facade = napi_facade_](sk_sp buffer) { - return OHOSImageGenerator::MakeFromData(std::move(buffer), runner, + return OHOSImageGenerator::MakeFromData(std::move(buffer), task_runners, napi_facade); }, -1); diff --git a/shell/platform/ohos/ohos_touch_processor.cpp b/shell/platform/ohos/ohos_touch_processor.cpp index 5abd00deca15cfea662ab31aee580eb43578ff9b..4849075ba095c396720b2a04fbf3d9a7e644978d 100644 --- a/shell/platform/ohos/ohos_touch_processor.cpp +++ b/shell/platform/ohos/ohos_touch_processor.cpp @@ -20,6 +20,9 @@ namespace flutter { constexpr int MSEC_PER_SECOND = 1000; +constexpr int PER_POINTER_MEMBER = 10; +constexpr int CHANGES_POINTER_MEMBER = 10; +constexpr int TOUCH_EVENT_ADDITIONAL_ATTRIBUTES = 4; PointerData::Change OhosTouchProcessor::getPointerChangeForAction( int maskedAction) { @@ -36,6 +39,40 @@ PointerData::Change OhosTouchProcessor::getPointerChangeForAction( return PointerData::Change::kCancel; } +PointerData::Change OhosTouchProcessor::getPointerChangeForMouseAction( + OH_NativeXComponent_MouseEventAction mouseAction) +{ + switch (mouseAction) { + case OH_NATIVEXCOMPONENT_MOUSE_PRESS: + return PointerData::Change::kDown; + case OH_NATIVEXCOMPONENT_MOUSE_RELEASE: + return PointerData::Change::kUp; + case OH_NATIVEXCOMPONENT_MOUSE_MOVE: + return PointerData::Change::kMove; + default: + return PointerData::Change::kCancel; + } +} + +PointerButtonMouse OhosTouchProcessor::getPointerButtonFromMouse( + OH_NativeXComponent_MouseEventButton mouseButton) +{ + switch (mouseButton) { + case OH_NATIVEXCOMPONENT_LEFT_BUTTON: + return kPointerButtonMousePrimary; + case OH_NATIVEXCOMPONENT_RIGHT_BUTTON: + return kPointerButtonMouseSecondary; + case OH_NATIVEXCOMPONENT_MIDDLE_BUTTON: + return kPointerButtonMouseMiddle; + case OH_NATIVEXCOMPONENT_BACK_BUTTON: + return kPointerButtonMouseBack; + case OH_NATIVEXCOMPONENT_FORWARD_BUTTON: + return kPointerButtonMouseForward; + default: + return kPointerButtonMousePrimary; + } +} + PointerData::DeviceKind OhosTouchProcessor::getPointerDeviceTypeForToolType( int toolType) { switch (toolType) { @@ -61,6 +98,47 @@ PointerData::DeviceKind OhosTouchProcessor::getPointerDeviceTypeForToolType( return PointerData::DeviceKind::kTouch; } +std::shared_ptr OhosTouchProcessor::packagePacketData( + std::unique_ptr touchPacket) { + if (touchPacket == nullptr) { + return nullptr; + } + int numPoints = touchPacket->touchEventInput->numPoints; + int offset = 0; + int size = CHANGES_POINTER_MEMBER + PER_POINTER_MEMBER * numPoints + TOUCH_EVENT_ADDITIONAL_ATTRIBUTES; + std::shared_ptr package(new std::string[size]); + + package[offset++] = std::to_string(touchPacket->touchEventInput->numPoints); + + package[offset++] = std::to_string(touchPacket->touchEventInput->id); + package[offset++] = std::to_string(touchPacket->touchEventInput->screenX); + package[offset++] = std::to_string(touchPacket->touchEventInput->screenY); + package[offset++] = std::to_string(touchPacket->touchEventInput->x); + package[offset++] = std::to_string(touchPacket->touchEventInput->y); + package[offset++] = std::to_string(touchPacket->touchEventInput->type); + package[offset++] = std::to_string(touchPacket->touchEventInput->size); + package[offset++] = std::to_string(touchPacket->touchEventInput->force); + package[offset++] = std::to_string(touchPacket->touchEventInput->deviceId); + package[offset++] = std::to_string(touchPacket->touchEventInput->timeStamp); + + for (int i = 0; i < numPoints; i++) { + package[offset++] = std::to_string(touchPacket->touchEventInput->touchPoints[i].id); + package[offset++] = std::to_string(touchPacket->touchEventInput->touchPoints[i].screenX); + package[offset++] = std::to_string(touchPacket->touchEventInput->touchPoints[i].screenY); + package[offset++] = std::to_string(touchPacket->touchEventInput->touchPoints[i].x); + package[offset++] = std::to_string(touchPacket->touchEventInput->touchPoints[i].y); + package[offset++] = std::to_string(touchPacket->touchEventInput->touchPoints[i].type); + package[offset++] = std::to_string(touchPacket->touchEventInput->touchPoints[i].size); + package[offset++] = std::to_string(touchPacket->touchEventInput->touchPoints[i].force); + package[offset++] = std::to_string(touchPacket->touchEventInput->touchPoints[i].timeStamp); + package[offset++] = std::to_string(touchPacket->touchEventInput->touchPoints[i].isPressed); + } + package[offset++] = std::to_string(touchPacket->toolTypeInput); + package[offset++] = std::to_string(touchPacket->tiltX); + package[offset++] = std::to_string(touchPacket->tiltY); + return package; +} + void OhosTouchProcessor::HandleTouchEvent( int64_t shell_holderID, OH_NativeXComponent* component, @@ -68,8 +146,8 @@ void OhosTouchProcessor::HandleTouchEvent( if (touchEvent == nullptr) { return; } - const int numPoints = 1; - auto packet = std::make_unique(numPoints); + const int numTouchPoints = 1; + std::unique_ptr packet = std::make_unique(numTouchPoints); PointerData pointerData; pointerData.Clear(); pointerData.embedder_id = touchEvent->id; @@ -113,6 +191,73 @@ void OhosTouchProcessor::HandleTouchEvent( auto ohos_shell_holder = reinterpret_cast(shell_holderID); ohos_shell_holder->GetPlatformView()->DispatchPointerDataPacket( std::move(packet)); + + int numPoints = touchEvent->numPoints; + float tiltX = 0.0; + float tiltY = 0.0; + OH_NativeXComponent_GetTouchPointTiltX(component, 0, &tiltX); + OH_NativeXComponent_GetTouchPointTiltY(component, 0, &tiltY); + std::unique_ptr touchPacket = + std::make_unique(); + touchPacket->touchEventInput = touchEvent; + touchPacket->toolTypeInput = toolType; + touchPacket->tiltX = tiltX; + touchPacket->tiltX = tiltY; + + std::shared_ptr touchPacketString = packagePacketData(std::move(touchPacket)); + int size = CHANGES_POINTER_MEMBER + PER_POINTER_MEMBER * numPoints + TOUCH_EVENT_ADDITIONAL_ATTRIBUTES; + ohos_shell_holder->GetPlatformView()->OnTouchEvent(touchPacketString, size); + return; } +void OhosTouchProcessor::HandleMouseEvent( + int64_t shell_holderID, + OH_NativeXComponent* component, + OH_NativeXComponent_MouseEvent mouseEvent, + double offsetY) +{ + const int numTouchPoints = 1; + std::unique_ptr packet = std::make_unique(numTouchPoints); + PointerData pointerData; + pointerData.Clear(); + pointerData.embedder_id = mouseEvent.button; + pointerData.time_stamp = mouseEvent.timestamp / MSEC_PER_SECOND; + pointerData.change = getPointerChangeForMouseAction(mouseEvent.action); + pointerData.physical_y = mouseEvent.y; + pointerData.physical_x = mouseEvent.x; + // Delta will be generated in pointer_data_packet_converter.cc. + pointerData.physical_delta_x = 0.0; + pointerData.physical_delta_y = 0.0; + pointerData.device = mouseEvent.button; + // Pointer identifier will be generated in pointer_data_packet_converter.cc. + pointerData.pointer_identifier = 0; + // XComponent not support Scroll + // now it's support + pointerData.signal_kind = offsetY != 0 ? PointerData::SignalKind::kScroll : PointerData::SignalKind::kNone; + pointerData.scroll_delta_x = 0.0; + pointerData.scroll_delta_y = offsetY; + pointerData.pressure = 0.0; + pointerData.pressure_max = 1.0; + pointerData.pressure_min = 0.0; + pointerData.kind = PointerData::DeviceKind::kMouse; + pointerData.buttons = getPointerButtonFromMouse(mouseEvent.button); + // hover support + if (mouseEvent.button == OH_NATIVEXCOMPONENT_NONE_BUTTON && pointerData.change == PointerData::Change::kMove) { + pointerData.change = PointerData::Change::kHover; + pointerData.buttons = 0; + } + pointerData.pan_x = 0.0; + pointerData.pan_y = 0.0; + // Delta will be generated in pointer_data_packet_converter.cc. + pointerData.pan_delta_x = 0.0; + pointerData.pan_delta_y = 0.0; + // The contact area between the fingerpad and the screen + pointerData.size = 0.0; + pointerData.scale = 1.0; + pointerData.rotation = 0.0; + packet->SetPointerData(0, pointerData); + auto ohos_shell_holder = reinterpret_cast(shell_holderID); + ohos_shell_holder->GetPlatformView()->DispatchPointerDataPacket(std::move(packet)); + return; +} } // namespace flutter \ No newline at end of file diff --git a/shell/platform/ohos/ohos_touch_processor.h b/shell/platform/ohos/ohos_touch_processor.h index 68873848fd417a47be9cf048feb04a5633174a5a..94012bf1a8a2f37aef726e958fc739aca6218651 100644 --- a/shell/platform/ohos/ohos_touch_processor.h +++ b/shell/platform/ohos/ohos_touch_processor.h @@ -17,19 +17,39 @@ #define OHOS_TOUCH_PROCESSOR_H #include #include +#include #include "flutter/lib/ui/window/pointer_data.h" #include "napi_common.h" namespace flutter { class OhosTouchProcessor { + public: + typedef struct { + OH_NativeXComponent_TouchEvent* touchEventInput; + OH_NativeXComponent_TouchPointToolType toolTypeInput; + float tiltX; + float tiltY; + } TouchPacket; + public: void HandleTouchEvent(int64_t shell_holderID, OH_NativeXComponent* component, OH_NativeXComponent_TouchEvent* touchEvent); + void HandleMouseEvent(int64_t shell_holderID, + OH_NativeXComponent* component, + OH_NativeXComponent_MouseEvent mouseEvent, + double offsetY); flutter::PointerData::Change getPointerChangeForAction(int maskedAction); flutter::PointerData::DeviceKind getPointerDeviceTypeForToolType( int toolType); + flutter::PointerData::Change getPointerChangeForMouseAction( + OH_NativeXComponent_MouseEventAction mouseAction); + PointerButtonMouse getPointerButtonFromMouse( + OH_NativeXComponent_MouseEventButton mouseButton); + + private: + std::shared_ptr packagePacketData(std::unique_ptr touchPacket); public: OH_NativeXComponent_TouchPointToolType touchType_; diff --git a/shell/platform/ohos/ohos_xcomponent_adapter.cpp b/shell/platform/ohos/ohos_xcomponent_adapter.cpp index 9c7fee2473fb2469228138661c94e9f22b88885b..4fa9afc7ab967f70c16377ca8170c5942d6c222c 100644 --- a/shell/platform/ohos/ohos_xcomponent_adapter.cpp +++ b/shell/platform/ohos/ohos_xcomponent_adapter.cpp @@ -19,6 +19,10 @@ #include namespace flutter { +bool g_isMouseLeftActive = false; +double g_scrollDistance = 0.0; +double g_resizeRate = 0.8; + XComponentAdapter XComponentAdapter::mXComponentAdapter; XComponentAdapter::XComponentAdapter(/* args */) {} @@ -102,6 +106,14 @@ void XComponentAdapter::DetachFlutterEngine(std::string& id) { } } +void XComponentAdapter::OnMouseWheel(std::string& id, mouseWheelEvent event) +{ + auto iter = xcomponetMap_.find(id); + if (iter != xcomponetMap_.end()) { + iter->second->OnDispatchMouseWheelEvent(event); + } +} + #include using OHOS_SurfaceBufferUsage = enum { BUFFER_USAGE_CPU_READ = (1ULL << 0), /**< CPU read buffer */ @@ -229,11 +241,27 @@ void DispatchTouchEventCB(OH_NativeXComponent* component, void* window) { } } +void DispatchMouseEventCB(OH_NativeXComponent* component, void* window) +{ + for (auto it: XComponentAdapter::GetInstance()->xcomponetMap_) { + if (it.second->nativeXComponent_ == component) { + it.second->OnDispatchMouseEvent(component, window); + } + } +} + +void DispatchHoverEventCB(OH_NativeXComponent* component, bool isHover) +{ + LOGD("XComponentManger::DispatchHoverEventCB"); +} + void XComponentBase::BindXComponentCallback() { callback_.OnSurfaceCreated = OnSurfaceCreatedCB; callback_.OnSurfaceChanged = OnSurfaceChangedCB; callback_.OnSurfaceDestroyed = OnSurfaceDestroyedCB; callback_.DispatchTouchEvent = DispatchTouchEventCB; + mouseCallback_.DispatchMouseEvent = DispatchMouseEventCB; + mouseCallback_.DispatchHoverEvent = DispatchHoverEventCB; } XComponentBase::XComponentBase(std::string id){ @@ -276,6 +304,7 @@ void XComponentBase::SetNativeXComponent(OH_NativeXComponent* nativeXComponent){ if (nativeXComponent_ != nullptr) { BindXComponentCallback(); OH_NativeXComponent_RegisterCallback(nativeXComponent_, &callback_); + OH_NativeXComponent_RegisterMouseEventCallback(nativeXComponent_, &mouseCallback_); } } @@ -338,12 +367,17 @@ void XComponentBase::OnSurfaceDestroyed(OH_NativeXComponent* component, void XComponentBase::OnDispatchTouchEvent(OH_NativeXComponent* component, void* window) { - LOGD("XComponentManger::DispatchTouchEvent"); int32_t ret = OH_NativeXComponent_GetTouchEvent(component, window, &touchEvent_); if (ret == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { if (isEngineAttached_) { - LOGD("XComponentManger::HandleTouchEvent"); + // if this touchEvent triggered by mouse, return + OH_NativeXComponent_EventSourceType sourceType; + int32_t ret2 = OH_NativeXComponent_GetTouchEventSourceType(component, touchEvent_.id, &sourceType); + if (ret2 == OH_NATIVEXCOMPONENT_RESULT_SUCCESS && + sourceType == OH_NATIVEXCOMPONENT_SOURCE_TYPE_MOUSE) { + return; + } ohosTouchProcessor_.HandleTouchEvent(std::stoll(shellholderId_), component, &touchEvent_); } else { @@ -354,4 +388,51 @@ void XComponentBase::OnDispatchTouchEvent(OH_NativeXComponent* component, } } +void XComponentBase::OnDispatchMouseEvent(OH_NativeXComponent* component, void* window) +{ + OH_NativeXComponent_MouseEvent mouseEvent; + int32_t ret = OH_NativeXComponent_GetMouseEvent(component, window, &mouseEvent); + if (ret == OH_NATIVEXCOMPONENT_RESULT_SUCCESS && isEngineAttached_) { + if (mouseEvent.button == OH_NATIVEXCOMPONENT_LEFT_BUTTON) { + if (mouseEvent.action == OH_NATIVEXCOMPONENT_MOUSE_PRESS) { + g_isMouseLeftActive = true; + } else if (mouseEvent.action == OH_NATIVEXCOMPONENT_MOUSE_RELEASE) { + g_isMouseLeftActive = false; + } + } + ohosTouchProcessor_.HandleMouseEvent(std::stoll(shellholderId_), component, mouseEvent, 0.0); + return; + } + LOGE("XComponentManger::DispatchMouseEvent XComponentBase is not attached"); +} + +void XComponentBase::OnDispatchMouseWheelEvent(mouseWheelEvent event) +{ + std::string shell_holder_str = std::to_string(event.shellHolder); + if (shell_holder_str != shellholderId_) { + return; + } + if (isEngineAttached_) { + if (g_isMouseLeftActive) { + return; + } + if (event.eventType == "actionUpdate") { + OH_NativeXComponent_MouseEvent mouseEvent; + double scrollY = event.offsetY - g_scrollDistance; + g_scrollDistance = event.offsetY; + // fix resize ratio + mouseEvent.x = event.globalX / g_resizeRate; + mouseEvent.y = event.globalY / g_resizeRate; + scrollY = scrollY / g_resizeRate; + mouseEvent.button = OH_NATIVEXCOMPONENT_NONE_BUTTON; + mouseEvent.action = OH_NATIVEXCOMPONENT_MOUSE_NONE; + mouseEvent.timestamp = event.timestamp; + ohosTouchProcessor_.HandleMouseEvent(std::stoll(shellholderId_), nullptr, mouseEvent, scrollY); + } else { + g_scrollDistance = 0.0; + } + } else { + LOGE("XComponentManger::DispatchMouseWheelEvent XComponentBase is not attached"); + } +} } // namespace flutter \ No newline at end of file diff --git a/shell/platform/ohos/ohos_xcomponent_adapter.h b/shell/platform/ohos/ohos_xcomponent_adapter.h index 4fd6ff49902c6c02c1745b7bbd265a48d7f93fd0..aecea89ded2c10ff634eeaa30e292e56bc661730 100644 --- a/shell/platform/ohos/ohos_xcomponent_adapter.h +++ b/shell/platform/ohos/ohos_xcomponent_adapter.h @@ -18,6 +18,7 @@ #include #include #include "flutter/shell/platform/ohos/ohos_touch_processor.h" +#include "flutter/shell/platform/ohos/napi/platform_view_ohos_napi.h" #include "napi/native_api.h" #include "napi_common.h" #include @@ -41,9 +42,12 @@ public: void OnSurfaceChanged(OH_NativeXComponent* component, void* window); void OnSurfaceDestroyed(OH_NativeXComponent* component, void* window); void OnDispatchTouchEvent(OH_NativeXComponent* component, void* window); + void OnDispatchMouseEvent(OH_NativeXComponent* component, void* window); + void OnDispatchMouseWheelEvent(mouseWheelEvent event); OH_NativeXComponent_TouchEvent touchEvent_; OH_NativeXComponent_Callback callback_; + OH_NativeXComponent_MouseEvent_Callback mouseCallback_; std::string id_; std::string shellholderId_; bool isEngineAttached_; @@ -66,6 +70,7 @@ class XComponentAdapter { OH_NativeXComponent* nativeXComponent); void AttachFlutterEngine(std::string& id, std::string& shellholderId); void DetachFlutterEngine(std::string& id); + void OnMouseWheel(std::string& id, mouseWheelEvent event); public: std::map xcomponetMap_; diff --git a/shell/platform/ohos/platform_view_ohos.cpp b/shell/platform/ohos/platform_view_ohos.cpp index f0b10bf4b1fd001c4fecbec5af970e6b0b76e83b..b6a03a0d5156e979a9217dd7d6442e74a2a9ad78 100644 --- a/shell/platform/ohos/platform_view_ohos.cpp +++ b/shell/platform/ohos/platform_view_ohos.cpp @@ -537,6 +537,11 @@ void PlatformViewOHOS::RegisterExternalTextureByPixelMap(int64_t texture_id, Nat } } +void PlatformViewOHOS::OnTouchEvent(const std::shared_ptr touchPacketString, int size) +{ + return napi_facade_->FlutterViewOnTouchEvent(touchPacketString, size); +} + OhosImageFrameData::OhosImageFrameData( PlatformViewOHOS* context, int64_t texture_id) diff --git a/shell/platform/ohos/platform_view_ohos.h b/shell/platform/ohos/platform_view_ohos.h index 9bcfe5b29bdbfc5c3e6a6a8ce850e8efb5dde572..8e070854afc2f19e8d798ea575c64f66d24ab025 100644 --- a/shell/platform/ohos/platform_view_ohos.h +++ b/shell/platform/ohos/platform_view_ohos.h @@ -121,6 +121,7 @@ class PlatformViewOHOS final : public PlatformView { const override { return platform_message_handler_; } + void OnTouchEvent(std::shared_ptr touchPacketString, int size); private: const std::shared_ptr napi_facade_; diff --git a/shell/platform/ohos/vsync_waiter_ohos.cpp b/shell/platform/ohos/vsync_waiter_ohos.cpp index 26cc3d2d8b70f30aff8ce278e8b5592a489782d2..595b4273a4f4db60d7da31b1362394669854bc9f 100644 --- a/shell/platform/ohos/vsync_waiter_ohos.cpp +++ b/shell/platform/ohos/vsync_waiter_ohos.cpp @@ -34,6 +34,7 @@ VsyncWaiterOHOS::~VsyncWaiterOHOS() { } void VsyncWaiterOHOS::AwaitVSync() { + TRACE_EVENT0("flutter", "VsyncWaiterOHOS::AwaitVSync"); if (vsyncHandle == nullptr) { LOGE("AwaitVSync vsyncHandle is nullptr"); return;