diff --git a/bundle.json b/bundle.json index b66b135f9f7ced270ed4f58f78649ad170cb3b75..c3add9d00e0314b0563491a72b4fc7435a267031 100644 --- a/bundle.json +++ b/bundle.json @@ -58,7 +58,8 @@ "hicollie", "resource_management", "screenlock_mgr", - "cJSON" + "cJSON", + "runtime_core" ] }, "build": { @@ -69,7 +70,8 @@ "//base/inputmethod/imf/frameworks/js/napi/inputmethodclient:inputmethod", "//base/inputmethod/imf/frameworks/js/napi/inputmethodlist:inputmethodlist", "//base/inputmethod/imf/frameworks/js/napi/inputmethodpanel:panel", - "//base/inputmethod/imf/frameworks/ndk:ohinputmethod" + "//base/inputmethod/imf/frameworks/ndk:ohinputmethod", + "//base/inputmethod/imf/frameworks/ets/taihe/inputMethod:inputmethod_taihe" ], "service_group": [ "//base/inputmethod/imf/etc/init:inputmethodservice.cfg", diff --git a/frameworks/ets/taihe/inputMethod/BUILD.gn b/frameworks/ets/taihe/inputMethod/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..7e537f19cc622139ec009fc111ac4e1d09a5aa18 --- /dev/null +++ b/frameworks/ets/taihe/inputMethod/BUILD.gn @@ -0,0 +1,116 @@ +# Copyright (c) 2025 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//build/config/components/ets_frontend/ets2abc_config.gni") +import("//base/inputmethod/imf/inputmethod.gni") +import("//build/ohos.gni") +import("//build/ohos/taihe_idl/taihe.gni") + +subsystem_name = "inputmethod" +part_name = "imf" +taihe_generated_file_path = "$taihe_file_path/out/$subsystem_name/$part_name" + +copy_taihe_idl("copy_taihe") { + sources = [ + "idl/ohos.inputMethod.Panel.taihe", + "idl/ohos.inputMethod.taihe", + "idl/ohos.InputMethodSubtype.taihe" + ] + + external_deps = [] +} + +ohos_taihe("run_taihe") { + taihe_generated_file_path = "$taihe_generated_file_path" + deps = [ ":copy_taihe" ] + outputs = [ + "$taihe_generated_file_path/src/ohos.inputMethod.ani.cpp", + "$taihe_generated_file_path/src/ohos.inputMethod.abi.c", + "$taihe_generated_file_path/src/ohos.inputMethod.Panel.ani.cpp", + "$taihe_generated_file_path/src/ohos.inputMethod.Panel.abi.c", + "$taihe_generated_file_path/src/ohos.InputMethodSubtype.ani.cpp", + "$taihe_generated_file_path/src/ohos.InputMethodSubtype.abi.c", + ] +} + +taihe_shared_library("inputmethod_taihe_native") { + branch_protector_ret = "pac_ret" + sanitize = { + boundary_sanitize = true + cfi = true + cfi_cross_dso = true + debug = false + integer_overflow = true + ubsan = true + } + taihe_generated_file_path = "$taihe_generated_file_path" + subsystem_name = "$subsystem_name" + part_name = "$part_name" + include_dirs = [ + "include", + "${inputmethod_path}/frameworks/js/napi/inputmethodability", + "${inputmethod_path}/frameworks/js/napi/inputmethodclient", + ] + sources = get_target_outputs(":run_taihe") + sources += [ + "src/ani_constructor.cpp", + "src/ohos.inputMethod.impl.cpp", + "src/input_method_controller_impl.cpp", + "src/input_method_setting_impl.cpp", + "src/input_method_event_listener.cpp", + "src/input_method_text_changed_listener.cpp", + "${inputmethod_path}/frameworks/js/napi/inputmethodclient/js_utils.cpp", + ] + deps = [ + ":run_taihe", + "${inputmethod_path}/frameworks/js/napi/common:inputmethod_js_common", + "${inputmethod_path}/interfaces/inner_api/inputmethod_controller:inputmethod_client", + ] + external_deps = [ + "ability_base:want", + "ability_runtime:abilitykit_native", + "ability_runtime:extensionkit_native", + "bundle_framework:bms_ani_common", + "c_utils:utils", + "eventhandler:libeventhandler", + "ffrt:libffrt", + "hilog:libhilog" + ] +} + +generate_static_abc("inputmethod_abc") { + base_url = "$taihe_generated_file_path" + files = [ + "$taihe_generated_file_path/@ohos.inputMethod.ets", + "$taihe_generated_file_path/@ohos.inputMethod.Panel.ets", + "$taihe_generated_file_path/@ohos.InputMethodSubtype.ets", + ] + is_boot_abc = "True" + device_dst_file = "/system/framework/inputmethod_abc.abc" + dependencies = [ ":run_taihe" ] +} + +ohos_prebuilt_etc("inputmethod_etc") { + source = "$target_out_dir/inputmethod_abc.abc" + module_install_dir = "framework" + part_name = "$part_name" + subsystem_name = "$subsystem_name" + deps = [ ":inputmethod_abc"] +} + +group("inputmethod_taihe") { + deps = [ + ":inputmethod_etc", + ":inputmethod_taihe_native", + ] +} diff --git a/frameworks/ets/taihe/inputMethod/idl/ohos.InputMethodSubtype.taihe b/frameworks/ets/taihe/inputMethod/idl/ohos.InputMethodSubtype.taihe new file mode 100644 index 0000000000000000000000000000000000000000..77f67b211e965f26dd61a1d403defae7f2089362 --- /dev/null +++ b/frameworks/ets/taihe/inputMethod/idl/ohos.InputMethodSubtype.taihe @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@!namespace("@ohos.InputMethodSubtype") +struct InputMethodSubtype { + @readonly name: String; + @readonly id: String; + @readonly locale: String; + @readonly language: String; + @readonly mode: Optional; + @readonly icon: Optional; + @readonly iconId: Optional; + @readonly label: Optional; + @readonly labelId: Optional; + extra: Optional; +} \ No newline at end of file diff --git a/frameworks/ets/taihe/inputMethod/idl/ohos.inputMethod.Panel.taihe b/frameworks/ets/taihe/inputMethod/idl/ohos.inputMethod.Panel.taihe new file mode 100644 index 0000000000000000000000000000000000000000..25488e3a90ee10c8af21295e221ee6569302fc96 --- /dev/null +++ b/frameworks/ets/taihe/inputMethod/idl/ohos.inputMethod.Panel.taihe @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@!namespace("@ohos.inputMethod.Panel") +enum PanelFlag: i32 { + FLAG_FIXED = 0, + FLAG_FLOATING = 1, + FLAG_CANDIDATE = 2 +} + +enum PanelType: i32 { + SOFT_KEYBOARD = 0, + STATUS_BAR = 1 +} + +struct PanelInfo { + type : PanelType; + flag : Optional; +} \ No newline at end of file diff --git a/frameworks/ets/taihe/inputMethod/idl/ohos.inputMethod.taihe b/frameworks/ets/taihe/inputMethod/idl/ohos.inputMethod.taihe new file mode 100644 index 0000000000000000000000000000000000000000..8fd2f5f1ab187e4bac4624f8990e385402b0530b --- /dev/null +++ b/frameworks/ets/taihe/inputMethod/idl/ohos.inputMethod.taihe @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +@!sts_inject_into_module("import {ElementName} from 'bundleManager.ElementName';") +@!namespace("@ohos.inputMethod", "inputMethod") +from ohos.inputMethod.Panel use PanelInfo; +from ohos.InputMethodSubtype use InputMethodSubtype; +@!sts_inject(""" +static { loadLibrary("inputmethod_taihe_native.z") } +""") + +struct InputMethodProperty { + @readonly name: String; + @readonly id: String; + @readonly label: Optional; + @readonly labelId: Optional; + @readonly icon: Optional; + @readonly iconId: Optional; + extra: Optional; +} + +struct InputWindowInfo { + name: String; + left: i32; + top: i32; + width: f64; + height: f64; +} + +interface InputMethodSetting { + @gen_async("getInputMethods") + @gen_promise("getInputMethods") + GetInputMethodsSync(enable: bool): Array; + @gen_async("listCurrentInputMethodSubtype") + @gen_promise("listCurrentInputMethodSubtype") + ListCurrentInputMethodSubtypeSync(): Array; + @gen_async("listInputMethodSubtype") + @gen_promise("listInputMethodSubtype") + ListInputMethodSubtypeSync(inputMethodProperty: InputMethodProperty): Array; + IsPanelShown(panelInfo: PanelInfo): bool; + @gen_async("getAllInputMethods") + @gen_promise("getAllInputMethods") + GetAllInputMethodsSync(): Array; + @!sts_inject_into_interface(""" + on(type: string, callback: (inputMethodProperty: InputMethodProperty, inputMethodSubtype: __ohos_InputMethodSubtype.InputMethodSubtype) => void): void; + off(type: string, callback?: (inputMethodProperty: InputMethodProperty, inputMethodSubtype: __ohos_InputMethodSubtype.InputMethodSubtype) => void): void; + on(type: string, callback: (info: Array) => void): void; + off(type: string, callback?: (info: Array) => void): void; + """) + @!sts_inject_into_class(""" + on(type: string, cb: Object) { + switch(type) { + case "imeHide": return this.onImeHide(cb as (info: Array) => void, cb); + case "imeShow": return this.onImeShow(cb as (info: Array) => void, cb); + case "imeChange": return this.onImeChange(cb as (inputMethodProperty: InputMethodProperty, inputMethodSubtype: __ohos_InputMethodSubtype.InputMethodSubtype) => void, cb); + default: throw new Error(`Unknown type: ${type}`); + } + } + off(type: string, cb?: Object) { + switch(type) { + case "imeHide": return this.offImeHide(cb); + case "imeShow": return this.offImeShow(cb); + case "imeChange": return this.offImeChange(cb); + default: throw new Error(`Unknown type: ${type}`); + } + } + """) + OnImeHide(f: (info: Array) => void, opq: Opaque); + OffImeHide(opq: Optional); + OnImeShow(f: (info: Array) => void, opq: Opaque); + OffImeShow(opq: Optional); + OnImeChange(f: (inputMethodProperty: InputMethodProperty, inputMethodSubtype: InputMethodSubtype) => void, opq: Opaque); + OffImeChange(opq: Optional); +} + +struct CursorInfo { + left: f64; + top: f64; + width: f64; + height: f64; +} + +enum Direction : i32 { + CURSOR_UP = 1, + CURSOR_DOWN = 2, + CURSOR_LEFT = 3, + CURSOR_RIGHT = 4 +} + +struct Range { + start: i32; + end: i32; +} + +struct Movement { + direction : Direction; +} + +enum ExtendAction : i32 { + SELECT_ALL = 0, + CUT = 3, + COPY = 4, + PASTE = 5 +} + +enum TextInputType : i32 { + NONE = -1, + TEXT = 0, + MULTILINE = 1, + NUMBER = 2, + PHONE = 3, + DATETIME = 4, + EMAIL_ADDRESS = 5, + URL = 6, + VISIBLE_PASSWORD = 7, + NUMBER_PASSWORD = 8 +} + +enum EnterKeyType : i32 { + UNSPECIFIED = 0, + NONE = 1, + GO = 2, + SEARCH = 3, + SEND = 4, + NEXT = 5, + DONE = 6, + PREVIOUS = 7, + NEWLINE = 8 +} + +struct FunctionKey { + enterKeyType : EnterKeyType; +} + +struct InputAttribute { + textInputType: TextInputType; + enterKeyType: EnterKeyType; +} + +struct TextConfig { + inputAttribute: InputAttribute; + @readonly cursorInfo: Optional; + @readonly selection: Optional; + @readonly windowId: Optional; +} + + +enum KeyboardStatus : i32 { + NONE = 0, + HIDE = 1, + SHOW = 2 +} + +enum RequestKeyboardReason : i32 { + NONE = 0, + MOUSE = 1, + TOUCH = 2, + OTHER = 20 +} + +interface InputMethodController { + @gen_async("hideSoftKeyboard") + @gen_promise("hideSoftKeyboard") + HideSoftKeyboardSync(); + @gen_promise("showTextInput") + ShowTextInputHasParam(requestKeyboardReason: RequestKeyboardReason); + @gen_async("showTextInput") + @gen_promise("showTextInput") + ShowTextInputSync(); + @gen_async("hideTextInput") + @gen_promise("hideTextInput") + HideTextInputSync(); + @gen_async("attach") + @gen_promise("attach") + AttachSync(showKeyboard: bool, textConfig: TextConfig); + @gen_promise("attach") + AttachWithReason(showKeyboard: bool, textConfig: TextConfig, requestKeyboardReason: RequestKeyboardReason); + @gen_async("detach") + @gen_promise("detach") + DetachSync(); + @!sts_inject_into_interface(""" + on(type: string, callback: (arg0 : Object) => Object): void; + off(type: string, callback?: (arg0 : Object) => Object): void; + on(type: string, callback: () => int): void; + off(type: string, callback?: () => int): void; + """) + @!sts_inject_into_class(""" + on(type: string, cb: Object) { + switch(type) { + case "selectByRange": return this.onSelectByRange(cb as (range : Range) => void, cb); + case "selectByMovement": return this.onSelectByMovement(cb as (movement : Movement) => void, cb); + case "insertText": return this.onInsertText(cb as (text: String) => void, cb); + case "deleteLeft": return this.onDeleteLeft(cb as (length: int) => void, cb); + case "deleteRight": return this.onDeleteRight(cb as (length: int) => void, cb); + case "sendKeyboardStatus": return this.onSendKeyboardStatus(cb as (keyboardStatus: KeyboardStatus) => void, cb); + case "sendFunctionKey": return this.onSendFunctionKey(cb as (functionKey: FunctionKey) => void, cb); + case "moveCursor": return this.onMoveCursor(cb as (direction: Direction) => void, cb); + case "handleExtendAction": return this.onHandleExtendAction(cb as (extendAction: ExtendAction) => void, cb); + case "getLeftTextOfCursor": return this.onGetLeftTextOfCursor(cb as (length: int) => String, cb); + case "getRightTextOfCursor": return this.onGetRightTextOfCursor(cb as (length: int) => String, cb); + case "getTextIndexAtCursor": return this.onGetTextIndexAtCursor(cb as () => int, cb); + default: throw new Error(`Unknown type: ${type}`); + } + } + off(type: string, cb?: Object) { + switch(type) { + case "selectByRange": return this.offSelectByRange(cb); + case "selectByMovement": return this.offSelectByMovement(cb); + case "insertText": return this.offInsertText(cb); + case "deleteLeft": return this.offDeleteLeft(cb); + case "deleteRight": return this.offDeleteRight(cb); + case "sendKeyboardStatus": return this.offSendKeyboardStatus(cb); + case "sendFunctionKey": return this.offSendFunctionKey(cb); + case "moveCursor": return this.offMoveCursor(cb); + case "handleExtendAction": return this.offHandleExtendAction(cb); + case "getLeftTextOfCursor": return this.offGetLeftTextOfCursor(cb); + case "getRightTextOfCursor": return this.offGetRightTextOfCursor(cb); + case "getTextIndexAtCursor": return this.offGetTextIndexAtCursor(cb); + default: throw new Error(`Unknown type: ${type}`); + } + } + """) + OnSelectByRange(f: (range : Range) => void, opq: Opaque); + OffSelectByRange(opq: Optional); + OnSelectByMovement(f: (movement : Movement) => void, opq: Opaque); + OffSelectByMovement(opq: Optional); + OnInsertText(f: (text: String) => void, opq: Opaque); + OffInsertText(opq: Optional); + OnDeleteLeft(f: (length: i32) => void, opq: Opaque); + OffDeleteLeft(opq: Optional); + OnDeleteRight(f: (length: i32) => void, opq: Opaque); + OffDeleteRight(opq: Optional); + OnSendKeyboardStatus(f: (keyboardStatus: KeyboardStatus) => void, opq: Opaque); + OffSendKeyboardStatus(opq: Optional); + OnSendFunctionKey(f: (functionKey: FunctionKey) => void, opq: Opaque); + OffSendFunctionKey(opq: Optional); + OnMoveCursor(f: (direction: Direction) => void, opq: Opaque); + OffMoveCursor(opq: Optional); + OnHandleExtendAction(f: (action: ExtendAction) => void, opq: Opaque); + OffHandleExtendAction(opq: Optional); + OnGetLeftTextOfCursor(f: (length: i32) => String, opq: Opaque); + OffGetLeftTextOfCursor(opq: Optional); + OnGetRightTextOfCursor(f: (length: i32) => String, opq: Opaque); + OffGetRightTextOfCursor(opq: Optional); + OnGetTextIndexAtCursor(f: () => i32, opq: Opaque); + OffGetTextIndexAtCursor(opq: Optional); +} + +function GetSetting(): InputMethodSetting; +function GetController(): InputMethodController; +function GetDefaultInputMethod(): InputMethodProperty; +function GetCurrentInputMethod(): InputMethodProperty; +function GetCurrentInputMethodSubtype(): InputMethodSubtype; +function GetSystemInputMethodConfigAbility(): @sts_type("ElementName") Opaque; +@gen_async("switchInputMethod") +@gen_promise("switchInputMethod") +function SwitchInputMethodWithTarget(target: InputMethodProperty): bool; +@gen_promise("switchInputMethod") +function SwitchInputMethodSync(bundleName: String, subtypeId: Optional); +@gen_async("switchCurrentInputMethodSubtype") +@gen_promise("switchCurrentInputMethodSubtype") +function SwitchCurrentInputMethodSubtypeSync(target: InputMethodSubtype): bool; \ No newline at end of file diff --git a/frameworks/ets/taihe/inputMethod/include/ani_common.h b/frameworks/ets/taihe/inputMethod/include/ani_common.h new file mode 100644 index 0000000000000000000000000000000000000000..756f4af3a5a9106b2321888dd7d254f714303a0c --- /dev/null +++ b/frameworks/ets/taihe/inputMethod/include/ani_common.h @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef INPUT_METHOD_TAIHE_ANI_COMMON_H +#define INPUT_METHOD_TAIHE_ANI_COMMON_H + +#include "input_method_property.h" +#include "input_method_utils.h" +#include "ohos.inputMethod.impl.hpp" +#include "ohos.inputMethod.proj.hpp" +#include "taihe/runtime.hpp" +using InputMethodProperty_t = ohos::inputMethod::InputMethodProperty; +using InputMethodSubtype_t = ohos::InputMethodSubtype::InputMethodSubtype; +using PanelInfo_t = ohos::inputMethod::Panel::PanelInfo; +using InputWindowInfo_t = ohos::inputMethod::InputWindowInfo; +using RequestKeyboardReason_t = ohos::inputMethod::RequestKeyboardReason; +using TextConfig_t = ohos::inputMethod::TextConfig; +using Range_t = ohos::inputMethod::Range; +using Movement_t = ohos::inputMethod::Movement; +using KeyboardStatus_t = ohos::inputMethod::KeyboardStatus; +using FunctionKey_t = ohos::inputMethod::FunctionKey; +using ExtendAction_t = ohos::inputMethod::ExtendAction; +using EnterKeyType_t = ohos::inputMethod::EnterKeyType; +using Direction_t = ohos::inputMethod::Direction; +namespace OHOS { +namespace MiscServices { +constexpr const int32_t SELECT_ALL = 0; +constexpr const int32_t CUT = 3; +constexpr const int32_t COPY = 4; +constexpr const int32_t PASTE = 5; + +class PropertyConverter { +public: + static InputMethodProperty_t ConvertProperty(const std::shared_ptr &obj) + { + return ConvertPropertyImpl(*obj); + } + + static InputMethodProperty_t ConvertProperty(const Property &obj) + { + return ConvertPropertyImpl(obj); + } + + static InputMethodSubtype_t ConvertSubProperty(const std::shared_ptr &obj) + { + return ConvertSubPropertyImpl(*obj); + } + + static InputMethodSubtype_t ConvertSubProperty(const SubProperty &obj) + { + return ConvertSubPropertyImpl(obj); + } + +private: + template + static InputMethodProperty_t ConvertPropertyImpl(T &&obj) + { + static_assert(std::is_same_v, Property>, "Invalid type for Property conversion"); + + InputMethodProperty_t result{}; + result.name = std::forward(obj).name; + result.id = obj.id; + result.label = taihe::optional(std::in_place_t{}, obj.label); + result.labelId = taihe::optional(std::in_place_t{}, obj.labelId); + result.icon = taihe::optional(std::in_place_t{}, obj.icon); + result.iconId = taihe::optional(std::in_place_t{}, obj.iconId); + return result; + } + + template + static InputMethodSubtype_t ConvertSubPropertyImpl(T &&obj) + { + static_assert(std::is_same_v, SubProperty>, "Invalid type for SubProperty conversion"); + + InputMethodSubtype_t result{}; + result.name = std::forward(obj).name; + result.id = obj.id; + result.locale = obj.locale; + result.language = obj.language; + result.label = taihe::optional(std::in_place_t{}, obj.label); + result.labelId = taihe::optional(std::in_place_t{}, obj.labelId); + result.icon = taihe::optional(std::in_place_t{}, obj.icon); + result.iconId = taihe::optional(std::in_place_t{}, obj.iconId); + return result; + } +}; + +using callbackType = std::variant, taihe::callback, + taihe::callback, taihe::callback, taihe::callback, + taihe::callback, taihe::callback, + taihe::callback, taihe::callback, + taihe::callback, taihe::callback, + taihe::callback, taihe::callback)>, + taihe::callback>; + +struct CallbackObject { + CallbackObject(callbackType cb, ani_ref ref) : callback(cb), ref(ref) + { + } + void Release() + { + taihe::env_guard guard; + if (auto *env = guard.get_env()) { + env->GlobalReference_Delete(ref); + } + } + callbackType callback; + ani_ref ref; +}; + +class GlobalRefGuard { + ani_env *env_ = nullptr; + ani_ref ref_ = nullptr; + +public: + GlobalRefGuard(ani_env *env, ani_object obj) : env_(env) + { + if (!env_) + return; + if (ANI_OK != env_->GlobalReference_Create(obj, &ref_)) { + ref_ = nullptr; + } + } + explicit operator bool() const + { + return ref_ != nullptr; + } + ani_ref get() const + { + return ref_; + } + ~GlobalRefGuard() + { + if (env_ && ref_) { + env_->GlobalReference_Delete(ref_); + } + } + + GlobalRefGuard(const GlobalRefGuard &) = delete; + GlobalRefGuard &operator=(const GlobalRefGuard &) = delete; +}; + +class EnumConvert { +public: + static Direction_t ConvertDirection(Direction direction) + { + switch (direction) { + case Direction::UP: + return Direction_t::key_t::CURSOR_UP; + case Direction::DOWN: + return Direction_t::key_t::CURSOR_DOWN; + case Direction::LEFT: + return Direction_t::key_t::CURSOR_LEFT; + case Direction::RIGHT: + return Direction_t::key_t::CURSOR_RIGHT; + default: + return Direction_t::key_t::CURSOR_UP; + } + } + + static ExtendAction_t ConvertExtendAction(int32_t action) + { + switch (action) { + case SELECT_ALL: + return ExtendAction_t::key_t::SELECT_ALL; + case CUT: + return ExtendAction_t::key_t::CUT; + case COPY: + return ExtendAction_t::key_t::COPY; + case PASTE: + return ExtendAction_t::key_t::PASTE; + default: + return ExtendAction_t::key_t::SELECT_ALL; + } + } + + static EnterKeyType_t ConvertEnterKeyType(EnterKeyType type) + { + switch (type) { + case EnterKeyType::UNSPECIFIED: + return EnterKeyType_t::key_t::UNSPECIFIED; + case EnterKeyType::NONE: + return EnterKeyType_t::key_t::NONE; + case EnterKeyType::GO: + return EnterKeyType_t::key_t::GO; + case EnterKeyType::SEARCH: + return EnterKeyType_t::key_t::SEARCH; + case EnterKeyType::SEND: + return EnterKeyType_t::key_t::SEND; + case EnterKeyType::NEXT: + return EnterKeyType_t::key_t::NEXT; + case EnterKeyType::DONE: + return EnterKeyType_t::key_t::DONE; + case EnterKeyType::PREVIOUS: + return EnterKeyType_t::key_t::PREVIOUS; + case EnterKeyType::NEW_LINE: + return EnterKeyType_t::key_t::NEWLINE; + default: + return EnterKeyType_t::key_t::UNSPECIFIED; + } + } + + static KeyboardStatus_t ConvertKeyboardStatus(KeyboardStatus status) + { + switch (status) { + case KeyboardStatus::NONE: + return KeyboardStatus_t::key_t::NONE; + case KeyboardStatus::SHOW: + return KeyboardStatus_t::key_t::SHOW; + case KeyboardStatus::HIDE: + return KeyboardStatus_t::key_t::HIDE; + default: + return KeyboardStatus_t::key_t::NONE; + } + } +}; +} // namespace MiscServices +} // namespace OHOS +#endif \ No newline at end of file diff --git a/frameworks/ets/taihe/inputMethod/include/input_method_controller_impl.h b/frameworks/ets/taihe/inputMethod/include/input_method_controller_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..c84c9fb545fa5c07c979e3cbc042e3355c456276 --- /dev/null +++ b/frameworks/ets/taihe/inputMethod/include/input_method_controller_impl.h @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef TAIHE_INPUT_METHOD_CONTROLLER_IMPL_H +#define TAIHE_INPUT_METHOD_CONTROLLER_IMPL_H +#include +#include +#include +#include +#include +#include + +#include "ani_common.h" +#include "controller_listener.h" +#include "input_method_utils.h" +#include "ohos.inputMethod.impl.hpp" +#include "ohos.inputMethod.proj.hpp" +namespace OHOS { +namespace MiscServices { +class InputMethodControllerImpl : public ControllerListener { +public: + static std::shared_ptr GetInstance(); + void HideSoftKeyboardSync(); + void ShowTextInputHasParam(RequestKeyboardReason_t requestKeyboardReason); + void ShowTextInputSync(); + void HideTextInputSync(); + void AttachSync(bool showKeyboard, TextConfig_t const &textConfig); + void AttachWithReason(bool showKeyboard, TextConfig_t const &textConfig, + RequestKeyboardReason_t requestKeyboardReason); + void DetachSync(); + void RegisterListener(std::string const &type, callbackType &&cb, uintptr_t opq); + void UnRegisterListener(std::string const &type, taihe::optional_view opq); + + void InsertTextCallback(const std::u16string &text); + void DeleteLeftCallback(int32_t length); + void DeleteRightCallback(int32_t length); + void SendKeyboardStatusCallback(const KeyboardStatus &status); + void SendFunctionKeyCallback(const FunctionKey &key); + void MoveCursorCallback(const Direction &direction); + void HandleExtendActionCallback(int32_t action); + std::u16string GetLeftTextOfCursorCallback(int32_t number); + std::u16string GetRightTextOfCursorCallback(int32_t number); + int32_t GetTextIndexAtCursorCallback(); + + void OnSelectByRange(int32_t start, int32_t end) override; + void OnSelectByMovement(int32_t direction) override; + +private: + std::mutex mutex_; + std::map>> jsCbMap_; + static std::mutex controllerMutex_; + static std::shared_ptr controller_; + static const std::set TEXT_EVENT_TYPE; +}; + +class IMFControllerImpl { +public: + void HideSoftKeyboardSync() + { + InputMethodControllerImpl::GetInstance()->HideSoftKeyboardSync(); + } + void ShowTextInputHasParam(RequestKeyboardReason_t requestKeyboardReason) + { + InputMethodControllerImpl::GetInstance()->ShowTextInputHasParam(requestKeyboardReason); + } + void ShowTextInputSync() + { + InputMethodControllerImpl::GetInstance()->ShowTextInputSync(); + } + void HideTextInputSync() + { + InputMethodControllerImpl::GetInstance()->HideTextInputSync(); + } + void AttachSync(bool showKeyboard, TextConfig_t const &textConfig) + { + InputMethodControllerImpl::GetInstance()->AttachSync(showKeyboard, textConfig); + } + void AttachWithReason(bool showKeyboard, TextConfig_t const &textConfig, + RequestKeyboardReason_t requestKeyboardReason) + { + InputMethodControllerImpl::GetInstance()->AttachWithReason(showKeyboard, textConfig, requestKeyboardReason); + } + void DetachSync() + { + InputMethodControllerImpl::GetInstance()->DetachSync(); + } + void OnSelectByRange(taihe::callback_view f, uintptr_t opq) + { + InputMethodControllerImpl::GetInstance()->RegisterListener("selectByRange", f, opq); + } + void OffSelectByRange(taihe::optional_view opq) + { + InputMethodControllerImpl::GetInstance()->UnRegisterListener("selectByRange", opq); + } + void OnSelectByMovement(taihe::callback_view f, uintptr_t opq) + { + InputMethodControllerImpl::GetInstance()->RegisterListener("selectByMovement", f, opq); + } + void OffSelectByMovement(taihe::optional_view opq) + { + InputMethodControllerImpl::GetInstance()->UnRegisterListener("selectByMovement", opq); + } + void OnInsertText(taihe::callback_view f, uintptr_t opq) + { + InputMethodControllerImpl::GetInstance()->RegisterListener("insertText", f, opq); + } + void OffInsertText(taihe::optional_view opq) + { + InputMethodControllerImpl::GetInstance()->UnRegisterListener("insertText", opq); + } + void OnDeleteLeft(taihe::callback_view f, uintptr_t opq) + { + InputMethodControllerImpl::GetInstance()->RegisterListener("deleteLeft", f, opq); + } + void OffDeleteLeft(taihe::optional_view opq) + { + InputMethodControllerImpl::GetInstance()->UnRegisterListener("deleteLeft", opq); + } + void OnDeleteRight(taihe::callback_view f, uintptr_t opq) + { + InputMethodControllerImpl::GetInstance()->RegisterListener("deleteRight", f, opq); + } + void OffDeleteRight(taihe::optional_view opq) + { + InputMethodControllerImpl::GetInstance()->UnRegisterListener("deleteRight", opq); + } + void OnSendKeyboardStatus(taihe::callback_view f, uintptr_t opq) + { + InputMethodControllerImpl::GetInstance()->RegisterListener("sendKeyboardStatus", f, opq); + } + void OffSendKeyboardStatus(taihe::optional_view opq) + { + InputMethodControllerImpl::GetInstance()->UnRegisterListener("sendKeyboardStatus", opq); + } + void OnSendFunctionKey(taihe::callback_view f, uintptr_t opq) + { + InputMethodControllerImpl::GetInstance()->RegisterListener("sendFunctionKey", f, opq); + } + void OffSendFunctionKey(taihe::optional_view opq) + { + InputMethodControllerImpl::GetInstance()->UnRegisterListener("sendFunctionKey", opq); + } + void OnMoveCursor(taihe::callback_view f, uintptr_t opq) + { + InputMethodControllerImpl::GetInstance()->RegisterListener("moveCursor", f, opq); + } + void OffMoveCursor(taihe::optional_view opq) + { + InputMethodControllerImpl::GetInstance()->UnRegisterListener("moveCursor", opq); + } + void OnHandleExtendAction(taihe::callback_view f, uintptr_t opq) + { + InputMethodControllerImpl::GetInstance()->RegisterListener("handleExtendAction", f, opq); + } + void OffHandleExtendAction(taihe::optional_view opq) + { + InputMethodControllerImpl::GetInstance()->UnRegisterListener("handleExtendAction", opq); + } + void OnGetLeftTextOfCursor(taihe::callback_view f, uintptr_t opq) + { + InputMethodControllerImpl::GetInstance()->RegisterListener("getLeftTextOfCursor", f, opq); + } + void OffGetLeftTextOfCursor(taihe::optional_view opq) + { + InputMethodControllerImpl::GetInstance()->UnRegisterListener("getLeftTextOfCursor", opq); + } + void OnGetRightTextOfCursor(taihe::callback_view f, uintptr_t opq) + { + InputMethodControllerImpl::GetInstance()->RegisterListener("getRightTextOfCursor", f, opq); + } + void OffGetRightTextOfCursor(taihe::optional_view opq) + { + InputMethodControllerImpl::GetInstance()->UnRegisterListener("getRightTextOfCursor", opq); + } + void OnGetTextIndexAtCursor(taihe::callback_view f, uintptr_t opq) + { + InputMethodControllerImpl::GetInstance()->RegisterListener("getTextIndexAtCursor", f, opq); + } + void OffGetTextIndexAtCursor(taihe::optional_view opq) + { + InputMethodControllerImpl::GetInstance()->UnRegisterListener("getTextIndexAtCursor", opq); + } +}; +} // namespace MiscServices +} // namespace OHOS +#endif \ No newline at end of file diff --git a/frameworks/ets/taihe/inputMethod/include/input_method_event_listener.h b/frameworks/ets/taihe/inputMethod/include/input_method_event_listener.h new file mode 100644 index 0000000000000000000000000000000000000000..f3fd40690c38bfd0053ce3460b46ea8103d1cb5c --- /dev/null +++ b/frameworks/ets/taihe/inputMethod/include/input_method_event_listener.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FRAMEWORKS_ETS_TAIHE_INPUT_METHOD_EVENT_LISTENER_H +#define FRAMEWORKS_ETS_TAIHE_INPUT_METHOD_EVENT_LISTENER_H + +#include "ani_common.h" +#include "ime_event_listener.h" +#include "input_method_property.h" +#include "input_window_info.h" + +namespace OHOS { +namespace MiscServices { +class InputMethodEventListener : public ImeEventListener { +public: + InputMethodEventListener() = default; + ~InputMethodEventListener() = default; + static std::shared_ptr GetInstance(); + void OnImeChange(const Property &property, const SubProperty &subProperty); + void OnImeShow(const ImeWindowInfo &info); + void OnImeHide(const ImeWindowInfo &info); + +private: + static std::mutex listenerMutex_; + static std::shared_ptr inputMethodListener_; +}; +} // namespace MiscServices +} // namespace OHOS + +#endif // FRAMEWORKS_ETS_TAIHE_INPUT_METHOD_EVENT_LISTENER_H diff --git a/frameworks/ets/taihe/inputMethod/include/input_method_setting_impl.h b/frameworks/ets/taihe/inputMethod/include/input_method_setting_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..2e4da01398514d4b6d09a6c4d024419ac6d17cd2 --- /dev/null +++ b/frameworks/ets/taihe/inputMethod/include/input_method_setting_impl.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef TAIHE_INPUT_METHOD_SETTING_IMPL_H +#define TAIHE_INPUT_METHOD_SETTING_IMPL_H +#include +#include +#include + +#include "ani_common.h" +#include "ime_event_listener.h" +#include "input_window_info.h" +#include "ohos.inputMethod.impl.hpp" +#include "ohos.inputMethod.proj.hpp" +namespace OHOS { +namespace MiscServices { +class InputMethodSettingImpl { +public: + static InputMethodSettingImpl &GetInstance(); + taihe::array GetInputMethodsSync(bool enable); + taihe::array ListCurrentInputMethodSubtypeSync(); + taihe::array ListInputMethodSubtypeSync(InputMethodProperty_t const &inputMethodProperty); + bool IsPanelShown(PanelInfo_t const &panelInfo); + taihe::array GetAllInputMethodsSync(); + void OnImeChangeCallback(const Property &property, const SubProperty &subProperty); + void OnImeShowCallback(const ImeWindowInfo &info); + void OnImeHideCallback(const ImeWindowInfo &info); + void RegisterImeEvent(std::string const &eventName, int32_t eventMask, callbackType &&f, uintptr_t opq); + void UnregisterImeEvent(std::string const &eventName, int32_t eventMask, taihe::optional_view opq); + +private: + PanelFlag softKbShowingFlag_{ FLG_CANDIDATE_COLUMN }; + PanelFlag GetSoftKbShowingFlag(); + void SetSoftKbShowingFlag(PanelFlag flag); + void OnPanelStatusChange(std::string const &type, const InputWindowInfo &info); + void RegisterListener(std::string const &type, callbackType &&cb, uintptr_t opq); + void UnregisterListener(std::string const &type, taihe::optional_view opq, bool &isUpdateFlag); + void HandleRegistrationError(std::string const &eventName, int32_t errorCode); + std::mutex mutex_; + std::map>> jsCbMap_; +}; + +class IMFSettingImpl { +public: + IMFSettingImpl() + { + } + taihe::array GetInputMethodsSync(bool enable) + { + return InputMethodSettingImpl::GetInstance().GetInputMethodsSync(enable); + } + taihe::array ListCurrentInputMethodSubtypeSync() + { + return InputMethodSettingImpl::GetInstance().ListCurrentInputMethodSubtypeSync(); + } + taihe::array ListInputMethodSubtypeSync(InputMethodProperty_t const &inputMethodProperty) + { + return InputMethodSettingImpl::GetInstance().ListInputMethodSubtypeSync(inputMethodProperty); + } + bool IsPanelShown(PanelInfo_t const &panelInfo) + { + return InputMethodSettingImpl::GetInstance().IsPanelShown(panelInfo); + } + taihe::array GetAllInputMethodsSync() + { + return InputMethodSettingImpl::GetInstance().GetAllInputMethodsSync(); + } + void OnImeHide(taihe::callback_view)> f, uintptr_t opq) + { + InputMethodSettingImpl::GetInstance().RegisterImeEvent("imeHide", EVENT_IME_HIDE_MASK, f, opq); + } + void OffImeHide(taihe::optional_view opq) + { + InputMethodSettingImpl::GetInstance().UnregisterImeEvent("imeHide", EVENT_IME_HIDE_MASK, opq); + } + void OnImeShow(taihe::callback_view)> f, uintptr_t opq) + { + InputMethodSettingImpl::GetInstance().RegisterImeEvent("imeShow", EVENT_IME_SHOW_MASK, f, opq); + } + void OffImeShow(taihe::optional_view opq) + { + InputMethodSettingImpl::GetInstance().UnregisterImeEvent("imeShow", EVENT_IME_SHOW_MASK, opq); + } + void OnImeChange(taihe::callback_view f, + uintptr_t opq) + { + InputMethodSettingImpl::GetInstance().RegisterImeEvent("imeChange", EVENT_IME_CHANGE_MASK, f, opq); + } + void OffImeChange(taihe::optional_view opq) + { + InputMethodSettingImpl::GetInstance().UnregisterImeEvent("imeChange", EVENT_IME_CHANGE_MASK, opq); + } +}; +} // namespace MiscServices +} // namespace OHOS +#endif \ No newline at end of file diff --git a/frameworks/ets/taihe/inputMethod/include/input_method_text_changed_listener.h b/frameworks/ets/taihe/inputMethod/include/input_method_text_changed_listener.h new file mode 100644 index 0000000000000000000000000000000000000000..7773722660b7c9d40a6f14cf2cefa06435b1526c --- /dev/null +++ b/frameworks/ets/taihe/inputMethod/include/input_method_text_changed_listener.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef FRAMEWORKS_ETS_TAIHE_INPUT_METHOD_TEXT_CHANGED_LISTENER_H +#define FRAMEWORKS_ETS_TAIHE_INPUT_METHOD_TEXT_CHANGED_LISTENER_H + +#include "input_method_controller.h" +namespace OHOS { +namespace MiscServices { +class InputMethodTextChangedListener : public OnTextChangedListener { +public: + InputMethodTextChangedListener() = default; + ~InputMethodTextChangedListener() = default; + static sptr GetInstance(); + + void InsertText(const std::u16string &text) override; + void DeleteForward(int32_t length) override; + void DeleteBackward(int32_t length) override; + void SendKeyEventFromInputMethod(const KeyEvent &event) override + { + } + void SendKeyboardStatus(const KeyboardStatus &status) override; + void SendFunctionKey(const FunctionKey &functionKey) override; + void SetKeyboardStatus(bool status) override + { + } + void MoveCursor(const Direction direction) override; + void HandleSetSelection(int32_t start, int32_t end) override + { + } + void HandleExtendAction(int32_t action) override; + void HandleSelect(int32_t keyCode, int32_t cursorMoveSkip) override + { + } + std::u16string GetLeftTextOfCursor(int32_t number) override; + std::u16string GetRightTextOfCursor(int32_t number) override; + int32_t GetTextIndexAtCursor() override; + int32_t ReceivePrivateCommand(const std::unordered_map &privateCommand) override + { + return 0; + } + bool IsFromTs() override + { + return false; + } + int32_t SetPreviewText(const std::u16string &text, const Range &range) override + { + return 0; + } + void FinishTextPreview() override + { + } + +private: + static std::mutex listenerMutex_; + static sptr inputMethodListener_; +}; +} // namespace MiscServices +} // namespace OHOS +#endif // FRAMEWORKS_ETS_TAIHE_INPUT_METHOD_TEXT_CHANGE_LISTENER_H \ No newline at end of file diff --git a/frameworks/ets/taihe/inputMethod/src/ani_constructor.cpp b/frameworks/ets/taihe/inputMethod/src/ani_constructor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b3719ccb261cec82061ed4417fbe8d00ceca9069 --- /dev/null +++ b/frameworks/ets/taihe/inputMethod/src/ani_constructor.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ohos.inputMethod.Panel.ani.hpp" +#include "ohos.inputMethod.ani.hpp" +#include "ohos.InputMethodSubtype.ani.hpp" +ANI_EXPORT ani_status ANI_Constructor(ani_vm *vm, uint32_t *result) +{ + ani_env *env; + if (ANI_OK != vm->GetEnv(ANI_VERSION_1, &env)) { + return ANI_ERROR; + } + if (ANI_OK != ohos::inputMethod::ANIRegister(env)) { + std::cerr << "Error from ohos::inputMethod::ANIRegister" << std::endl; + return ANI_ERROR; + } + if (ANI_OK != ohos::InputMethodSubtype::ANIRegister(env)) { + std::cerr << "Error from ohos::InputMethodSubtype::ANIRegister" << std::endl; + return ANI_ERROR; + } + if (ANI_OK != ohos::inputMethod::Panel::ANIRegister(env)) { + std::cerr << "Error from ohos::inputMethod::Panel::ANIRegister" << std::endl; + return ANI_ERROR; + } + *result = ANI_VERSION_1; + return ANI_OK; +} diff --git a/frameworks/ets/taihe/inputMethod/src/input_method_controller_impl.cpp b/frameworks/ets/taihe/inputMethod/src/input_method_controller_impl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d149d463c84f2462432783920e2258ceb3e2745a --- /dev/null +++ b/frameworks/ets/taihe/inputMethod/src/input_method_controller_impl.cpp @@ -0,0 +1,337 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "input_method_controller_impl.h" + +#include + +#include "ani_common.h" +#include "input_method_controller.h" +#include "input_method_text_changed_listener.h" +#include "js_utils.h" +#include "stdexcept" +#include "string_ex.h" +#include "taihe/runtime.hpp" + +namespace OHOS { +namespace MiscServices { +using namespace taihe; +std::mutex InputMethodControllerImpl::controllerMutex_; +std::shared_ptr InputMethodControllerImpl::controller_{ nullptr }; +const std::set InputMethodControllerImpl::TEXT_EVENT_TYPE{ + "insertText", + "deleteLeft", + "deleteRight", + "sendKeyboardStatus", + "sendFunctionKey", + "moveCursor", + "handleExtendAction", + "getLeftTextOfCursor", + "getRightTextOfCursor", + "getTextIndexAtCursor", +}; +std::shared_ptr InputMethodControllerImpl::GetInstance() +{ + if (controller_ == nullptr) { + std::lock_guard lock(controllerMutex_); + if (controller_ == nullptr) { + auto controller = std::make_shared(); + controller_ = controller; + InputMethodController::GetInstance()->SetControllerListener(controller_); + } + } + return controller_; +} + +void InputMethodControllerImpl::HideSoftKeyboardSync() +{ + int32_t errCode = InputMethodController::GetInstance()->HideSoftKeyboard(); + if (errCode != ErrorCode::NO_ERROR) { + set_business_error(JsUtils::Convert(errCode), JsUtils::ToMessage(JsUtils::Convert(errCode))); + IMSA_HILOGE("InputMethodController::HideSoftKeyboard failed, errCode: %{public}d!", errCode); + } +} + +void InputMethodControllerImpl::ShowTextInputHasParam(RequestKeyboardReason_t requestKeyboardReason) +{ + AttachOptions attachOptions; + attachOptions.requestKeyboardReason = static_cast(requestKeyboardReason.get_value()); + int32_t errCode = InputMethodController::GetInstance()->ShowTextInput(attachOptions, ClientType::JS); + if (errCode != ErrorCode::NO_ERROR) { + set_business_error(JsUtils::Convert(errCode), JsUtils::ToMessage(JsUtils::Convert(errCode))); + IMSA_HILOGE("InputMethodController::ShowTextInput failed, errCode: %{public}d!", errCode); + } +} + +void InputMethodControllerImpl::ShowTextInputSync() +{ + ShowTextInputHasParam(RequestKeyboardReason_t::key_t::NONE); +} + +void InputMethodControllerImpl::HideTextInputSync() +{ + int32_t errCode = InputMethodController::GetInstance()->HideTextInput(); + if (errCode != ErrorCode::NO_ERROR) { + set_business_error(JsUtils::Convert(errCode), JsUtils::ToMessage(JsUtils::Convert(errCode))); + IMSA_HILOGE("InputMethodController::HideTextInput failed, errCode: %{public}d!", errCode); + } +} + +void InputMethodControllerImpl::AttachSync(bool showKeyboard, TextConfig_t const &textConfig) +{ + AttachWithReason(showKeyboard, textConfig, RequestKeyboardReason_t::key_t::NONE); +} + +void InputMethodControllerImpl::AttachWithReason(bool showKeyboard, TextConfig_t const &textConfig, + RequestKeyboardReason_t requestKeyboardReason) +{ + AttachOptions attachOptions; + attachOptions.isShowKeyboard = showKeyboard; + attachOptions.requestKeyboardReason = static_cast(requestKeyboardReason.get_value()); + TextConfig config; + config.inputAttribute.inputPattern = textConfig.inputAttribute.textInputType.get_value(); + config.inputAttribute.enterKeyType = textConfig.inputAttribute.enterKeyType.get_value(); + if (textConfig.cursorInfo.has_value()) { + config.cursorInfo.left = textConfig.cursorInfo.value().left; + config.cursorInfo.top = textConfig.cursorInfo.value().top; + config.cursorInfo.width = textConfig.cursorInfo.value().width; + config.cursorInfo.height = textConfig.cursorInfo.value().height; + } + if (textConfig.selection.has_value()) { + config.range.start = textConfig.selection.value().start; + config.range.end = textConfig.selection.value().end; + } + if (textConfig.windowId.has_value()) { + config.windowId = textConfig.windowId.value(); + } + + int32_t errCode = InputMethodController::GetInstance()->Attach(InputMethodTextChangedListener::GetInstance(), + attachOptions, config, ClientType::JS); + if (errCode != ErrorCode::NO_ERROR) { + set_business_error(JsUtils::Convert(errCode), JsUtils::ToMessage(JsUtils::Convert(errCode))); + IMSA_HILOGE("InputMethodController::Attach failed, errCode: %{public}d!", errCode); + } +} + +void InputMethodControllerImpl::DetachSync() +{ + int32_t errCode = InputMethodController::GetInstance()->Close(); + if (errCode != ErrorCode::NO_ERROR) { + set_business_error(JsUtils::Convert(errCode), JsUtils::ToMessage(JsUtils::Convert(errCode))); + IMSA_HILOGE("InputMethodController::Close failed, errCode: %{public}d!", errCode); + } +} + +void InputMethodControllerImpl::RegisterListener(std::string const &type, callbackType &&cb, uintptr_t opq) +{ + if (TEXT_EVENT_TYPE.find(type) != TEXT_EVENT_TYPE.end()) { + if (!InputMethodController::GetInstance()->WasAttached()) { + std::string message = JsUtils::ToMessage(IMFErrorCode::EXCEPTION_DETACHED) + "need to be attached first"; + set_business_error(IMFErrorCode::EXCEPTION_DETACHED, message); + IMSA_HILOGE("RegisterListener failed, need to be attached first type: %{public}s!", type.c_str()); + return; + } + } + std::lock_guard lock(mutex_); + ani_object callbackObj = reinterpret_cast(opq); + ani_ref callbackRef; + ani_env *env = taihe::get_env(); + if (env == nullptr || ANI_OK != env->GlobalReference_Create(callbackObj, &callbackRef)) { + IMSA_HILOGE("ani_env is nullptr or GlobalReference_Create failed, type: %{public}s!", type.c_str()); + return; + } + auto &cbVec = jsCbMap_[type]; + bool isDuplicate = + std::any_of(cbVec.begin(), cbVec.end(), [env, callbackRef](std::unique_ptr &obj) { + ani_boolean isEqual = false; + return (ANI_OK == env->Reference_StrictEquals(callbackRef, obj->ref, &isEqual)) && isEqual; + }); + if (isDuplicate) { + env->GlobalReference_Delete(callbackRef); + IMSA_HILOGI("callback already registered, type: %{public}s!", type.c_str()); + return; + } + cbVec.emplace_back(std::make_unique(cb, callbackRef)); + IMSA_HILOGI("register callback success, type: %{public}s!", type.c_str()); +} +void InputMethodControllerImpl::UnRegisterListener(std::string const &type, taihe::optional_view opq) +{ + std::lock_guard lock(mutex_); + const auto iter = jsCbMap_.find(type); + if (iter == jsCbMap_.end()) { + IMSA_HILOGE("methodName: %{public}s already unRegistered!", type.c_str()); + return; + } + + if (!opq.has_value()) { + for (auto &uniquePtr : iter->second) { + uniquePtr->Release(); + } + jsCbMap_.erase(iter); + IMSA_HILOGE("callback is nullptr!"); + return; + } + + ani_env *env = taihe::get_env(); + if (env == nullptr) { + IMSA_HILOGE("ani_env is nullptr!"); + return; + } + + GlobalRefGuard guard(env, reinterpret_cast(opq.value())); + if (!guard) { + IMSA_HILOGE("GlobalRefGuard is false!"); + return; + } + + const auto pred = [env, targetRef = guard.get()](std::unique_ptr &obj) { + ani_boolean is_equal = false; + return (ANI_OK == env->Reference_StrictEquals(targetRef, obj->ref, &is_equal)) && is_equal; + }; + auto &callbacks = iter->second; + const auto it = std::find_if(callbacks.begin(), callbacks.end(), pred); + if (it != callbacks.end()) { + it->get()->Release(); + callbacks.erase(it); + IMSA_HILOGI("UnRegisterListener one type:%{public}s", type.c_str()); + } + if (callbacks.empty()) { + jsCbMap_.erase(iter); + IMSA_HILOGI("UnRegisterListener callbacks is empty type:%{public}s", type.c_str()); + } +} + +void InputMethodControllerImpl::InsertTextCallback(const std::u16string &text) +{ + std::lock_guard lock(mutex_); + auto &cbVec = jsCbMap_["insertText"]; + for (auto &cb : cbVec) { + auto &func = std::get>(cb->callback); + taihe::string textStr = Str16ToStr8(text); + func(textStr); + } +} +void InputMethodControllerImpl::DeleteLeftCallback(int32_t length) +{ + std::lock_guard lock(mutex_); + auto &cbVec = jsCbMap_["deleteLeft"]; + for (auto &cb : cbVec) { + auto &func = std::get>(cb->callback); + func(length); + } +} +void InputMethodControllerImpl::DeleteRightCallback(int32_t length) +{ + std::lock_guard lock(mutex_); + auto &cbVec = jsCbMap_["deleteRight"]; + for (auto &cb : cbVec) { + auto &func = std::get>(cb->callback); + func(length); + } +} +void InputMethodControllerImpl::SendKeyboardStatusCallback(const KeyboardStatus &status) +{ + std::lock_guard lock(mutex_); + auto &cbVec = jsCbMap_["sendKeyboardStatus"]; + for (auto &cb : cbVec) { + auto &func = std::get>(cb->callback); + KeyboardStatus_t state = EnumConvert::ConvertKeyboardStatus(status); + func(state); + } +} +void InputMethodControllerImpl::SendFunctionKeyCallback(const FunctionKey &key) +{ + std::lock_guard lock(mutex_); + auto &cbVec = jsCbMap_["sendFunctionKey"]; + for (auto &cb : cbVec) { + auto &func = std::get>(cb->callback); + FunctionKey_t funcKey{ .enterKeyType = EnumConvert::ConvertEnterKeyType(key.GetEnterKeyType()) }; + func(funcKey); + } +} +void InputMethodControllerImpl::MoveCursorCallback(const Direction &direction) +{ + std::lock_guard lock(mutex_); + auto &cbVec = jsCbMap_["moveCursor"]; + for (auto &cb : cbVec) { + auto &func = std::get>(cb->callback); + Direction_t directionType = EnumConvert::ConvertDirection(direction); + func(directionType); + } +} +void InputMethodControllerImpl::HandleExtendActionCallback(int32_t action) +{ + std::lock_guard lock(mutex_); + auto &cbVec = jsCbMap_["handleExtendAction"]; + for (auto &cb : cbVec) { + auto &func = std::get>(cb->callback); + ExtendAction_t actionType = EnumConvert::ConvertExtendAction(action); + func(actionType); + } +} +std::u16string InputMethodControllerImpl::GetLeftTextOfCursorCallback(int32_t number) +{ + std::lock_guard lock(mutex_); + auto &cbVec = jsCbMap_["getLeftTextOfCursor"]; + for (auto &cb : cbVec) { + auto &func = std::get>(cb->callback); + taihe::string s = func(number); + return Str8ToStr16(std::string(s)); + } + return Str8ToStr16(std::string()); +} +std::u16string InputMethodControllerImpl::GetRightTextOfCursorCallback(int32_t number) +{ + std::lock_guard lock(mutex_); + auto &cbVec = jsCbMap_["getRightTextOfCursor"]; + for (auto &cb : cbVec) { + auto &func = std::get>(cb->callback); + taihe::string s = func(number); + return Str8ToStr16(std::string(s)); + } + return Str8ToStr16(std::string()); +} +int32_t InputMethodControllerImpl::GetTextIndexAtCursorCallback() +{ + std::lock_guard lock(mutex_); + auto &cbVec = jsCbMap_["getTextIndexAtCursor"]; + for (auto &cb : cbVec) { + auto &func = std::get>(cb->callback); + return func(); + } + return 0; +} + +void InputMethodControllerImpl::OnSelectByRange(int32_t start, int32_t end) +{ + std::lock_guard lock(mutex_); + auto &cbVec = jsCbMap_["selectByRange"]; + for (auto &cb : cbVec) { + auto &func = std::get>(cb->callback); + Range_t range{ .start = start, .end = end }; + func(range); + } +} +void InputMethodControllerImpl::OnSelectByMovement(int32_t direction) +{ + std::lock_guard lock(mutex_); + auto &cbVec = jsCbMap_["selectByMovement"]; + for (auto &cb : cbVec) { + auto &func = std::get>(cb->callback); + Movement_t movement{ .direction = EnumConvert::ConvertDirection(static_cast(direction)) }; + func(movement); + } +} +} // namespace MiscServices +} // namespace OHOS \ No newline at end of file diff --git a/frameworks/ets/taihe/inputMethod/src/input_method_event_listener.cpp b/frameworks/ets/taihe/inputMethod/src/input_method_event_listener.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e5ff11f1d2cd5569f572a6278fadb8ba9f90a06a --- /dev/null +++ b/frameworks/ets/taihe/inputMethod/src/input_method_event_listener.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "input_method_event_listener.h" + +#include "input_method_setting_impl.h" +namespace OHOS { +namespace MiscServices { +std::mutex InputMethodEventListener::listenerMutex_; +std::shared_ptr InputMethodEventListener::inputMethodListener_{ nullptr }; +std::shared_ptr InputMethodEventListener::GetInstance() +{ + if (inputMethodListener_ == nullptr) { + std::lock_guard lock(listenerMutex_); + if (inputMethodListener_ == nullptr) { + inputMethodListener_ = std::make_shared(); + } + } + return inputMethodListener_; +} +void InputMethodEventListener::OnImeChange(const Property &property, const SubProperty &subProperty) +{ + InputMethodSettingImpl::GetInstance().OnImeChangeCallback(property, subProperty); +} + +void InputMethodEventListener::OnImeShow(const ImeWindowInfo &info) +{ + InputMethodSettingImpl::GetInstance().OnImeShowCallback(info); +} + +void InputMethodEventListener::OnImeHide(const ImeWindowInfo &info) +{ + InputMethodSettingImpl::GetInstance().OnImeHideCallback(info); +} +} // namespace MiscServices +} // namespace OHOS \ No newline at end of file diff --git a/frameworks/ets/taihe/inputMethod/src/input_method_setting_impl.cpp b/frameworks/ets/taihe/inputMethod/src/input_method_setting_impl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bf8a5085a4a28e73091f10f1e21ad8446fcd92ea --- /dev/null +++ b/frameworks/ets/taihe/inputMethod/src/input_method_setting_impl.cpp @@ -0,0 +1,287 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "input_method_setting_impl.h" + +#include "ani_common.h" +#include "ime_event_monitor_manager_impl.h" +#include "input_method_controller.h" +#include "input_method_event_listener.h" +#include "js_utils.h" +#include "stdexcept" +#include "taihe/runtime.hpp" + +namespace OHOS { +namespace MiscServices { +using namespace taihe; + +InputMethodSettingImpl &InputMethodSettingImpl::GetInstance() +{ + static InputMethodSettingImpl instance; + return instance; +} + +array InputMethodSettingImpl::GetInputMethodsSync(bool enable) +{ + std::vector properties; + int32_t errCode = InputMethodController::GetInstance()->ListInputMethod(enable, properties); + if (errCode != ErrorCode::NO_ERROR) { + set_business_error(JsUtils::Convert(errCode), "failed to get input method!"); + IMSA_HILOGE("failed to get input method, errCode:%{public}d!", errCode); + return array(nullptr, 0); + } + std::vector vecProperty; + for (const auto &property : properties) { + vecProperty.push_back(PropertyConverter::ConvertProperty(property)); + } + return array(vecProperty); +} + +array InputMethodSettingImpl::ListCurrentInputMethodSubtypeSync() +{ + std::vector subProperties; + int32_t errCode = InputMethodController::GetInstance()->ListCurrentInputMethodSubtype(subProperties); + if (errCode != ErrorCode::NO_ERROR) { + set_business_error(JsUtils::Convert(errCode), JsUtils::ToMessage(JsUtils::Convert(errCode))); + IMSA_HILOGE("failed to get input method subtype, errCode:%{public}d!", errCode); + return array(nullptr, 0); + } + std::vector vecSubtype; + for (const auto &property : subProperties) { + vecSubtype.push_back(PropertyConverter::ConvertSubProperty(property)); + } + return array(vecSubtype); +} + +array InputMethodSettingImpl::ListInputMethodSubtypeSync( + InputMethodProperty_t const &inputMethodProperty) +{ + Property property{ .name = std::string(inputMethodProperty.name), .id = std::string(inputMethodProperty.id) }; + if (property.name.empty() || property.id.empty()) { + set_business_error(IMFErrorCode::EXCEPTION_PARAMCHECK, "name and id must be string and cannot empty"); + IMSA_HILOGE("Property name and id must be string and cannot empty"); + return array(nullptr, 0); + } + std::vector subProperties; + int32_t errCode = InputMethodController::GetInstance()->ListInputMethodSubtype(property, subProperties); + if (errCode != ErrorCode::NO_ERROR) { + set_business_error(JsUtils::Convert(errCode), JsUtils::ToMessage(JsUtils::Convert(errCode))); + IMSA_HILOGE("failed to get input method subtype, errCode:%{public}d!", errCode); + return array(nullptr, 0); + } + std::vector vecSubtype; + for (const auto &property : subProperties) { + vecSubtype.push_back(PropertyConverter::ConvertSubProperty(property)); + } + return array(vecSubtype); +} + +bool InputMethodSettingImpl::IsPanelShown(PanelInfo_t const &panelInfo) +{ + PanelInfo info; + if (panelInfo.flag.has_value()) { + info.panelFlag = static_cast(panelInfo.flag.value().get_value()); + } + info.panelType = static_cast(panelInfo.type.get_value()); + bool isShown = false; + int32_t errorCode = InputMethodController::GetInstance()->IsPanelShown(info, isShown); + if (errorCode != ErrorCode::NO_ERROR) { + set_business_error(JsUtils::Convert(errorCode), "failed to query is panel shown!"); + IMSA_HILOGE("failed to query is panel shown, errCode:%{public}d!", errorCode); + return false; + } + return isShown; +} + +array InputMethodSettingImpl::GetAllInputMethodsSync() +{ + std::vector properties; + int32_t errCode = InputMethodController::GetInstance()->ListInputMethod(properties); + if (errCode != ErrorCode::NO_ERROR) { + set_business_error(JsUtils::Convert(errCode), "failed to get input method!"); + IMSA_HILOGE("failed to get input method, errCode:%{public}d!", errCode); + return array(nullptr, 0); + } + std::vector vecProperty; + for (const auto &property : properties) { + vecProperty.push_back(PropertyConverter::ConvertProperty(property)); + } + return array(vecProperty); +} + +void InputMethodSettingImpl::RegisterImeEvent(std::string const &eventName, int32_t eventMask, callbackType &&f, + uintptr_t opq) +{ + auto ret = ImeEventMonitorManagerImpl::GetInstance().RegisterImeEventListener(eventMask, + InputMethodEventListener::GetInstance()); + if (ret == ErrorCode::NO_ERROR) { + RegisterListener(eventName, std::forward(f), opq); + } else { + HandleRegistrationError(eventName, ret); + } +} + +void InputMethodSettingImpl::UnregisterImeEvent(std::string const &eventName, int32_t eventMask, + optional_view opq) +{ + bool isUpdateFlag = false; + UnregisterListener(eventName, opq, isUpdateFlag); + + if (isUpdateFlag) { + auto ret = ImeEventMonitorManagerImpl::GetInstance().UnRegisterImeEventListener(eventMask, + InputMethodEventListener::GetInstance()); + IMSA_HILOGI("Updated event: %{public}s flag, ret: %{public}d", eventName.c_str(), ret); + } +} +void InputMethodSettingImpl::HandleRegistrationError(std::string const &eventName, int32_t errorCode) +{ + auto errCode = JsUtils::Convert(errorCode); + if (errCode == EXCEPTION_SYSTEM_PERMISSION) { + set_business_error(EXCEPTION_SYSTEM_PERMISSION, JsUtils::ToMessage(EXCEPTION_SYSTEM_PERMISSION)); + } + IMSA_HILOGE("Failed to register %{public}s, error: %{public}d", eventName.c_str(), errorCode); +} +void InputMethodSettingImpl::RegisterListener(std::string const &type, callbackType &&cb, uintptr_t opq) +{ + std::lock_guard lock(mutex_); + ani_object callbackObj = reinterpret_cast(opq); + ani_ref callbackRef; + ani_env *env = taihe::get_env(); + if (env == nullptr || ANI_OK != env->GlobalReference_Create(callbackObj, &callbackRef)) { + IMSA_HILOGE("Failed to register %{public}s", type.c_str()); + return; + } + auto &cbVec = jsCbMap_[type]; + bool isDuplicate = + std::any_of(cbVec.begin(), cbVec.end(), [env, callbackRef](std::unique_ptr &obj) { + ani_boolean isEqual = false; + return (ANI_OK == env->Reference_StrictEquals(callbackRef, obj->ref, &isEqual)) && isEqual; + }); + if (isDuplicate) { + env->GlobalReference_Delete(callbackRef); + IMSA_HILOGD("%{public}s is already registered", type.c_str()); + return; + } + cbVec.emplace_back(std::make_unique(cb, callbackRef)); + IMSA_HILOGI("Registered success type: %{public}s", type.c_str()); +} +void InputMethodSettingImpl::UnregisterListener(std::string const &type, optional_view opq, + bool &isUpdateFlag) +{ + std::lock_guard lock(mutex_); + const auto iter = jsCbMap_.find(type); + if (iter == jsCbMap_.end()) { + IMSA_HILOGE("%{public}s is not registered", type.c_str()); + return; + } + + if (!opq.has_value()) { + jsCbMap_.erase(iter); + isUpdateFlag = true; + return; + } + + ani_env *env = taihe::get_env(); + if (env == nullptr) { + IMSA_HILOGE("Failed to unregister %{public}s, env is nullptr", type.c_str()); + return; + } + + GlobalRefGuard guard(env, reinterpret_cast(opq.value())); + if (!guard) { + IMSA_HILOGE("Failed to unregister %{public}s, GlobalRefGuard is false!", type.c_str()); + return; + } + + const auto pred = [env, targetRef = guard.get()](std::unique_ptr &obj) { + ani_boolean is_equal = false; + return (ANI_OK == env->Reference_StrictEquals(targetRef, obj->ref, &is_equal)) && is_equal; + }; + auto &callbacks = iter->second; + const auto it = std::find_if(callbacks.begin(), callbacks.end(), pred); + if (it != callbacks.end()) { + callbacks.erase(it); + } + if (callbacks.empty()) { + jsCbMap_.erase(iter); + isUpdateFlag = true; + } +} + +void InputMethodSettingImpl::OnImeChangeCallback(const Property &property, const SubProperty &subProperty) +{ + std::lock_guard lock(mutex_); + auto &cbVec = jsCbMap_["imeChange"]; + for (auto &cb : cbVec) { + auto &func = + std::get>(cb->callback); + func(PropertyConverter::ConvertProperty(property), PropertyConverter::ConvertSubProperty(subProperty)); + } +} +void InputMethodSettingImpl::OnImeShowCallback(const ImeWindowInfo &info) +{ + if (info.panelInfo.panelType != PanelType::SOFT_KEYBOARD || + (info.panelInfo.panelFlag != FLG_FLOATING && info.panelInfo.panelFlag != FLG_FIXED)) { + return; + } + auto showingFlag = GetSoftKbShowingFlag(); + // FLG_FIXED->FLG_FLOATING in show + if (info.panelInfo.panelFlag == FLG_FLOATING && showingFlag == FLG_FIXED) { + InputWindowInfo windowInfo{ info.windowInfo.name, 0, 0, 0, 0 }; + OnPanelStatusChange("imeHide", windowInfo); + } + // FLG_FLOATING->FLG_FIXED in show/show FLG_FIXED/ rotating(resize) in FLG_FIXED show + if ((info.panelInfo.panelFlag == FLG_FIXED && showingFlag == FLG_FLOATING) || + (info.panelInfo.panelFlag == FLG_FIXED && showingFlag == FLG_CANDIDATE_COLUMN) || + (info.panelInfo.panelFlag == FLG_FIXED && showingFlag == FLG_FIXED)) { + OnPanelStatusChange("imeShow", info.windowInfo); + } + SetSoftKbShowingFlag(info.panelInfo.panelFlag); +} + +void InputMethodSettingImpl::OnImeHideCallback(const ImeWindowInfo &info) +{ + SetSoftKbShowingFlag(FLG_CANDIDATE_COLUMN); + if (info.panelInfo.panelType != PanelType::SOFT_KEYBOARD || info.panelInfo.panelFlag != PanelFlag::FLG_FIXED) { + return; + } + OnPanelStatusChange("imeHide", info.windowInfo); +} + +PanelFlag InputMethodSettingImpl::GetSoftKbShowingFlag() +{ + return softKbShowingFlag_; +} +void InputMethodSettingImpl::SetSoftKbShowingFlag(PanelFlag flag) +{ + softKbShowingFlag_ = flag; +} + +void InputMethodSettingImpl::OnPanelStatusChange(std::string const &type, const InputWindowInfo &info) +{ + std::lock_guard lock(mutex_); + auto &cbVec = jsCbMap_[type]; + for (auto &cb : cbVec) { + InputWindowInfo_t inputWindowInfo{ .name = info.name, + .left = info.left, + .top = info.top, + .width = static_cast(info.width), + .height = static_cast(info.height) }; + taihe::array arrInfo{ inputWindowInfo }; + auto &func = std::get)>>(cb->callback); + func(arrInfo); + } +} +} // namespace MiscServices +} // namespace OHOS \ No newline at end of file diff --git a/frameworks/ets/taihe/inputMethod/src/input_method_text_changed_listener.cpp b/frameworks/ets/taihe/inputMethod/src/input_method_text_changed_listener.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d59aeae38d1e44b9f6046213d915927448570ab6 --- /dev/null +++ b/frameworks/ets/taihe/inputMethod/src/input_method_text_changed_listener.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "input_method_text_changed_listener.h" + +#include "input_method_controller_impl.h" + +namespace OHOS { +namespace MiscServices { +std::mutex InputMethodTextChangedListener::listenerMutex_; +sptr InputMethodTextChangedListener::inputMethodListener_{ nullptr }; +sptr InputMethodTextChangedListener::GetInstance() +{ + if (inputMethodListener_ == nullptr) { + std::lock_guard lock(listenerMutex_); + if (inputMethodListener_ == nullptr) { + inputMethodListener_ = new (std::nothrow) InputMethodTextChangedListener(); + } + } + return inputMethodListener_; +} + +void InputMethodTextChangedListener::InsertText(const std::u16string &text) +{ + InputMethodControllerImpl::GetInstance()->InsertTextCallback(text); +} + +void InputMethodTextChangedListener::DeleteForward(int32_t length) +{ + InputMethodControllerImpl::GetInstance()->DeleteRightCallback(length); +} + +void InputMethodTextChangedListener::DeleteBackward(int32_t length) +{ + InputMethodControllerImpl::GetInstance()->DeleteLeftCallback(length); +} + +void InputMethodTextChangedListener::SendKeyboardStatus(const KeyboardStatus &status) +{ + InputMethodControllerImpl::GetInstance()->SendKeyboardStatusCallback(status); +} + +void InputMethodTextChangedListener::SendFunctionKey(const FunctionKey &functionKey) +{ + InputMethodControllerImpl::GetInstance()->SendFunctionKeyCallback(functionKey); +} + +void InputMethodTextChangedListener::MoveCursor(const Direction direction) +{ + InputMethodControllerImpl::GetInstance()->MoveCursorCallback(direction); +} + +void InputMethodTextChangedListener::HandleExtendAction(int32_t action) +{ + InputMethodControllerImpl::GetInstance()->HandleExtendActionCallback(action); +} + +std::u16string InputMethodTextChangedListener::GetLeftTextOfCursor(int32_t number) +{ + return InputMethodControllerImpl::GetInstance()->GetLeftTextOfCursorCallback(number); +} + +std::u16string InputMethodTextChangedListener::GetRightTextOfCursor(int32_t number) +{ + return InputMethodControllerImpl::GetInstance()->GetRightTextOfCursorCallback(number); +} + +int32_t InputMethodTextChangedListener::GetTextIndexAtCursor() +{ + return InputMethodControllerImpl::GetInstance()->GetTextIndexAtCursorCallback(); +} +} // namespace MiscServices +} // namespace OHOS \ No newline at end of file diff --git a/frameworks/ets/taihe/inputMethod/src/ohos.inputMethod.impl.cpp b/frameworks/ets/taihe/inputMethod/src/ohos.inputMethod.impl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a4f2c9ac33f918ed1177083bd31de10de23ecb34 --- /dev/null +++ b/frameworks/ets/taihe/inputMethod/src/ohos.inputMethod.impl.cpp @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ohos.inputMethod.impl.hpp" + +#include "ani_common.h" +#include "common_fun_ani.h" +#include "input_method_controller.h" +#include "input_method_controller_impl.h" +#include "input_method_setting_impl.h" +#include "js_utils.h" +#include "ohos.inputMethod.proj.hpp" +#include "stdexcept" +#include "taihe/runtime.hpp" + +using namespace taihe; +using namespace ohos::inputMethod; +using namespace ohos::InputMethodSubtype; +using namespace OHOS::MiscServices; +namespace { +InputMethodSetting GetSetting() +{ + // The parameters in the make_holder function should be of the same type + // as the parameters in the constructor of the actual implementation class. + return make_holder(); +} + +ohos::inputMethod::InputMethodController GetController() +{ + // The parameters in the make_holder function should be of the same type + // as the parameters in the constructor of the actual implementation class. + return make_holder(); +} + +InputMethodProperty GetDefaultInputMethod() +{ + InputMethodProperty inputMethodProperty{}; + std::shared_ptr property; + int32_t ret = OHOS::MiscServices::InputMethodController::GetInstance()->GetDefaultInputMethod(property); + if (ret != ErrorCode::NO_ERROR) { + taihe::set_business_error(JsUtils::Convert(ret), "failed to get default input method!"); + IMSA_HILOGE("failed to get default input method!"); + return inputMethodProperty; + } + inputMethodProperty = PropertyConverter::ConvertProperty(property); + return inputMethodProperty; +} + +InputMethodProperty GetCurrentInputMethod() +{ + InputMethodProperty inputMethodProperty{}; + std::shared_ptr property = + OHOS::MiscServices::InputMethodController::GetInstance()->GetCurrentInputMethod(); + if (property == nullptr) { + IMSA_HILOGE("current input method is nullptr!"); + return inputMethodProperty; + } + inputMethodProperty = PropertyConverter::ConvertProperty(property); + return inputMethodProperty; +} + +InputMethodSubtype GetCurrentInputMethodSubtype() +{ + InputMethodSubtype inputMethodSubtype{}; + std::shared_ptr subProperty = + OHOS::MiscServices::InputMethodController::GetInstance()->GetCurrentInputMethodSubtype(); + if (subProperty == nullptr) { + IMSA_HILOGE("current input method subtype is nullptr!"); + return inputMethodSubtype; + } + inputMethodSubtype = PropertyConverter::ConvertSubProperty(subProperty); + return inputMethodSubtype; +} + +uintptr_t GetSystemInputMethodConfigAbility() +{ + OHOS::AppExecFwk::ElementName elementName; + int32_t ret = OHOS::MiscServices::InputMethodController::GetInstance()->GetInputMethodConfig(elementName); + if (ret != ErrorCode::NO_ERROR) { + taihe::set_business_error(JsUtils::Convert(ret), "failed to get input method config ability!"); + IMSA_HILOGE("failed to get input method config ability!"); + return reinterpret_cast(nullptr); + } + ani_object obj = OHOS::AppExecFwk::CommonFunAni::ConvertElementName(taihe::get_env(), elementName); + return reinterpret_cast(obj); +} + +bool SwitchInputMethodWithTarget(InputMethodProperty const &target) +{ + std::string packageName(target.name); + std::string id(target.id); + if (packageName.empty() || id.empty()) { + taihe::set_business_error(IMFErrorCode::EXCEPTION_PARAMCHECK, "packageName and methodId is empty"); + IMSA_HILOGE("failed to switch input method, packageName or id is empty!"); + return false; + } + int32_t errCode = + OHOS::MiscServices::InputMethodController::GetInstance()->SwitchInputMethod(SwitchTrigger::CURRENT_IME, + packageName, ""); + if (errCode != ErrorCode::NO_ERROR) { + int32_t code = JsUtils::Convert(errCode); + std::string message = JsUtils::ToMessage(code); + taihe::set_business_error(code, message); + IMSA_HILOGE("failed to switch input method, code:%{public}d message: %{public}s", code, message.c_str()); + return false; + } + return true; +} + +void SwitchInputMethodSync(string_view bundleName, optional_view subtypeId) +{ + std::string id; + if (subtypeId.has_value()) { + id = subtypeId.value(); + } + int32_t errCode = + OHOS::MiscServices::InputMethodController::GetInstance()->SwitchInputMethod(SwitchTrigger::SYSTEM_APP, + std::string(bundleName), id); + if (errCode != ErrorCode::NO_ERROR) { + int32_t code = JsUtils::Convert(errCode); + std::string message = JsUtils::ToMessage(code); + taihe::set_business_error(code, message); + IMSA_HILOGE("failed to switch input method, code:%{public}d message: %{public}s", code, message.c_str()); + } +} + +bool SwitchCurrentInputMethodSubtypeSync(InputMethodSubtype const &target) +{ + std::string name(target.name); + std::string id(target.id); + int32_t errCode = + OHOS::MiscServices::InputMethodController::GetInstance()->SwitchInputMethod(SwitchTrigger::CURRENT_IME, name, + id); + if (errCode != ErrorCode::NO_ERROR) { + int32_t code = JsUtils::Convert(errCode); + std::string message = JsUtils::ToMessage(code); + taihe::set_business_error(code, message); + IMSA_HILOGE("failed to switch Current input method subtype, code:%{public}d message: %{public}s", code, + message.c_str()); + return false; + } + return true; +} +} // namespace + +TH_EXPORT_CPP_API_GetSetting(GetSetting); +TH_EXPORT_CPP_API_GetController(GetController); +TH_EXPORT_CPP_API_GetDefaultInputMethod(GetDefaultInputMethod); +TH_EXPORT_CPP_API_GetCurrentInputMethod(GetCurrentInputMethod); +TH_EXPORT_CPP_API_GetCurrentInputMethodSubtype(GetCurrentInputMethodSubtype); +TH_EXPORT_CPP_API_GetSystemInputMethodConfigAbility(GetSystemInputMethodConfigAbility); +TH_EXPORT_CPP_API_SwitchInputMethodWithTarget(SwitchInputMethodWithTarget); +TH_EXPORT_CPP_API_SwitchInputMethodSync(SwitchInputMethodSync); +TH_EXPORT_CPP_API_SwitchCurrentInputMethodSubtypeSync(SwitchCurrentInputMethodSubtypeSync);