diff --git a/frameworks/ets/taihe/inputMethod/BUILD.gn b/frameworks/ets/taihe/inputMethod/BUILD.gn index 6bc740450f5796665da47a358c9d171bfc629084..4f008280a5365ddae5f57c8492368c0db46ac8ed 100644 --- a/frameworks/ets/taihe/inputMethod/BUILD.gn +++ b/frameworks/ets/taihe/inputMethod/BUILD.gn @@ -24,10 +24,13 @@ copy_taihe_idl("copy_taihe") { sources = [ "idl/ohos.inputMethod.Panel.taihe", "idl/ohos.inputMethod.taihe", - "idl/ohos.InputMethodSubtype.taihe" + "idl/ohos.InputMethodSubtype.taihe", + "idl/ohos.inputMethodEngine.taihe", ] - external_deps = [] + external_deps = [ + "input:key_event_taihe" + ] } ohos_taihe("run_taihe") { @@ -40,6 +43,8 @@ ohos_taihe("run_taihe") { "$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_generated_file_path/src/ohos.inputMethodEngine.ani.cpp", + "$taihe_generated_file_path/src/ohos.inputMethodEngine.abi.c", ] } @@ -70,11 +75,19 @@ taihe_shared_library("inputmethod_taihe_native") { "src/input_method_event_listener.cpp", "src/input_method_text_changed_listener.cpp", "${inputmethod_path}/frameworks/js/napi/inputmethodclient/js_utils.cpp", + #"src/ani_callback_object.cpp", + #"src/ani_message_handler.cpp", + "src/input_method_ability_impl.cpp", + "src/input_method_keyboard_delegate_impl.cpp", + "src/input_method_panel_impl.cpp", + "src/input_method_panel_listener.cpp", + "src/ohos.inputMethodEngine.impl.cpp" ] deps = [ ":run_taihe", "${inputmethod_path}/frameworks/js/napi/common:inputmethod_js_common", "${inputmethod_path}/interfaces/inner_api/inputmethod_controller:inputmethod_client", + "${inputmethod_path}/interfaces/inner_api/inputmethod_ability:inputmethod_ability", ] external_deps = [ "ability_base:want", @@ -142,11 +155,30 @@ ohos_prebuilt_etc("inputmethod_subtype_etc") { deps = [ ":inputmethod_subtype_abc"] } +generate_static_abc("inputmethod_engine_abc") { + base_url = "$taihe_generated_file_path" + files = [ + "$taihe_generated_file_path/@ohos.inputMethodEngine.ets" + ] + is_boot_abc = "True" + device_dst_file = "/system/framework/inputmethod_engine_abc.abc" + dependencies = [ ":run_taihe" ] +} + +ohos_prebuilt_etc("inputmethod_engine_etc") { + source = "$target_out_dir/inputmethod_engine_abc.abc" + module_install_dir = "framework" + part_name = "$part_name" + subsystem_name = "$subsystem_name" + deps = [ ":inputmethod_engine_abc"] +} + group("inputmethod_taihe") { deps = [ ":inputmethod_taihe_native", ":inputmethod_etc", ":inputmethod_panel_etc", - ":inputmethod_subtype_etc" + ":inputmethod_subtype_etc", + ":inputmethod_engine_etc" ] } diff --git a/frameworks/ets/taihe/inputMethod/idl/ohos.inputMethodEngine.taihe b/frameworks/ets/taihe/inputMethod/idl/ohos.inputMethodEngine.taihe new file mode 100644 index 0000000000000000000000000000000000000000..203d1bb31fd473af8c530ee47d8242c79fa0b86a --- /dev/null +++ b/frameworks/ets/taihe/inputMethod/idl/ohos.inputMethodEngine.taihe @@ -0,0 +1,620 @@ +/* + * 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 BaseContext from 'application.BaseContext'; +import window from '@ohos.window'; +""") +@!sts_inject_into_module("import * as ohos_InputMethodSubtype from './@ohos.InputMethodSubtype';") +@!sts_inject_into_module("import * as ohos_multimodalInput_keyEvent from './@ohos.multimodalInput.keyEvent';") + +@!namespace("@ohos.inputMethodEngine", "inputMethodEngine") + +from ohos.InputMethodSubtype use InputMethodSubtype; +from ohos.multimodalInput.keyEvent use KeyEvent as InputKeyEvent; + +enum Direction: i32 { + CURSOR_UP = 1, + CURSOR_DOWN = 2, + CURSOR_LEFT = 3, + CURSOR_RIGHT = 4 +} + +struct Movement { + direction: Direction; +} + +struct Range { + start: i32; + end: i32; +} + +enum ImmersiveMode: i32 { + NONE_IMMERSIVE = 0, + IMMERSIVE = 1, + LIGHT_IMMERSIVE = 2, + DARK_IMMERSIVE = 3 +} + +enum CapitalizeMode: i32 { + NONE = 0, + SENTENCES = 1, + WORDS = 2, + CHARACTERS = 3 +} + +enum GradientMode: i32 { + NONE = 0, + LINEAR_GRADIENT = 1 +} + +enum FluidLightMode: i32 { + NONE = 0, + BACKGROUND_FLUID_LIGHT = 1 +} + +union CommandDataType { + type_Int: i32; + type_String: String; + type_Bool: bool; +} + +struct EditorAttribute { + @readonly inputPattern: i32; + @readonly enterKeyType: i32; + isTextPreviewSupported: bool; + @readonly bundleName: Optional; + @readonly immersiveMode: Optional; + @readonly windowId: Optional; + @readonly displayId: Optional; + @readonly placeholder: Optional; + @readonly abilityName: Optional; + @readonly capitalizeMode: Optional; + @readonly gradientMode: Optional; + @readonly fluidLightMode: Optional; +} + +struct WindowInfo { + rect: @sts_type("window.Rect") Opaque; + status: @sts_type("window.WindowStatusType") Opaque; +} + +enum PanelType: i32 { + SOFT_KEYBOARD = 0, + STATUS_BAR = 1 +} + +enum PanelFlag: i32 { + FLG_FIXED = 0, + FLG_FLOATING = 1, + FLAG_CANDIDATE = 2 +} + +enum SecurityMode: i32 { + BASIC = 0, + FULL = 1 +} + +enum ExtendAction: i32 { + SELECT_ALL = 0, + CUT = 3, + COPY = 4, + PASTE = 5 +} + +struct PanelInfo { + type: PanelType; + flag: Optional; +} + +@const +enum EnterKeyType: u32 { + ENTER_KEY_TYPE_UNSPECIFIED = 0, + ENTER_KEY_TYPE_GO = 2, + ENTER_KEY_TYPE_SEARCH = 3, + ENTER_KEY_TYPE_SEND = 4, + ENTER_KEY_TYPE_NEXT = 5, + ENTER_KEY_TYPE_DONE = 6, + ENTER_KEY_TYPE_PREVIOUS = 7, + ENTER_KEY_TYPE_NEWLINE = 8 +} + +@const +enum PatternType: i32 { + PATTERN_NULL = -1, + PATTERN_TEXT = 0, + PATTERN_NUMBER = 2, + PATTERN_PHONE = 3, + PATTERN_DATETIME = 4, + PATTERN_EMAIL = 5, + PATTERN_URI = 6, + PATTERN_PASSWORD = 7, + PATTERN_PASSWORD_NUMBER = 8, + PATTERN_PASSWORD_SCREEN_LOCK = 9, + PATTERN_USER_NAME = 10, + PATTERN_NEW_PASSWORD = 11, + PATTERN_NUMBER_DECIMAL = 12, + PATTERN_ONE_TIME_CODE = 13 +} + +@const +enum FlagType: u32 { + DISPLAY_MODE_PART = 0, + DISPLAY_MODE_FULL = 1, + FLAG_SINGLE_LINE = 1, + FLAG_SELECTING = 2 +} + +@const +enum OptionType: u32 { + OPTION_NONE = 0, + OPTION_MULTI_LINE = 1, + OPTION_AUTO_CAP_CHARACTERS = 2, + OPTION_AUTO_WORDS = 4, + OPTION_AUTO_CAP_SENTENCES = 8, + OPTION_NO_FULLSCREEN = 10, + OPTION_ASCII = 20, +} + +@const +enum CursorType: u32 { + CURSOR_UP = 1, + CURSOR_DOWN = 2, + CURSOR_LEFT = 3, + CURSOR_RIGHT = 4 +} + +@const +enum WindowType: u32 { + WINDOW_TYPE_INPUT_METHOD_FLOAT = 2105 +} + +struct PanelRect { + landscapeRect: @sts_type("window.Rect") Opaque; + portraitRect: @sts_type("window.Rect") Opaque; +} + +struct KeyboardArea { + top: i32; + bottom: i32; + left: i32; + right: i32; +} + +struct EnhancedPanelRect { + landscapeRect: Optional<@sts_type("window.Rect") Opaque>; + portraitRect: Optional<@sts_type("window.Rect") Opaque>; + landscapeAvoidY: Optional; + landscapeInputRegion: Optional>; + portraitAvoidY: Optional; + portraitInputRegion: Optional>; + fullScreenMode: Optional; +} + +struct KeyEventType { + @readonly keyCode: i32; + @readonly keyAction: i32; +} + +interface TextInputClient { + +} + +interface InputMethodEngine { + @!sts_inject_into_interface(""" + on(type: string, callback: (data1: Object, data2: Object) => void): void; + off(type: string, callback?: (data1: Object, data2: Object) => void): void; + """) + @!sts_inject_into_class(""" + on(type: string, callback: (data1: Object, data2: Object) => void) { + switch(type) { + case "inputStart": return this.onInputStart(callback as (data1: KeyboardController, data2: TextInputClient) => void, callback); + default: throw new Error(`Unknown type: ${type}`); + } + } + off(type: string, callback?: (data1: Object, data2: Object) => void) { + switch(type) { + case "inputStart": return this.offInputStart(callback); + default: throw new Error(`Unknown type: ${type}`); + } + } + """) + OnInputStart(callback: (kbController: KeyboardController, textInputClient: TextInputClient) => void, opq: Opaque); + OffInputStart(opq: Optional); + + @!sts_inject_into_interface(""" + on(type: string, callback: () => void): void; + off(type: string, callback?: () => void): void; + """) + @!sts_inject_into_class(""" + on(type: string, callback: () => void) { + switch(type) { + case "keyboardShow": + case "keyboardHide": return this.onKeyboard(type, callback, callback); + default: throw new Error(`Unknown type: ${type}`); + } + } + off(type: string, callback?: () => void) { + switch(type) { + case "keyboardShow": + case "keyboardHide": return this.offKeyboard(type, callback); + default: throw new Error(`Unknown type: ${type}`); + } + } + """) + OnKeyboard(type: String, callback: () => void, opq: Opaque); + OffKeyboard(type: String, opq: Optional); +} + +interface InputClient { + @gen_async("selectByMovement") + @gen_promise("selectByMovement") + SelectByMovementAsync(movement: Movement): void; + + @gen_async("selectByRange") + @gen_promise("selectByRange") + SelectByRangeAsync(range: Range): void; + + @gen_async("moveCursor") + @gen_promise("moveCursor") + MoveCursorAsync(direction: i32): void; + + @gen_async("getBackward") + @gen_promise("getBackward") + GetBackwardAsync(length: i32): String; + + @gen_async("getForward") + @gen_promise("getForward") + GetForwardAsync(length: i32): String; + + @gen_async("insertText") + @gen_promise("insertText") + InsertTextAsync(text: String): bool; + + @gen_async("deleteBackward") + @gen_promise("deleteBackward") + DeleteBackwardAsync(length: i32): bool; + + @gen_async("deleteForward") + @gen_promise("deleteForward") + DeleteForwardAsync(length: i32): bool; + + @gen_async("sendKeyFunction") + @gen_promise("sendKeyFunction") + SendKeyFunctionAsync(action: i32): bool; + + @gen_async("getTextIndexAtCursor") + @gen_promise("getTextIndexAtCursor") + GetTextIndexAtCursorAsync(): i32; + + @gen_async("getEditorAttribute") + @gen_promise("getEditorAttribute") + GetEditorAttributeAsync(): EditorAttribute; + + @gen_promise("sendMessage") + SendMessageAsync(msgId: String, msgParam: Optional<@typedarray Array>): void; + + RecvMessage(msgHandler: Optional): void; + + @gen_promise("setPreviewText") + SetPreviewTextAsync(text: String, range: Range): void; + + FinishTextPreviewSync(): void; + + @gen_promise("getCallingWindowInfo") + GetCallingWindowInfoAsync(): WindowInfo; + + @gen_promise("sendPrivateCommand") + SendPrivateCommandAsync(commandData: @record Map): void; + + @gen_promise("finishTextPreview") + FinishTextPreviewAsync(): void; + + SetPreviewTextSync(text: String, range: Range): void; + + @gen_async("createPanel") + @gen_promise("createPanel") + CreatePanelAsync(ctx: @sts_type("BaseContext") Opaque, info: PanelInfo): Panel; + + @gen_async("sendExtendAction") + @gen_promise("sendExtendAction") + SendExtendActionAsync(action: ExtendAction): void; + + SelectByMovementSync(movement: Movement): void; + SelectByRangeSync(range: Range): void; + MoveCursorSync(direction: i32): void; + GetBackwardSync(length: i32): String; + GetForwardSync(length: i32): String; + InsertTextSync(text: String): void; + DeleteBackwardSync(length: i32): void; + DeleteForwardSync(length: i32): void; + GetEditorAttributeSync(): EditorAttribute; + GetTextIndexAtCursorSync(): i32; +} + +interface KeyboardDelegate { + + @!sts_inject_into_interface(""" + on(type: string, callback: (data1: Object, data2: Object, data3: Object) => void): void; + off(type: string, callback?: (data1: Object, data2: Object, data3: Object) => void): void; + on(type: string, callback: (data1: Object, data2: Object, data3: Object, data4: Object) => void): void; + off(type: string, callback?: (data1: Object, data2: Object, data3: Object, data4: Object) => void): void; + """) + @!sts_inject_into_class(""" + on(type: string, callback: (data1: Object, data2: Object, data3: Object) => void) { + switch(type) { + case "cursorContextChange": return this.onCursorContextChange(callback as (data1: int, data2: int, data3: int) => void, callback); + default: throw new Error(`Unknown type: ${type}`); + } + } + off(type: string, callback?: () => void) { + switch(type) { + case "cursorContextChange": return this.offCursorContextChange(callback); + default: throw new Error(`Unknown type: ${type}`); + } + } + on(type: string, callback: (data1: Object, data2: Object, data3: Object, data4: Object) => void) { + switch(type) { + case "selectionChange": return this.onSelectionChange(callback as (data1: int, data2: int, data3: int, data4: int) => void, callback); + default: throw new Error(`Unknown type: ${type}`); + } + } + off(type: string, callback?: (data1: Object, data2: Object, data3: Object, data4: Object) => void) { + switch(type) { + case "selectionChange": return this.offSelectionChange(callback); + default: throw new Error(`Unknown type: ${type}`); + } + } + """) + OnCursorContextChange(callback: (x: i32, y: i32, height: i32) => void, opq: Opaque); + OffCursorContextChange(opq: Optional); + OnSelectionChange(callback: (oldBegin: i32, oldEnd: i32, newBegin: i32, newEnd: i32) => void, opq: Opaque); + OffSelectionChange(opq: Optional); + + @!sts_inject_into_interface(""" + on(type: string, callback: (data: object) => object): void; + off(type: string, callback?: (data: object) => object): void; + """) + @!sts_inject_into_class(""" + on(type: string, callback: object) { + switch(type) { + case "keyDown": + case "keyUp": return this.onKeyEventType(type, callback as (data: KeyEventType) => boolean, callback); + case "keyEvent": return this.onKeyEvent(callback as (data: ohos_multimodalInput_keyEvent.KeyEvent) => boolean, callback); + case "textChange": return this.onTextChange(callback as (data: string) => void, callback); + case "editorAttributeChanged": return this.onEditorAttributeChanged(callback as (data: EditorAttribute) => void, callback); + default: throw new Error(`Unknown type: ${type}`); + } + } + off(type: string, callback?: Object) { + switch(type) { + case "keyDown": + case "keyUp": return this.offKeyEventType(type, callback); + case "keyEvent": return this.offKeyEvent(callback); + case "textChange": return this.offTextChange(callback); + case "editorAttributeChanged": return this.offEditorAttributeChanged(callback); + default: throw new Error(`Unknown type: ${type}`); + } + } + """) + OnKeyEventType(type: String, callback: (event: KeyEventType) => bool, opq: Opaque); + OffKeyEventType(type: String, opq: Optional); + OnKeyEvent(callback: (event: InputKeyEvent) => bool, opq: Opaque); + OffKeyEvent(opq: Optional); + OnTextChange(callback: (text: String) => void, opq: Opaque); + OffTextChange(opq: Optional); + OnEditorAttributeChanged(callback: (attr: EditorAttribute) => void, opq: Opaque); + OffEditorAttributeChanged(opq: Optional); +} + +interface KeyboardController { + @gen_async("hide") + @gen_promise("hide") + HideAsync(): void; + + @gen_async("exitCurrentInputType") + @gen_promise("exitCurrentInputType") + ExitCurrentInputTypeAsync(): void; +} + +interface InputMethodAbility { + GetSecurityMode(): SecurityMode; + + @gen_async("destroyPanel") + @gen_promise("destroyPanel") + DestroyPanelAsync(panel: Panel): void; + + @!sts_inject_into_interface(""" + on(type: string, callback: () => void): void; + off(type: string, callback?: () => void): void; + """) + @!sts_inject_into_class(""" + on(type: string, callback: () => void) { + switch(type) { + case "keyboardShow": + case "keyboardHide": return this.onKeyboard(type, callback, callback); + case "inputStop": return this.onInputStop(callback as () => void, callback); + case "discardTypingText": return this.onDiscardTypingText(callback as () => void, callback); + default: throw new Error(`Unknown type: ${type}`); + } + } + off(type: string, callback?: () => void) { + switch(type) { + case "keyboardShow": + case "keyboardHide": return this.offKeyboard(type, callback); + case "inputStop": return this.offInputStop(callback); + case "discardTypingText": return this.offDiscardTypingText(callback); + default: throw new Error(`Unknown type: ${type}`); + } + } + """) + OnKeyboard(type: String, callback: () => void, opq: Opaque); + OffKeyboard(type: String, opq: Optional); + OnInputStop(callback: () => void, opq: Opaque); + OffInputStop(opq: Optional); + OnDiscardTypingText(callback: () => void, opq: Opaque); + OffDiscardTypingText(opq: Optional); + + @!sts_inject_into_interface(""" + on(type: string, callback: (data: Object) => void): void; + off(type: string, callback?: (data: Object) => void): void; + """) + @!sts_inject_into_class(""" + on(type: string, callback: (data: Object) => void) { + switch(type) { + case "privateCommand": return this.onPrivateCommand(callback as (data: Record) => void, callback); + case "setCallingWindow": return this.onSetCallingWindow(callback as (data: long) => void, callback); + case "callingDisplayDidChange": return this.onCallingDisplayDidChange(callback as (data: bigint) => void, callback); + case "securityModeChange": return this.onSecurityModeChange(callback as (data: SecurityMode) => void, callback); + case "setSubtype": return this.onSetSubtype(callback as (data: ohos_InputMethodSubtype.InputMethodSubtype) => void, callback); + default: throw new Error(`Unknown type: ${type}`); + } + } + off(type: string, callback?: (data: Object) => void) { + switch(type) { + case "privateCommand": return this.offPrivateCommand(callback); + case "setCallingWindow": return this.offSetCallingWindow(callback); + case "callingDisplayDidChange": return this.offCallingDisplayDidChange(callback); + case "securityModeChange": return this.offSecurityModeChange(callback); + case "setSubtype": return this.offSetSubtype(callback); + default: throw new Error(`Unknown type: ${type}`); + } + } + """) + OnPrivateCommand(callback: (data: @record Map) => void, opq: Opaque); + OffPrivateCommand(opq: Optional); + OnSetCallingWindow(callback: (wid: i64) => void, opq: Opaque); + OffSetCallingWindow(opq: Optional); + OnCallingDisplayDidChange(callback: (callingDisplayId: @bigint Array) => void, opq: Opaque); + OffCallingDisplayDidChange(opq: Optional); + OnSecurityModeChange(callback: (mode: SecurityMode) => void, opq: Opaque); + OffSecurityModeChange(opq: Optional); + OnSetSubtype(callback: (inputMethodSubtype: InputMethodSubtype) => void, opq: Opaque); + OffSetSubtype(opq: Optional); + + @!sts_inject_into_interface(""" + on(type: string, callback: (data1: Object, data2: Object) => void): void; + off(type: string, callback?: (data1: Object, data2: Object) => void): void; + """) + @!sts_inject_into_class(""" + on(type: string, callback: (data1: Object, data2: Object) => void) { + switch(type) { + case "inputStart": return this.onInputStart(callback as (data1: KeyboardController, data2: InputClient) => void, callback); + default: throw new Error(`Unknown type: ${type}`); + } + } + off(type: string, callback?: (data1: Object, data2: Object) => void) { + switch(type) { + case "inputStart": return this.offInputStart(callback); + default: throw new Error(`Unknown type: ${type}`); + } + } + """) + OnInputStart(callback: (kbController: KeyboardController, inputClient: InputClient) => void, opq: Opaque); + OffInputStart(opq: Optional); +} + +interface Panel { + GetImplPtr(): i64; + + StartMoving(): void; + UpdateRegion(inputRegion: Array<@sts_type("window.Rect") Opaque>): void; + + @overload("adjustPanelRect") + AdjustPanelRect(flag: PanelFlag, rect: PanelRect): void; + @overload("adjustPanelRect") + AdjustPanelRectEnhanced(flag: PanelFlag, rect: EnhancedPanelRect): void; + + @gen_promise("getDisplayId") + GetDisplayIdSync(): u32; + + GetImmersiveMode(): ImmersiveMode; + SetImmersiveMode(mode: ImmersiveMode): void; + SetPrivacyMode(isPrivacyMode: bool): void; + ChangeFlag(flag: PanelFlag): void; + + @gen_async("moveTo") + @gen_promise("moveTo") + MoveToAsync(x: i32, y: i32): void; + + @gen_async("resize") + @gen_promise("resize") + ResizeAsync(width: u32, height: u32): void; + + @gen_async("setUiContent") + @gen_promise("setUiContent") + //@overload("setUiContentSync") + SetUiContentAsync(path: String): void; + + // @gen_async("setUiContent") + // @gen_promise("setUiContent") + // @overload("setUiContentSync") + // SetUiContentStorage(path: String, storage: @sts_type("LocalStorage") Opaque): void; + + @gen_async("hide") + @gen_promise("hide") + HideAsync(): void; + + @gen_async("show") + @gen_promise("show") + ShowAsync(): void; + + + @!sts_inject_into_interface(""" + on(type: string, callback: () => void): void; + off(type: string, callback?: () => void): void; + on(type: string, callback: (data1: Object, data2: Object) => void): void; + off(type: string, callback?: (data1: Object, data2: Object) => void): void; + on(type: string, callback: (data1: Object, data2?: Object) => void): void; + off(type: string, callback?: (data1: Object, data2?: Object) => void): void; + """) + @!sts_inject_into_class(""" + on(type: string, callback: Object) { + switch(type) { + case "show": return this.onShow(callback as () => void, callback); + case "hide": return this.onHide(callback as () => void, callback); + case "sizeChange": return this.onSizeChange(callback as (data1: window.Size, data2?: KeyboardArea) => void, callback); + case "sizeUpdate": return this.onSizeUpdate(callback as (data1: window.Size, data2: KeyboardArea) => void, callback); + default: throw new Error(`Unknown type: ${type}`); + } + } + off(type: string, callback?: Object) { + switch(type) { + case "show": return this.offShow(callback); + case "hide": return this.offHide(callback); + case "sizeChange": return this.offSizeChange(callback); + case "sizeUpdate": return this.offSizeUpdate(callback); + default: throw new Error(`Unknown type: ${type}`); + } + } + """) + OnShow(callback: () => void, opq: Opaque); + OffShow(opq: Optional); + OnHide(callback: () => void, opq: Opaque); + OffHide(opq: Optional); + OnSizeChange(callback: (size: @sts_type("window.Size") Opaque, keyboardArea: Optional) => void, opq: Opaque): void; + OffSizeChange(opq: Optional); + OnSizeUpdate(callback: (size: @sts_type("window.Size") Opaque, keyboardArea: KeyboardArea) => void, opq: Opaque): void; + OffSizeUpdate(opq: Optional); +} + +interface MessageHandler { + OnMessage(msgId: String, msgParam: Optional<@arraybuffer Array>): void; + OnTerminated(): void; +} + +function GetKeyboardDelegate(): KeyboardDelegate; + +function GetInputMethodAbility(): InputMethodAbility; \ No newline at end of file diff --git a/frameworks/ets/taihe/inputMethod/include/ani_callback_object.h b/frameworks/ets/taihe/inputMethod/include/ani_callback_object.h new file mode 100644 index 0000000000000000000000000000000000000000..406a36f125432bfbdf5549f62fa96705357c6417 --- /dev/null +++ b/frameworks/ets/taihe/inputMethod/include/ani_callback_object.h @@ -0,0 +1,56 @@ +/* + * 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 ANI_CALLBACK_OBJECT_H +#define ANI_CALLBACK_OBJECT_H + +#include +#include + +#include "ani.h" +#include "block_data.h" +#include "event_handler.h" + +namespace OHOS { +namespace MiscServices { +class AniCallbackObject { +public: + AniCallbackObject(ani_env* env, uintptr_t callback, std::thread::id threadId, + std::shared_ptr aniHandler); + ~AniCallbackObject(); + ani_ref callback_ = nullptr; + ani_env env_{}; + std::thread::id threadId_; + std::shared_ptr> isDone_; + std::shared_ptr aniHandler_; +}; + +// Ensure this object abstract in constract thread. +class AniMsgHandlerCallbackObject { +public: + AniMsgHandlerCallbackObject(ani_env* env, uintptr_t onTerminated, uintptr_t onMessage); + ~AniMsgHandlerCallbackObject(); + ani_env env_{}; + ani_ref onTerminatedCallback_ = nullptr; + ani_ref onMessageCallback_ = nullptr; + std::shared_ptr GetEventHandler(); + +private: + std::mutex eventHandlerMutex_; + std::shared_ptr handler_ = nullptr; + std::thread::id threadId_; +}; +} // namespace MiscServices +} // namespace OHOS +#endif // ANI_CALLBACK_OBJECT_H diff --git a/frameworks/ets/taihe/inputMethod/include/ani_common_engine.h b/frameworks/ets/taihe/inputMethod/include/ani_common_engine.h new file mode 100644 index 0000000000000000000000000000000000000000..20229983135c704ee7f8a273e4a0fe9cff50ea66 --- /dev/null +++ b/frameworks/ets/taihe/inputMethod/include/ani_common_engine.h @@ -0,0 +1,353 @@ +/* + * 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_ENGINE_H +#define INPUT_METHOD_TAIHE_ANI_COMMON_ENGINE_H + +#include "input_method_property.h" +#include "input_method_utils.h" +#include "ohos.inputMethod.impl.hpp" +#include "ohos.inputMethod.proj.hpp" +#include "ohos.inputMethodEngine.proj.hpp" +#include "ohos.inputMethodEngine.impl.hpp" +#include "taihe/runtime.hpp" +#include "ani.h" +#include "string_ex.h" +#include "panel_info.h" + +using InputMethodSubtype_t = ohos::InputMethodSubtype::InputMethodSubtype; +using EnhancedPanelRect_t = ohos::inputMethodEngine::EnhancedPanelRect; +using ImmersiveMode_t = ohos::inputMethodEngine::ImmersiveMode; +using KeyboardArea_t = ohos::inputMethodEngine::KeyboardArea; +using Movement_t = ohos::inputMethodEngine::Movement; +using Range_t = ohos::inputMethodEngine::Range; +using EditorAttribute_t = ohos::inputMethodEngine::EditorAttribute; +using CapitalizeMode_t = ohos::inputMethodEngine::CapitalizeMode; +using GradientMode_t = ohos::inputMethodEngine::GradientMode; +using FluidLightMode_t = ohos::inputMethodEngine::FluidLightMode; +using CommandDataType_t = ohos::inputMethodEngine::CommandDataType; +using SecurityMode_t = ohos::inputMethodEngine::SecurityMode; +using KeyEventType_t = ohos::inputMethodEngine::KeyEventType; +using KeyEvent_t = ohos::multimodalInput::keyEvent::KeyEvent; +using Action_t = ohos::multimodalInput::keyEvent::Action; +using InputEvent_t = ohos::multimodalInput::inputEvent::InputEvent; +using Key_t = ohos::multimodalInput::keyEvent::Key; +using KeyCode_t = ohos::multimodalInput::keyCode::KeyCode; +using PanelInfo_t = ohos::inputMethodEngine::PanelInfo; +using PanelFlag_t = ohos::inputMethodEngine::PanelFlag; +using PanelRect_t = ohos::inputMethodEngine::PanelRect; +using KeyboardController_t = ohos::inputMethodEngine::weak::KeyboardController; +using InputClient_t = ohos::inputMethodEngine::weak::InputClient; +using Panel_t =ohos::inputMethodEngine::weak::Panel; +using TextInputClient_t = ohos::inputMethodEngine::weak::TextInputClient; // 大概率废弃 +namespace OHOS { +namespace MiscServices { +using ValueMap = std::unordered_map; + +class TaiheConverter { +public: + static InputMethodSubtype_t ConvertSubProperty(const SubProperty &property) + { + return ConvertSubPropertyImpl(property); + } + +private: + static InputMethodSubtype_t ConvertSubPropertyImpl(const SubProperty &property) + { + InputMethodSubtype_t result {}; + result.name = property.name; + result.id = property.id; + result.locale = property.locale; + result.language = property.language; + result.label = taihe::optional(std::in_place_t{}, property.label); + result.labelId = taihe::optional(std::in_place_t{}, property.labelId); + result.icon = taihe::optional(std::in_place_t{}, property.icon); + result.iconId = taihe::optional(std::in_place_t{}, property.iconId); + return result; + } +}; + +using callbackTypes = 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)>, taihe::callback>; + +struct CallbackObjects { + CallbackObjects(callbackTypes cb, ani_ref ref) : callback(cb), ref(ref) + { + } + void Release() + { + taihe::env_guard guard; + if (auto *env = guard.get_env()) { + env->GlobalReference_Delete(ref); + } + } + callbackTypes callback; + ani_ref ref; +}; + +class GlobalRefGuards { + ani_env *env_ = nullptr; + ani_ref ref_ = nullptr; + +public: + GlobalRefGuards(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_; + } + ~GlobalRefGuards() + { + if (env_ && ref_) { + env_->GlobalReference_Delete(ref_); + } + } + + GlobalRefGuards(const GlobalRefGuards &) = delete; + GlobalRefGuards &operator=(const GlobalRefGuards &) = delete; +}; + +class CommonConvert { +public: + static constexpr int KEYEVENT_ACTION_JS_NATIVE_DELTA = 1; + static SecurityMode_t ConvertSecurityMode(SecurityMode mode) + { + switch (mode) { + case SecurityMode::BASIC: + return SecurityMode_t::key_t::BASIC; + case SecurityMode::FULL: + return SecurityMode_t::key_t::FULL; + default: + return SecurityMode_t::key_t::BASIC; + } + } + static ImmersiveMode_t ConvertMode(ImmersiveMode mode) + { + switch (mode) { + case ImmersiveMode::NONE_IMMERSIVE: + return ImmersiveMode_t::key_t::NONE_IMMERSIVE; + case ImmersiveMode::IMMERSIVE: + return ImmersiveMode_t::key_t::IMMERSIVE; + case ImmersiveMode::LIGHT_IMMERSIVE: + return ImmersiveMode_t::key_t::LIGHT_IMMERSIVE; + case ImmersiveMode::DARK_IMMERSIVE: + return ImmersiveMode_t::key_t::DARK_IMMERSIVE; + default: + return ImmersiveMode_t::key_t::NONE_IMMERSIVE; + } + } + static CapitalizeMode_t ConvertCapMode(CapitalizeMode mode) + { + switch (mode) { + case CapitalizeMode::NONE: + return CapitalizeMode_t::key_t::NONE; + case CapitalizeMode::SENTENCES: + return CapitalizeMode_t::key_t::SENTENCES; + case CapitalizeMode::WORDS: + return CapitalizeMode_t::key_t::WORDS; + case CapitalizeMode::CHARACTERS: + return CapitalizeMode_t::key_t::CHARACTERS; + default: + return CapitalizeMode_t::key_t::NONE; + } + } + + static GradientMode_t ConvertGraMode(GradientMode mode) + { + switch (mode) { + case GradientMode::NONE: + return GradientMode_t::key_t::NONE; + case GradientMode::LINEAR_GRADIENT: + return GradientMode_t::key_t::LINEAR_GRADIENT; + default: + return GradientMode_t::key_t::NONE; + } + } + + static FluidLightMode_t ConvertFLMode(FluidLightMode mode) + { + switch (mode) { + case FluidLightMode::NONE: + return FluidLightMode_t::key_t::NONE; + case FluidLightMode::BACKGROUND_FLUID_LIGHT: + return FluidLightMode_t::key_t::BACKGROUND_FLUID_LIGHT; + default: + return FluidLightMode_t::key_t::NONE; + } + } + + static EditorAttribute_t NativeAttributeToAni(InputAttribute inputAttribute) + { + EditorAttribute_t result {}; + result.inputPattern = inputAttribute.inputPattern; + result.enterKeyType = inputAttribute.enterKeyType; + result.isTextPreviewSupported = inputAttribute.isTextPreviewSupported; + result.bundleName = taihe::optional(std::in_place_t{}, inputAttribute.bundleName); + result.immersiveMode = taihe::optional(std::in_place_t{}, ConvertMode(static_cast(inputAttribute.immersiveMode))); + result.windowId = taihe::optional(std::in_place_t{}, inputAttribute.windowId); + result.displayId = taihe::optional(std::in_place_t{}, inputAttribute.callingDisplayId); + result.placeholder = taihe::optional(std::in_place_t{}, std::string(Str16ToStr8(inputAttribute.placeholder))); + result.abilityName = taihe::optional(std::in_place_t{}, std::string(Str16ToStr8(inputAttribute.abilityName))); + result.capitalizeMode = taihe::optional(std::in_place_t{}, ConvertCapMode(static_cast(inputAttribute.capitalizeMode))); + result.gradientMode = taihe::optional(std::in_place_t{}, ConvertGraMode(static_cast(inputAttribute.gradientMode))); + result.fluidLightMode = taihe::optional(std::in_place_t{}, ConvertFLMode(static_cast(inputAttribute.fluidLightMode))); + return result; + } + + static ValueMap AniConvertPCommandToNative(taihe::map_view commandData) + { + ValueMap valueMap; + for (auto &item : commandData) { + if (item.first.empty()) { + continue; + } + PrivateDataValue value; + switch (item.second.get_tag()) { + case CommandDataType_t::tag_t::type_Int: + value = item.second.get_type_Int_ref(); + break; + case CommandDataType_t::tag_t::type_String: + value = std::string(item.second.get_type_String_ref()); + break; + case CommandDataType_t::tag_t::type_Bool: + value = item.second.get_type_Bool_ref(); + break; + default: + break; + } + + valueMap.insert(std::make_pair(std::string(item.first), value)); + } + return valueMap; + } + + static CommandDataType_t ConvertToDataType(const PrivateDataValue &value) + { + size_t idx = value.index(); + if (idx == static_cast(PrivateDataValueType::VALUE_TYPE_STRING)) { + auto stringValue = std::get_if(&value); + if (stringValue != nullptr) { + return CommandDataType_t::make_type_String(*stringValue); + } + } else if (idx == static_cast(PrivateDataValueType::VALUE_TYPE_BOOL)) { + auto boolValue = std::get_if(&value); + if (boolValue != nullptr) { + return CommandDataType_t::make_type_Bool(*boolValue); + } + } else if (idx == static_cast(PrivateDataValueType::VALUE_TYPE_NUMBER)) { + auto numberValue = std::get_if(&value); + if (numberValue != nullptr) { + return CommandDataType_t::make_type_Int(*numberValue); + } + } + return CommandDataType_t::make_type_Bool(true); + } + + static taihe::map NativeConvertPCommandToAni(const ValueMap &valueMap) + { + taihe::map result(valueMap.size()); + for (const auto &[key, value] : valueMap) { + result.emplace(key, ConvertToDataType(value)); + } + return result; + } + + static PanelInfo AniConvertPanelInfoToNative(PanelInfo_t panel) + { + PanelInfo info{}; + info.panelType = static_cast(panel.type.get_value()); + if (panel.flag.has_value()) { + info.panelFlag = static_cast(panel.flag.value().get_value()); + } + return info; + } + + static Key_t ToTaiheKey(const std::optional &in) + { + bool isNull = (in == std::nullopt); + int32_t keyCode = isNull ? OHOS::MMI::KeyEvent::KEYCODE_UNKNOWN : in->GetKeyCode(); + Key_t out = { + .code = KeyCode_t::from_value(keyCode), + .pressedTime = isNull ? 0 : in->GetDownTime(), + .deviceId = isNull ? 0 : in->GetDeviceId(), + }; + return out; + } + + static taihe::array ToTaiheKeys(const std::vector &in) + { + std::vector resultVec; + for (const auto &item : in) { + Key_t result = ToTaiheKey(item); + resultVec.emplace_back(result); + } + return taihe::array(resultVec); + } + + static InputEvent_t ToTaiheInputEvent(const std::shared_ptr &in) + { + auto key = in->GetKeyItem(); + bool isNull = (key == std::nullopt); + InputEvent_t out = { + .id = isNull ? 0 : in->GetKeyItem()->GetKeyCode(), + .deviceId = isNull ? 0 : in->GetKeyItem()->GetDeviceId(), + .actionTime = isNull ? 0 : in->GetKeyItem()->GetDownTime(), + .screenId = 0, + .windowId = 0, + }; + return out; + } + + static KeyEvent_t ToTaiheKeyEvent(const std::shared_ptr &in) + { + auto key = in->GetKeyItem(); + int32_t unicodeChar = (key == std::nullopt) ? 0 : static_cast(key->GetUnicode()); + KeyEvent_t out = { + .base = ToTaiheInputEvent(in), + .action = Action_t::from_value(in->GetKeyAction()), + .key = ToTaiheKey(key), + .unicodeChar = unicodeChar, + .keys = ToTaiheKeys(in->GetKeyItems()), + .ctrlKey = in->GetKeyItem(MMI::KeyEvent::KEYCODE_CTRL_RIGHT).has_value(), + .altKey = in->GetKeyItem(MMI::KeyEvent::KEYCODE_ALT_RIGHT).has_value(), + .shiftKey = in->GetKeyItem(MMI::KeyEvent::KEYCODE_SHIFT_RIGHT).has_value(), + .logoKey = in->GetKeyItem(MMI::KeyEvent::KEYCODE_META_RIGHT).has_value(), + .fnKey = in->GetKeyItem(MMI::KeyEvent::KEYCODE_FN).has_value(), + .capsLock = in->GetFunctionKey(OHOS::MMI::KeyEvent::CAPS_LOCK_FUNCTION_KEY), + .numLock = in->GetFunctionKey(OHOS::MMI::KeyEvent::NUM_LOCK_FUNCTION_KEY), + .scrollLock = in->GetFunctionKey(OHOS::MMI::KeyEvent::SCROLL_LOCK_FUNCTION_KEY), + }; + return out; + } +}; +} // namespace MiscServices +} // namespace OHOS +#endif // INPUT_METHOD_TAIHE_ANI_COMMON_ENGINE_H \ No newline at end of file diff --git a/frameworks/ets/taihe/inputMethod/include/ani_message_handler.h b/frameworks/ets/taihe/inputMethod/include/ani_message_handler.h new file mode 100644 index 0000000000000000000000000000000000000000..907bbfdbf8acb0709a7b65461d6528a4c4a191b3 --- /dev/null +++ b/frameworks/ets/taihe/inputMethod/include/ani_message_handler.h @@ -0,0 +1,37 @@ +/* + * 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 ANI_MESSAGE_HANDLER_H +#define ANI_MESSAGE_HANDLER_H + +#include +#include "msg_handler_callback_interface.h" +#include "ani_callback_object.h" + +namespace OHOS { +namespace MiscServices { +class AniMessageHandler : public MsgHandlerCallbackInterface { +public: + explicit AniMessageHandler(ani_env* env, uintptr_t onTerminated, uintptr_t onMessage) + : AniMessageHandler_(std::make_shared(env, onTerminated, onMessage)) {}; + virtual ~AniMessageHandler() {}; + int32_t OnTerminated() override; + int32_t OnMessage(const ArrayBuffer &arrayBuffer) override; +private: + std::mutex callbackObjectMutex_; + std::shared_ptr aniMessageHandler_ = nullptr; +} +} // namespace MiscServices +} // namespace OHOS +#endif // ANI_MESSAGE_HANDLER_H \ No newline at end of file diff --git a/frameworks/ets/taihe/inputMethod/include/input_method_ability_impl.h b/frameworks/ets/taihe/inputMethod/include/input_method_ability_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..6eac1cb742e5b1fdc7da468366399b553cf8cd29 --- /dev/null +++ b/frameworks/ets/taihe/inputMethod/include/input_method_ability_impl.h @@ -0,0 +1,170 @@ +/* + * 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_ABILITY_IMPL_H +#define TAIHE_INPUT_METHOD_ABILITY_IMPL_H + +#include +#include +#include +#include +#include + +#include "ani_common_engine.h" +#include "ohos.inputMethodEngine.proj.hpp" +#include "ohos.inputMethodEngine.impl.hpp" +#include "input_method_engine_listener.h" + +namespace OHOS { +namespace MiscServices { +class InputMethodAbilityImpl : public InputMethodEngineListener { +public: + static std::shared_ptr GetInstance(); + SecurityMode_t GetSecurityMode(); + void DestroyPanelAsync(Panel_t panel); + void RegisterListener(std::string const &type, callbackTypes &&cb, uintptr_t opq); + void UnRegisterListener(std::string const &type, taihe::optional_view opq); + void OnKeyboardStatus(bool isShow) override; + void OnInputStart() override; + int32_t OnInputStop() override; + int32_t OnDiscardTypingText() override; + void OnSecurityChange(int32_t security) override; + void OnSetCallingWindow(uint32_t windowId) override; + void OnSetSubtype(const SubProperty &property) override; + void OnCallingDisplayIdChanged(uint64_t callingDisplayId) override; + void ReceivePrivateCommand(const std::unordered_map &privateCommand) override; + +private: + std::mutex mutex_; + std::map>> jsCbMap_; + static std::mutex engineMutex_; + static std::shared_ptr inputMethodEngine_; +}; + +class IMFAbilityImpl { +public: + IMFAbilityImpl() + { + } + SecurityMode_t GetSecurityMode() + { + return InputMethodAbilityImpl::GetInstance()->GetSecurityMode(); + } + + void DestroyPanelAsync(ohos::inputMethodEngine::weak::Panel panel) + { + InputMethodAbilityImpl::GetInstance()->DestroyPanelAsync(panel); + } + + void OnKeyboard(taihe::string_view type, taihe::callback_view callback, uintptr_t opq) + { + if (type == "keyboardShow") { + InputMethodAbilityImpl::GetInstance()->RegisterListener("keyboardShow", callback, opq); + } else { + InputMethodAbilityImpl::GetInstance()->RegisterListener("keyboardHide", callback, opq); + } + } + + void OffKeyboard(taihe::string_view type, taihe::optional_view opq) + { + if (type == "keyboardShow") { + InputMethodAbilityImpl::GetInstance()->UnRegisterListener("keyboardShow", opq); + } else { + InputMethodAbilityImpl::GetInstance()->UnRegisterListener("keyboardHide", opq); + } + } + + void OnInputStop(taihe::callback_view callback, uintptr_t opq) + { + InputMethodAbilityImpl::GetInstance()->RegisterListener("inputStop", callback, opq); + } + + void OffInputStop(taihe::optional_view opq) + { + InputMethodAbilityImpl::GetInstance()->UnRegisterListener("inputStop", opq); + } + + void OnDiscardTypingText(taihe::callback_view callback, uintptr_t opq) + { + InputMethodAbilityImpl::GetInstance()->RegisterListener("discardTypingText", callback, opq); + } + + void OffDiscardTypingText(taihe::optional_view opq) + { + InputMethodAbilityImpl::GetInstance()->UnRegisterListener("discardTypingText", opq); + } + + void OnPrivateCommand(taihe::callback_view)> callback, uintptr_t opq) + { + InputMethodAbilityImpl::GetInstance()->RegisterListener("privateCommand", callback, opq); + } + + void OffPrivateCommand(taihe::optional_view opq) + { + InputMethodAbilityImpl::GetInstance()->UnRegisterListener("privateCommand", opq); + } + + void OnSetCallingWindow(taihe::callback_view callback, uintptr_t opq) + { + InputMethodAbilityImpl::GetInstance()->RegisterListener("setCallingWindow", callback, opq); + } + + void OffSetCallingWindow(taihe::optional_view opq) + { + InputMethodAbilityImpl::GetInstance()->UnRegisterListener("setCallingWindow", opq); + } + + void OnCallingDisplayDidChange(taihe::callback_view)> callback, uintptr_t opq) + { + InputMethodAbilityImpl::GetInstance()->RegisterListener("callingDisplayDidChange", callback, opq); + } + + void OffCallingDisplayDidChange(taihe::optional_view opq) + { + InputMethodAbilityImpl::GetInstance()->UnRegisterListener("callingDisplayDidChange", opq); + } + + void OnSecurityModeChange(taihe::callback_view callback, uintptr_t opq) + { + InputMethodAbilityImpl::GetInstance()->RegisterListener("securityModeChange", callback, opq); + } + + void OffSecurityModeChange(taihe::optional_view opq) + { + InputMethodAbilityImpl::GetInstance()->UnRegisterListener("securityModeChange", opq); + } + + void OnSetSubtype(taihe::callback_view callback, uintptr_t opq) + { + InputMethodAbilityImpl::GetInstance()->RegisterListener("setSubtype", callback, opq); + } + + void OffSetSubtype(taihe::optional_view opq) + { + InputMethodAbilityImpl::GetInstance()->UnRegisterListener("setSubtype", opq); + } + + void OnInputStart(taihe::callback_view callback, uintptr_t opq) + { + InputMethodAbilityImpl::GetInstance()->RegisterListener("inputStart", callback, opq); + } + + void OffInputStart(taihe::optional_view opq) + { + InputMethodAbilityImpl::GetInstance()->UnRegisterListener("inputStart", opq); + } +}; +} // namespace MiscServices +} // namespace OHOS +#endif // TAIHE_INPUT_METHOD_ABILITY_IMPL_H \ No newline at end of file diff --git a/frameworks/ets/taihe/inputMethod/include/input_method_keyboard_delegate_impl.h b/frameworks/ets/taihe/inputMethod/include/input_method_keyboard_delegate_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..b0eef79e937572542b17ec20aa4bc2fd19dd0fd7 --- /dev/null +++ b/frameworks/ets/taihe/inputMethod/include/input_method_keyboard_delegate_impl.h @@ -0,0 +1,134 @@ +/* + * 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_KEYBOARD_DELEGATE_IMPL_H +#define TAIHE_INPUT_METHOD_KEYBOARD_DELEGATE_IMPL_H + +#include +#include +#include +#include +#include + +#include "ani_common_engine.h" +#include "ohos.inputMethodEngine.proj.hpp" +#include "ohos.inputMethodEngine.impl.hpp" +#include "key_event_consumer_proxy.h" +#include "keyboard_listener.h" + +namespace OHOS { +namespace MiscServices { +class KeyboardDelegateImpl : public KeyboardListener { +public: + static std::shared_ptr GetInstance(); + void RegisterListener(std::string const &type, callbackTypes &&cb, uintptr_t opq); + void UnRegisterListener(std::string const &type, taihe::optional_view opq); + bool OnKeyEvent(int32_t keyCode, int32_t keyStatus, sptr &consumer) override; + bool OnKeyEvent(const std::shared_ptr &keyEvent, sptr &consumer) override; + void OnCursorUpdate(int32_t positionX, int32_t positionY, int32_t height) override; + void OnSelectionChange(int32_t oldBegin, int32_t oldEnd, int32_t newBegin, int32_t newEnd) override; + void OnTextChange(const std::string &text) override; + void OnEditorAttributeChange(const InputAttribute &inputAttribute) override; + bool OnDealKeyEvent(const std::shared_ptr &keyEvent, sptr &consumer) override; + void OnKeyEventConsumeResult(bool isConsumed, sptr consumer); + void OnKeyCodeConsumeResult(bool isConsumed, sptr consumer); +private: + std::mutex mutex_; + std::map>> jsCbMap_; + static std::mutex keyboardMutex_; + static std::shared_ptr keyboardDelegate_; + + bool keyEventConsume_ = false; + bool keyCodeConsume_ = false; + bool keyEventResult_ = false; + bool keyCodeResult_ = false; +}; + +class IMFKeyboardDelegateImpl { + public: + IMFKeyboardDelegateImpl() + { + } + + void OnCursorContextChange(taihe::callback_view callback, uintptr_t opq) + { + KeyboardDelegateImpl::GetInstance()->RegisterListener("cursorContextChange", callback, opq); + } + + void OffCursorContextChange(taihe::optional_view opq) + { + KeyboardDelegateImpl::GetInstance()->UnRegisterListener("cursorContextChange", opq); + } + + void OnSelectionChange(taihe::callback_view callback, uintptr_t opq) + { + KeyboardDelegateImpl::GetInstance()->RegisterListener("selectionChange", callback, opq); + } + + void OffSelectionChange(taihe::optional_view opq) + { + KeyboardDelegateImpl::GetInstance()->UnRegisterListener("selectionChange", opq); + } + + void OnKeyEventType(taihe::string_view type, taihe::callback_view callback, uintptr_t opq) + { + if (type == "keyUp") { + KeyboardDelegateImpl::GetInstance()->RegisterListener("keyUp", callback, opq); + } else { + KeyboardDelegateImpl::GetInstance()->RegisterListener("keyDown", callback, opq); + } + } + + void OffKeyEventType(taihe::string_view type, taihe::optional_view opq) + { + if (type == "keyUp") { + KeyboardDelegateImpl::GetInstance()->UnRegisterListener("keyUp", opq); + } else { + KeyboardDelegateImpl::GetInstance()->UnRegisterListener("keyDown", opq); + } + } + + void OnKeyEvent(taihe::callback_view callback, uintptr_t opq) + { + KeyboardDelegateImpl::GetInstance()->RegisterListener("keyEvent", callback, opq); + } + + void OffKeyEvent(taihe::optional_view opq) + { + KeyboardDelegateImpl::GetInstance()-> UnRegisterListener("keyEvent", opq); + } + + void OnTextChange(taihe::callback_view callback, uintptr_t opq) + { + KeyboardDelegateImpl::GetInstance()->RegisterListener("textChange", callback, opq); + } + + void OffTextChange(taihe::optional_view opq) + { + KeyboardDelegateImpl::GetInstance()->UnRegisterListener("textChange", opq); + } + + void OnEditorAttributeChanged(taihe::callback_view callback, uintptr_t opq) + { + KeyboardDelegateImpl::GetInstance()->RegisterListener("editorAttributeChanged", callback, opq); + } + + void OffEditorAttributeChanged(taihe::optional_view opq) + { + KeyboardDelegateImpl::GetInstance()->UnRegisterListener("editorAttributeChanged", opq); + } +}; +} // namespace MiscServices +} // namespace OHOS +#endif // TAIHE_INPUT_METHOD_KEYBOARD_DELEGATE_IMPL_H \ No newline at end of file diff --git a/frameworks/ets/taihe/inputMethod/include/input_method_panel_impl.h b/frameworks/ets/taihe/inputMethod/include/input_method_panel_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..10c344230349dfa915bd61329d785bebfec9b893 --- /dev/null +++ b/frameworks/ets/taihe/inputMethod/include/input_method_panel_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_PANEL_IMPL_H +#define TAIHE_INPUT_METHOD_PANEL_IMPL_H + +#include +#include +#include +#include +#include + +#include "ani_common_engine.h" +#include "ohos.inputMethodEngine.proj.hpp" +#include "ohos.inputMethodEngine.impl.hpp" +#include "input_method_ability.h" + +namespace OHOS { +namespace MiscServices { +class PanelImpl { +public: + static std::shared_ptr GetInstance(); + ~PanelImpl(); + void CreatePanel(uintptr_t ctx, PanelInfo_t const& info, std::shared_ptr &panel); + void StartMoving(); + void UpdateRegion(taihe::array_view inputRegion); + void AdjustPanelRect(PanelFlag_t flag, PanelRect_t const& rect); + void AdjustPanelRectEnhanced(PanelFlag_t flag, EnhancedPanelRect_t const& rect); + uint32_t GetDisplayIdSync(); + ImmersiveMode_t GetImmersiveMode(); + void SetImmersiveMode(ImmersiveMode_t mode); + void SetPrivacyMode(bool isPrivacyMode); + void ChangeFlag(PanelFlag_t flag); + void MoveToAsync(int32_t x, int32_t y); + void ResizeAsync(uint32_t width, uint32_t height); + void SetUiContentAsync(taihe::string_view path); + void HideAsync(); + void ShowAsync(); + void RegisterListener(std::string const &type, callbackTypes &&cb, uintptr_t opq); + void UnRegisterListener(std::string const &type, taihe::optional_view opq); +private: + bool IsPanelFlagValid(PanelFlag panelFlag, bool isEnhancedCalled); + ImmersiveMode_t ConvertMode(ImmersiveMode mode); + bool IsVaildImmersiveMode(ImmersiveMode mode); + void SetNative(const std::shared_ptr &panel); + std::shared_ptr GetNative(); + static std::mutex panelMutex_; + static std::shared_ptr panel_; + static std::shared_ptr inputMethodPanel_; +}; + +class IMFPanelImpl { +public: + + int64_t GetImplPtr() + { + return reinterpret_cast(this); + } + + std::shared_ptr GetNativePtr() + { + return value_; + } + + IMFPanelImpl(uintptr_t ctx, PanelInfo_t const& info) + { + value_ = std::shared_ptr(); + PanelImpl::GetInstance()->CreatePanel(ctx, info, value_); + } + + void StartMoving() + { + PanelImpl::GetInstance()->StartMoving(); + } + + void UpdateRegion(taihe::array_view inputRegion) + { + PanelImpl::GetInstance()->UpdateRegion(inputRegion); + } + + void AdjustPanelRect(PanelFlag_t flag, PanelRect_t const& rect) + { + PanelImpl::GetInstance()->AdjustPanelRect(flag, rect); + } + + void AdjustPanelRectEnhanced(PanelFlag_t flag, EnhancedPanelRect_t const& rect) + { + PanelImpl::GetInstance()->AdjustPanelRectEnhanced(flag, rect); + } + + uint32_t GetDisplayIdSync() + { + return PanelImpl::GetInstance()->GetDisplayIdSync(); + } + + ImmersiveMode_t GetImmersiveMode() + { + return PanelImpl::GetInstance()->GetImmersiveMode(); + } + + void SetImmersiveMode(ImmersiveMode_t mode) + { + PanelImpl::GetInstance()->SetImmersiveMode(mode); + } + + void SetPrivacyMode(bool isPrivacyMode) + { + PanelImpl::GetInstance()->SetPrivacyMode(isPrivacyMode); + } + + void ChangeFlag(PanelFlag_t flag) + { + PanelImpl::GetInstance()->ChangeFlag(flag); + } + + void MoveToAsync(int32_t x, int32_t y) + { + PanelImpl::GetInstance()->MoveToAsync(x, y); + } + + void ResizeAsync(uint32_t width, uint32_t height) + { + PanelImpl::GetInstance()->ResizeAsync(width, height); + } + + void SetUiContentAsync(taihe::string_view path) + { + PanelImpl::GetInstance()->SetUiContentAsync(path); + } + + void HideAsync() + { + PanelImpl::GetInstance()->HideAsync(); + } + + void ShowAsync() + { + PanelImpl::GetInstance()->ShowAsync(); + } + + void OnShow(taihe::callback_view callback, uintptr_t opq) + { + PanelImpl::GetInstance()->RegisterListener("show", callback, opq); + } + + void OffShow(taihe::optional_view opq) + { + PanelImpl::GetInstance()->UnRegisterListener("show", opq); + } + + void OnHide(taihe::callback_view callback, uintptr_t opq) + { + PanelImpl::GetInstance()->RegisterListener("hide", callback, opq); + } + + void OffHide(taihe::optional_view opq) + { + PanelImpl::GetInstance()->UnRegisterListener("hide", opq); + } + + void OnSizeChange(taihe::callback_view)> callback, uintptr_t opq) + { + PanelImpl::GetInstance()->RegisterListener("sizeChange", callback, opq); + } + + void OffSizeChange(taihe::optional_view opq) + { + PanelImpl::GetInstance()->UnRegisterListener("sizeChange", opq); + } + + void OnSizeUpdate(taihe::callback_view callback, uintptr_t opq) + { + PanelImpl::GetInstance()->RegisterListener("sizeUpdate", callback, opq); + } + + void OffSizeUpdate(taihe::optional_view opq) + { + PanelImpl::GetInstance()->UnRegisterListener("sizeUpdate", opq); + } + +private: + std::shared_ptr value_; +}; +} // namespace MiscServices +} // namespace OHOS +#endif // TAIHE_INPUT_METHOD_PANEL_IMPL_H \ No newline at end of file diff --git a/frameworks/ets/taihe/inputMethod/include/input_method_panel_listener.h b/frameworks/ets/taihe/inputMethod/include/input_method_panel_listener.h new file mode 100644 index 0000000000000000000000000000000000000000..8a478014c0dc4bc2d4606250a42b2a874f512639 --- /dev/null +++ b/frameworks/ets/taihe/inputMethod/include/input_method_panel_listener.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2023 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 INPUTMETHOD_PANEL_LISTENER_H +#define INPUTMETHOD_PANEL_LISTENER_H + +#include +#include +#include +#include +#include + +#include "ani_common_engine.h" +#include "concurrent_map.h" +#include "panel_status_listener.h" +#include "panel_common.h" +#include "event_handler.h" +namespace OHOS { +namespace MiscServices { +class InputMethodPanelListener : public PanelStatusListener { +public: + static std::shared_ptr GetInstance(); + ~InputMethodPanelListener(); + void OnPanelStatus(uint32_t windowId, bool isShow) override; + void OnSizeChange(uint32_t windowId, const WindowSize &size) override; + void OnSizeChange(uint32_t windowId, const WindowSize &size, const PanelAdjustInfo &keyboardArea, + const std::string &event) override; + void Subscribe(uint32_t windowId, const std::string &type, callbackTypes &&cb, uintptr_t opq); + void RemoveInfo(uint32_t windowId, const std::string &type); + void SetEventHandler(std::shared_ptr handler); + +private: + std::unique_ptr GetCallback(uint32_t windowId, const std::string &type); + std::mutex mutex_; + ConcurrentMap>> jsCbMap_; + static std::mutex listenerMutex_; + static std::shared_ptr instance_; + mutable std::shared_mutex eventHandlerMutex_; + std::shared_ptr handler_; +}; +} // namespace MiscServices +} // namespace OHOS + +#endif //INPUTMETHOD_PANEL_LISTENER_H \ No newline at end of file diff --git a/frameworks/ets/taihe/inputMethod/src/ani_callback_object.cpp b/frameworks/ets/taihe/inputMethod/src/ani_callback_object.cpp new file mode 100644 index 0000000000000000000000000000000000000000..42ac5c292ccc0234057612db84e7689da8bb0f1c --- /dev/null +++ b/frameworks/ets/taihe/inputMethod/src/ani_callback_object.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2022 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 "ani_callback_object.h" +#include "global.h" + +namespace OHOS { +namespace MiscServices { +constexpr int32_t MAX_TIMEOUT = 2000; +AniCallbackObject::AniCallbackObject(ani_env* env, uintptr_t callback, std::thread::id threadId, + std::shared_ptr aniHandler) + : env_(env), threadId_(threadId), aniHandler_(aniHandler) +{ + ani_object callbackObj = reinterpret_cast(callback); + if (ANI_OK != env->GlobalReference_Create(callbackObj, &callback_)) { + IMSA_HILOGE(UDMF_ANI, "GlobalReference_Create callback_ failed."); + return; + } +} + +AniCallbackObject::~AniCallbackObject() +{ + if (callback_ != nullptr) { + if (threadId_ == std::this_thread::get_id()) { + env_->GlobalReference_Delete(env_, callback_); + env_ = nullptr; + return; + } + isDone_ = std::make_shared>(MAX_TIMEOUT, false); + std::string type = "~AniCallbackObject"; + auto eventHandler = aniHandler_; + if (eventHandler == nullptr) { + IMSA_HILOGE("eventHandler is nullptr!"); + return; + } + auto task = [env = env_, callback = callback_, isDone = isDone_]() { + env->GlobalReference_Delete(env, callback); + bool isFinish = true; + isDone->SetValue(isFinish); + }; + eventHandler->PostTask(task, type); + isDone_->GetValue(); + } + env_ = nullptr; +} + + +AniMsgHandlerCallbackObject::AniMsgHandlerCallbackObject(ani_env *env, uintptr_t onTerminated, uintptr_t onMessage) + : env_(env), handler_(AppExecFwk::EventHandler::Current()), threadId_(std::this_thread::get_id()) +{ + ani_object onTerminatedObj = reinterpret_cast(onTerminated); + ani_object onMessageObj = reinterpret_cast(onMessage); + if (ANI_OK != env->GlobalReference_Create(onTerminatedObj, &onTerminatedCallback_)) { + IMSA_HILOGE(UDMF_ANI, "GlobalReference_Create onTerminated failed."); + return; + } + if (ANI_OK != env->GlobalReference_Create(onMessageObj, &onMessageCallback_)) { + IMSA_HILOGE(UDMF_ANI, "GlobalReference_Create onMessage failed."); + return; + } +} + +AniMsgHandlerCallbackObject::~AniMsgHandlerCallbackObject() +{ + if (threadId_ == std::this_thread::get_id()) { + if (onTerminatedCallback_ != nullptr) { + env_->GlobalReference_Delete(env_, onTerminatedCallback_); + } + if (onMessageCallback_ != nullptr) { + env_->GlobalReference_Delete(env_, onMessageCallback_); + } + env_ = nullptr; + return; + } + IMSA_HILOGW("Thread id is not same, abstract destructor is run in muti-thread!"); + env_ = nullptr; +} + +std::shared_ptr AniMsgHandlerCallbackObject::GetEventHandler() +{ + std::lock_guard lock(eventHandlerMutex_); + return handler_; +} +} // namespace MiscServices +} // namespace OHOS diff --git a/frameworks/ets/taihe/inputMethod/src/ani_constructor.cpp b/frameworks/ets/taihe/inputMethod/src/ani_constructor.cpp index b3719ccb261cec82061ed4417fbe8d00ceca9069..11ae92c08eef75936740aced35517382945c3077 100644 --- a/frameworks/ets/taihe/inputMethod/src/ani_constructor.cpp +++ b/frameworks/ets/taihe/inputMethod/src/ani_constructor.cpp @@ -13,27 +13,42 @@ * limitations under the License. */ +#include "ohos.InputMethodSubtype.ani.hpp" #include "ohos.inputMethod.Panel.ani.hpp" +#include "ohos.multimodalInput.keyEvent.ani.hpp" #include "ohos.inputMethod.ani.hpp" -#include "ohos.InputMethodSubtype.ani.hpp" -ANI_EXPORT ani_status ANI_Constructor(ani_vm *vm, uint32_t *result) -{ +#include "ohos.multimodalInput.keyCode.ani.hpp" +#include "ohos.multimodalInput.inputEvent.ani.hpp" +#include "ohos.inputMethodEngine.ani.hpp" +#if __has_include() +#include +#elif __has_include() +#include +#else +#error "ani.h not found. Please ensure the Ani SDK is correctly installed." +#endif +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; - } + ani_status status = ANI_OK; if (ANI_OK != ohos::InputMethodSubtype::ANIRegister(env)) { std::cerr << "Error from ohos::InputMethodSubtype::ANIRegister" << std::endl; - return ANI_ERROR; + status = ANI_ERROR; } if (ANI_OK != ohos::inputMethod::Panel::ANIRegister(env)) { std::cerr << "Error from ohos::inputMethod::Panel::ANIRegister" << std::endl; - return ANI_ERROR; + status = ANI_ERROR; + } + if (ANI_OK != ohos::inputMethod::ANIRegister(env)) { + std::cerr << "Error from ohos::inputMethod::ANIRegister" << std::endl; + status = ANI_ERROR; + } + if (ANI_OK != ohos::inputMethodEngine::ANIRegister(env)) { + std::cerr << "Error from ohos::inputMethodEngine::ANIRegister" << std::endl; + status = ANI_ERROR; } *result = ANI_VERSION_1; - return ANI_OK; + return status; } diff --git a/frameworks/ets/taihe/inputMethod/src/ani_message_handler.cpp b/frameworks/ets/taihe/inputMethod/src/ani_message_handler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..245239617d5f13966a433eb9bc42ba21087657d8 --- /dev/null +++ b/frameworks/ets/taihe/inputMethod/src/ani_message_handler.cpp @@ -0,0 +1,51 @@ +/* + * 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 "ani_message_handler.h" + +namespace OHOS { +namespace MiscServices { +int32_t AniMessageHandler::OnTerminated() +{ + std::lock_guard lock(callbackObjectMutex_); + if (aniMessageHandler_ == nullptr) { + IMSA_HILOGI("aniMessageHandler_ is nullptr, can not call OnTerminated!."); + return ErrorCode::ERROR_NULL_POINTER; + } + auto func = aniMessageHandler_->onTerminatedCallback_; + std::shared_ptr>cacheCallback = std::reinterpret_pointer_cast>(func); + (*cacheCallback); + return ErrorCode::NO_ERROR; +} + +int32_t AniMessageHandler::OnMessage(const ArrayBuffer &arrayBuffer) +{ + std::lock_guard lock(callbackObjectMutex_); + if (aniMessageHandler_ == nullptr) { + IMSA_HILOGE("MessageHandler was not regist!."); + return ErrorCode::ERROR_MSG_HANDLER_NOT_REGIST; + } + if (!ArrayBuffer::IsSizeValid(arrayBuffer)) { + IMSA_HILOGE("msgId limit 256B and msgParam limit 128KB."); + return ErrorCode::ERROR_PARAMETER_CHECK_FAILED; + } + auto func = aniMessageHandler_->onMessageCallback_; + std::shared_ptr>)>>cacheCallback = std::reinterpret_pointer_cast>(func); + (*cacheCallback)(taihe::string_view(arrayBuffer.msgId), ::taihe::optional<::taihe::array>::make( + ::taihe::array(arrayBuffer.msgParam))); + return ErrorCode::NO_ERROR; +} +} // namespace MiscServices +} // namespace OHOS \ No newline at end of file diff --git a/frameworks/ets/taihe/inputMethod/src/input_method_ability_impl.cpp b/frameworks/ets/taihe/inputMethod/src/input_method_ability_impl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a4d344c4657cd4ac40da95386995125b393529b8 --- /dev/null +++ b/frameworks/ets/taihe/inputMethod/src/input_method_ability_impl.cpp @@ -0,0 +1,276 @@ +/* + * 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_ability_impl.h" +#include "input_method_panel_impl.h" +#include "js_utils.h" +#include "event_checker.h" + +namespace OHOS { +namespace MiscServices { +using namespace taihe; +std::mutex InputMethodAbilityImpl::engineMutex_; +std::shared_ptr InputMethodAbilityImpl::inputMethodEngine_ {nullptr}; +std::shared_ptr InputMethodAbilityImpl::GetInstance() +{ + if (inputMethodEngine_ == nullptr) { + std::lock_guard lock(engineMutex_); + if (inputMethodEngine_ == nullptr) { + auto engine = std::make_shared(); + if (engine == nullptr) { + IMSA_HILOGE("create engine failed!"); + return nullptr; + } + inputMethodEngine_ = engine; + } + } + return inputMethodEngine_; +} + +SecurityMode_t InputMethodAbilityImpl::GetSecurityMode() +{ + int32_t security = 0; + int32_t ret = InputMethodAbility::GetInstance().GetSecurityMode(security); + if (ret != ErrorCode::NO_ERROR) { + IMSA_HILOGE("failed to get security mode"); + taihe::set_business_error(JsUtils::Convert(ret), "failed to get security mode"); + return CommonConvert::ConvertSecurityMode(static_cast(security)); + } + IMSA_HILOGI("GetSecurityMode success!"); + return CommonConvert::ConvertSecurityMode(static_cast(security)); +} + +void InputMethodAbilityImpl::DestroyPanelAsync(Panel_t panel) +{ + // ohos::inputMethodEngine::weak::Panel -> panel + IMFPanelImpl* panelImpl = reinterpret_cast(panel->GetImplPtr()); + if (panelImpl == nullptr) { + IMSA_HILOGE("Failed to unwrap IMFPanelImpl"); + taihe::set_business_error(IMFErrorCode::EXCEPTION_PARAMCHECK, "Failed to unwrap IMFPanelImpl"); + return; + } + auto panelNative = panelImpl->GetNativePtr(); + if (panelNative == nullptr) { + IMSA_HILOGE("get panelNative failed"); + } + auto errCode = InputMethodAbility::GetInstance().DestroyPanel(panelNative); + if (errCode != ErrorCode::NO_ERROR) { + IMSA_HILOGE("DestroyPanel failed, errCode: %{public}d!", errCode); + taihe::set_business_error(JsUtils::Convert(errCode), "DestroyPanel failed"); + return; + } + IMSA_HILOGI("DestroyPanel success!"); +} + +void InputMethodAbilityImpl::RegisterListener(std::string const &type, callbackTypes &&cb, uintptr_t opq) +{ + if (!EventChecker::IsValidEventType(EventSubscribeModule::INPUT_METHOD_ABILITY, type)) { + IMSA_HILOGE("subscribe failed, type: %{public}s.", type.c_str()); + return; + } + if (type == "privateCommand" && !InputMethodAbility::GetInstance().IsDefaultIme()) { + taihe::set_business_error(JsUtils::Convert(ErrorCode::ERROR_NOT_DEFAULT_IME), "default ime check failed"); + return; + } +#ifndef SCENE_BOARD_ENABLE + if (type == "callingDisplayDidChange") { + taihe::set_business_error(JsUtils::Convert(ErrorCode::ERROR_DEVICE_UNSUPPORTED), "capability not supported."); + return; + } +#endif + 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 InputMethodAbilityImpl::UnRegisterListener(std::string const &type, taihe::optional_view opq) +{ + if (!EventChecker::IsValidEventType(EventSubscribeModule::INPUT_METHOD_ABILITY, type)) { + IMSA_HILOGE("subscribe failed, type: %{public}s.", type.c_str()); + return; + } + if (type == "privateCommand" && !InputMethodAbility::GetInstance().IsDefaultIme()) { + taihe::set_business_error(JsUtils::Convert(ErrorCode::ERROR_NOT_DEFAULT_IME), "default ime check failed"); + return; + } + 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); + return; + } + + ani_env *env = taihe::get_env(); + if (env == nullptr) { + IMSA_HILOGE("Failed to unregister %{public}s, env is nullptr", type.c_str()); + return; + } + + GlobalRefGuards 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); + } +} + +void InputMethodAbilityImpl::OnKeyboardStatus(bool isShow) +{ + std::string type = isShow ? "keyboardShow" : "keyboardHide"; + std::lock_guard lock(mutex_); + auto &cbVec = jsCbMap_[type]; + for (auto &cb : cbVec) { + auto &func = std::get>(cb->callback); + func(); + } +} + +void InputMethodAbilityImpl::OnInputStart() +{ + IMSA_HILOGI("OnInputStart start."); + std::lock_guard lock(mutex_); + auto &cbVec = jsCbMap_["inputStart"]; + for (auto &cb : cbVec) { + auto &func = std::get>(cb->callback); + // 获取kbController, inputClient + // func(); + } +} + +int32_t InputMethodAbilityImpl::OnInputStop() +{ + IMSA_HILOGI("OnInputStop start."); + std::lock_guard lock(mutex_); + auto &cbVec = jsCbMap_["inputStop"]; + for (auto &cb : cbVec) { + auto &func = std::get>(cb->callback); + func(); + } + return 0; +} + +int32_t InputMethodAbilityImpl::OnDiscardTypingText() +{ + IMSA_HILOGI("DiscardTypingText start."); + std::lock_guard lock(mutex_); + auto &cbVec = jsCbMap_["discardTypingText"]; + for (auto &cb : cbVec) { + auto &func = std::get>(cb->callback); + func(); + } + return 0; +} + +void InputMethodAbilityImpl::OnSecurityChange(int32_t security) +{ + IMSA_HILOGI("OnSecurityChange start."); + std::lock_guard lock(mutex_); + auto &cbVec = jsCbMap_["securityModeChange"]; + for (auto &cb : cbVec) { + auto &func = std::get>(cb->callback); + SecurityMode_t modeType = CommonConvert::ConvertSecurityMode(static_cast(security)); + func(modeType); + } +} + +void InputMethodAbilityImpl::OnSetCallingWindow(uint32_t windowId) +{ + IMSA_HILOGI("OnSetCallingWindow start."); + std::lock_guard lock(mutex_); + auto &cbVec = jsCbMap_["setCallingWindow"]; + for (auto &cb : cbVec) { + // uint32 int64_t ? + auto &func = std::get>(cb->callback); + int64_t wid = static_cast(windowId); + func(wid); + } +} + +void InputMethodAbilityImpl::OnSetSubtype(const SubProperty &property) +{ + IMSA_HILOGI("OnSetSubtype start."); + std::lock_guard lock(mutex_); + auto &cbVec = jsCbMap_["setSubtype"]; + for (auto &cb : cbVec) { + auto &func = std::get>(cb->callback); + func(TaiheConverter::ConvertSubProperty(property)); + } +} + +void InputMethodAbilityImpl::OnCallingDisplayIdChanged(uint64_t callingDisplayId) +{ + IMSA_HILOGI("OnCallingDisplayIdChanged start."); + if (callingDisplayId > UINT32_MAX) { + IMSA_HILOGE("callingDisplayId over range!"); + return; + } + std::lock_guard lock(mutex_); + auto &cbVec = jsCbMap_["callingDisplayDidChange"]; + for (auto &cb : cbVec) { + auto &func = std::get)>>(cb->callback); + // callingDisplayId-> array_view + std::vector displayId = {callingDisplayId}; + taihe::array_view displayIdView = taihe::array_view(displayId); + func(displayIdView); + } +} + +void InputMethodAbilityImpl::ReceivePrivateCommand(const std::unordered_map &privateCommand) +{ + IMSA_HILOGD("start."); + std::lock_guard lock(mutex_); + auto &cbVec = jsCbMap_["privateCommand"]; + for (auto &cb : cbVec) { + auto &func = std::get)>>(cb->callback); + func(CommonConvert::NativeConvertPCommandToAni(privateCommand)); + } +} + +} // namespace MiscServices +} // namespace OHOS \ No newline at end of file diff --git a/frameworks/ets/taihe/inputMethod/src/input_method_keyboard_delegate_impl.cpp b/frameworks/ets/taihe/inputMethod/src/input_method_keyboard_delegate_impl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5354f28716a7b40910eafda887de2cdebfd4cb0a --- /dev/null +++ b/frameworks/ets/taihe/inputMethod/src/input_method_keyboard_delegate_impl.cpp @@ -0,0 +1,269 @@ +/* + * 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_keyboard_delegate_impl.h" +#include "input_method_ability.h" +#include "event_checker.h" + +namespace OHOS { +namespace MiscServices { +constexpr size_t ARGC_TWO = 2; +using namespace taihe; +std::mutex KeyboardDelegateImpl::keyboardMutex_; +std::shared_ptr KeyboardDelegateImpl::keyboardDelegate_{ nullptr }; + +std::shared_ptr KeyboardDelegateImpl::GetInstance() +{ + if (keyboardDelegate_ == nullptr) { + std::lock_guard lock(keyboardMutex_); + if (keyboardDelegate_ == nullptr) { + auto delegate = std::make_shared(); + if (delegate == nullptr) { + IMSA_HILOGE("keyboard delegate is nullptr!"); + return nullptr; + } + keyboardDelegate_ = delegate; + } + } + return keyboardDelegate_; +} + +void KeyboardDelegateImpl::RegisterListener(std::string const &type, callbackTypes &&cb, uintptr_t opq) +{ + if (!EventChecker::IsValidEventType(EventSubscribeModule::KEYBOARD_DELEGATE, type)) { + IMSA_HILOGE("subscribe failed, 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("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 KeyboardDelegateImpl::UnRegisterListener(std::string const &type, taihe::optional_view opq) +{ + if (!EventChecker::IsValidEventType(EventSubscribeModule::KEYBOARD_DELEGATE, type)) { + IMSA_HILOGE("subscribe failed, type: %{public}s.", type.c_str()); + return; + } + 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); + return; + } + + ani_env *env = taihe::get_env(); + if (env == nullptr) { + IMSA_HILOGE("Failed to unregister %{public}s, env is nullptr", type.c_str()); + return; + } + + GlobalRefGuards 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); + } +} + +bool KeyboardDelegateImpl::OnKeyEvent(int32_t keyCode, int32_t keyStatus, sptr &consumer) +{ + std::string type = (keyStatus == ARGC_TWO ? "keyDown" : "keyUp"); + std::lock_guard lock(mutex_); + auto &cbVec = jsCbMap_[type]; + for (auto &cb : cbVec) { + auto &func = std::get>(cb->callback); + KeyEventType_t event { + .keyCode = keyCode, + .keyAction = keyStatus, + }; + bool isConsumed = false; + isConsumed = func(event); + if (consumer != nullptr) { + IMSA_HILOGE("consumer result: %{public}d!", isConsumed); + OnKeyCodeConsumeResult(isConsumed, consumer); + } + } + return true; +} + +void KeyboardDelegateImpl::OnKeyCodeConsumeResult(bool isConsumed, sptr consumer) +{ + IMSA_HILOGI("result: %{public}d.", isConsumed); + keyCodeConsume_ = true; + keyCodeResult_ = isConsumed; + if (keyEventConsume_) { + consumer->OnKeyEventResult(keyCodeResult_ || keyEventResult_); + keyCodeConsume_ = false; + keyCodeResult_ = false; + } +} + +void KeyboardDelegateImpl::OnKeyEventConsumeResult(bool isConsumed, sptr consumer) +{ + IMSA_HILOGI("result: %{public}d.", isConsumed); + keyEventConsume_ = true; + keyEventResult_ = isConsumed; + if (keyCodeConsume_) { + consumer->OnKeyEventResult(keyCodeResult_ || keyEventResult_); + keyEventConsume_ = false; + keyEventResult_ = false; + } +} + +bool KeyboardDelegateImpl::OnKeyEvent(const std::shared_ptr &keyEvent, sptr &consumer) +{ + std::string type = "keyEvent"; + std::lock_guard lock(mutex_); + auto &cbVec = jsCbMap_[type]; + for (auto &cb : cbVec) { + auto &func = std::get>(cb->callback); + // KeyEvent_t event; + bool isConsumed = func(CommonConvert::ToTaiheKeyEvent(keyEvent)); + if (consumer != nullptr) { + IMSA_HILOGE("consumer result: %{public}d!", isConsumed); + OnKeyEventConsumeResult(isConsumed, consumer); + } + } + return true; +} + +void KeyboardDelegateImpl::OnCursorUpdate(int32_t positionX, int32_t positionY, int32_t height) +{ + std::string type = "cursorContextChange"; + std::lock_guard lock(mutex_); + auto &cbVec = jsCbMap_[type]; + for (auto &cb : cbVec) { + auto &func = std::get>(cb->callback); + func(positionX, positionY, height); + } +} + +void KeyboardDelegateImpl::OnSelectionChange(int32_t oldBegin, int32_t oldEnd, int32_t newBegin, int32_t newEnd) +{ + std::string type = "selectionChange"; + std::lock_guard lock(mutex_); + auto &cbVec = jsCbMap_[type]; + for (auto &cb : cbVec) { + auto &func = std::get>(cb->callback); + func(oldBegin, oldEnd, newBegin, newEnd); + } +} + +void KeyboardDelegateImpl::OnTextChange(const std::string &text) +{ + std::string type = "textChange"; + std::lock_guard lock(mutex_); + auto &cbVec = jsCbMap_[type]; + for (auto &cb : cbVec) { + auto &func = std::get>(cb->callback); + func(taihe::string_view(text)); + } +} + +void KeyboardDelegateImpl::OnEditorAttributeChange(const InputAttribute &inputAttribute) +{ + std::string type = "editorAttributeChanged"; + std::lock_guard lock(mutex_); + auto &cbVec = jsCbMap_[type]; + for (auto &cb : cbVec) { + auto &func = std::get>(cb->callback); + func(CommonConvert::NativeAttributeToAni(inputAttribute)); + } +} + +bool KeyboardDelegateImpl::OnDealKeyEvent(const std::shared_ptr &keyEvent, sptr &consumer) +{ + // 正确? + if (keyEvent == nullptr) { + IMSA_HILOGE("keyEvent is nullptr"); + return false; + } + std::lock_guard lock(mutex_); + auto &cbEventVec = jsCbMap_["keyEvent"]; + for (auto &cb : cbEventVec) { + auto &func = std::get>(cb->callback); + // keyevent转换 + bool isKeyEventConsumed = func(CommonConvert::ToTaiheKeyEvent(keyEvent)); + if (consumer != nullptr) { + if (!isKeyEventConsumed) { + if (keyEvent != nullptr && keyEvent->GetKeyAction() == MMI::KeyEvent::KEY_ACTION_DOWN) { + IMSA_HILOGW("keyEvent is not consumed by ime"); + } + isKeyEventConsumed = InputMethodAbility::GetInstance().HandleUnconsumedKey(keyEvent); + } + IMSA_HILOGD("final consumed result: %{public}d.", isKeyEventConsumed); + consumer->OnKeyEventResult(isKeyEventConsumed); + } + } + std::string type = (keyEvent->GetKeyAction() == ARGC_TWO ? "keyDown" : "keyUp"); + auto &cbCodeVec = jsCbMap_[type]; + for (auto &cb : cbCodeVec) { + auto &func = std::get>(cb->callback); + KeyEventType_t event { + .keyCode = keyEvent->GetKeyCode(), + .keyAction = keyEvent->GetKeyAction(), + }; + bool isKeyCodeConsumed = false; + isKeyCodeConsumed = func(event); + if (consumer != nullptr) { + if (!isKeyCodeConsumed) { + if (keyEvent != nullptr && keyEvent->GetKeyAction() == MMI::KeyEvent::KEY_ACTION_DOWN) { + IMSA_HILOGW("keyEvent is not consumed by ime"); + } + isKeyCodeConsumed = InputMethodAbility::GetInstance().HandleUnconsumedKey(keyEvent); + } + IMSA_HILOGD("final consumed result: %{public}d.", isKeyCodeConsumed); + consumer->OnKeyEventResult(isKeyCodeConsumed); + } + } + return true; +} +} // namespace MiscServices +} // namespace OHOS \ No newline at end of file diff --git a/frameworks/ets/taihe/inputMethod/src/input_method_panel_impl.cpp b/frameworks/ets/taihe/inputMethod/src/input_method_panel_impl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ee73dcc7581e764133e729e451c3f87dddd7b55f --- /dev/null +++ b/frameworks/ets/taihe/inputMethod/src/input_method_panel_impl.cpp @@ -0,0 +1,438 @@ +/* + * 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_panel_impl.h" +#include "js_utils.h" +#include "input_method_panel_listener.h" +#include "event_checker.h" + +namespace OHOS { +namespace MiscServices { +using namespace taihe; +using WMError = OHOS::Rosen::WMError; +std::mutex PanelImpl::panelMutex_; +std::shared_ptr PanelImpl::panel_{ nullptr }; +std::shared_ptr PanelImpl::inputMethodPanel_{ nullptr }; +std::shared_ptr PanelImpl::GetInstance() +{ + std::shared_ptr panelImpl = InputMethodPanelListener::GetInstance(); + if (panelImpl != nullptr) { + IMSA_HILOGD("set eventHandler."); + panelImpl->SetEventHandler(AppExecFwk::EventHandler::Current()); + } + if (panel_ == nullptr) { + std::lock_guard lock(panelMutex_); + auto panel = std::make_shared(); + if (panel == nullptr) { + IMSA_HILOGE("create panel failed!"); + return nullptr; + } + panel_ = panel; + } + return panel_; +} + +PanelImpl::~PanelImpl() +{ + inputMethodPanel_ = nullptr; +} + +void PanelImpl::SetNative(const std::shared_ptr &panel) +{ + inputMethodPanel_ = panel; +} +std::shared_ptr PanelImpl::GetNative() +{ + return inputMethodPanel_; +} + +void PanelImpl::CreatePanel(uintptr_t ctx, PanelInfo_t const& info, std::shared_ptr &panel) +{ + // ctx info 转换 + std::shared_ptr context; + PanelInfo panelInfo = CommonConvert::AniConvertPanelInfoToNative(info); + auto ret = InputMethodAbility::GetInstance().CreatePanel(context, panelInfo, panel); + if (ret != ErrorCode::NO_ERROR) { + IMSA_HILOGE("panel create failed!"); + taihe::set_business_error(IMFErrorCode::EXCEPTION_IME, "panel create failed!"); + return; + } + if (panel != nullptr) { + inputMethodPanel_ = panel; + IMSA_HILOGI("panel create success!"); + } +} + +void PanelImpl::StartMoving() +{ + if (inputMethodPanel_ == nullptr) { + IMSA_HILOGE("panel is nullptr!"); + taihe::set_business_error(JsUtils::Convert(ErrorCode::ERROR_IME), "failed to start moving, panel is nullptr"); + return; + } + auto ret = inputMethodPanel_->StartMoving(); + if (ret != ErrorCode::NO_ERROR) { + taihe::set_business_error(JsUtils::Convert(ret), "failed to start moving"); + } +} + +bool PanelImpl::IsPanelFlagValid(PanelFlag panelFlag, bool isEnhancedCalled) +{ + bool isValid = false; + if (InputMethodAbility::GetInstance().IsDefaultIme()) { + isValid = panelFlag == FLG_FIXED || panelFlag == FLG_FLOATING || panelFlag == FLG_CANDIDATE_COLUMN; + } else { + isValid = panelFlag == FLG_FIXED || panelFlag == FLG_FLOATING; + } + IMSA_HILOGI("flag: %{public}d, isEnhanced: %{public}d, isValid: %{public}d", panelFlag, isEnhancedCalled, isValid); + if (!isEnhancedCalled && !isValid) { + IMSA_HILOGE("invalid panelFlag!"); + return false; + } + return true; +} + +void PanelImpl::UpdateRegion(taihe::array_view inputRegion) +{ + if (inputMethodPanel_ == nullptr) { + IMSA_HILOGE("panel is nullptr!"); + taihe::set_business_error(JsUtils::Convert(ErrorCode::ERROR_IME), "panel is nullptr!"); + return; + } + if (inputMethodPanel_->GetPanelType() != PanelType::SOFT_KEYBOARD) { + taihe::set_business_error(JsUtils::Convert(ErrorCode::ERROR_INVALID_PANEL_TYPE), + "only used for SOFT_KEYBOARD panel"); + return; + } + if (!IsPanelFlagValid(inputMethodPanel_->GetPanelFlag(), true)) { + IMSA_HILOGE("invalid panelFlag!"); + taihe::set_business_error(JsUtils::Convert(ErrorCode::ERROR_INVALID_PANEL_FLAG), + "param flag should be FIXED or FLOATING"); + return; + } + // inputRegion -> vector hotArea + std::vector hotArea; + int32_t code = inputMethodPanel_->UpdateRegion(hotArea); + if (code == ErrorCode::NO_ERROR) { + IMSA_HILOGI("UpdateRegion success!"); + return; + } else if (code == ErrorCode::ERROR_INVALID_PANEL_TYPE) { + IMSA_HILOGE("only used for SOFT_KEYBOARD panel!"); + taihe::set_business_error(JsUtils::Convert(code), "only used for SOFT_KEYBOARD panel!"); + return; + } else if (code == ErrorCode::ERROR_INVALID_PANEL_FLAG) { + IMSA_HILOGE("only used for fixed or floating panel!"); + taihe::set_business_error(JsUtils::Convert(code), "only used for fixed or floating panel!"); + return; + } + IMSA_HILOGE("UpdateRegion failed!"); + taihe::set_business_error(JsUtils::Convert(code), "UpdateRegion failed!"); + return; +} + +void PanelImpl::AdjustPanelRect(PanelFlag_t flag, PanelRect_t const& rect) +{ + if (inputMethodPanel_ == nullptr) { + IMSA_HILOGE("panel is nullptr!"); + taihe::set_business_error(JsUtils::Convert(ErrorCode::ERROR_IME), "panel is nullptr!"); + return; + } + // PanelFlag_t -> PanelFlag PanelRect_t -> LayoutParams + PanelFlag panelFlag = static_cast(flag.get_value()); + LayoutParams layoutParams; + int32_t ret = inputMethodPanel_->AdjustPanelRect(panelFlag, layoutParams); + if (ret == ErrorCode::NO_ERROR) { + IMSA_HILOGI("AdjustPanelRect success!"); + return; + } else if (ret == ErrorCode::ERROR_PARAMETER_CHECK_FAILED) { + IMSA_HILOGE("invalid param"); + taihe::set_business_error(JsUtils::Convert(ret), + "width limit:[0, displayWidth], height limit:[0, 70 percent of displayHeight]!"); + return; + } + IMSA_HILOGE("AdjustPanelRect failed!"); + taihe::set_business_error(JsUtils::Convert(ret), "AdjustPanelRect failed!"); +} + +void PanelImpl::AdjustPanelRectEnhanced(PanelFlag_t flag, EnhancedPanelRect_t const& rect) +{ + if (inputMethodPanel_ == nullptr) { + IMSA_HILOGE("panel is nullptr!"); + taihe::set_business_error(JsUtils::Convert(ErrorCode::ERROR_IME), "panel is nullptr!"); + return; + } + // PanelFlag_t -> PanelFlag EnhancedPanelRect_t -> EnhancedLayoutParams 、HotAreas + // portraitInputRegion -> hotAreas.portrait.keyboardHotArea 、 landscapeInputRegion -> hotAreas.landscape.keyboardHotArea + PanelFlag panelFlag = static_cast(flag.get_value()); + EnhancedLayoutParams enhancedLayoutParams; + HotAreas hotAreas; + int32_t ret = inputMethodPanel_->AdjustPanelRect(panelFlag, enhancedLayoutParams, hotAreas); + if (ret == ErrorCode::NO_ERROR) { + IMSA_HILOGI("AdjustPanelRect success!"); + return; + } else if (ret == ErrorCode::ERROR_PARAMETER_CHECK_FAILED) { + IMSA_HILOGE("invalid param"); + taihe::set_business_error(JsUtils::Convert(ret), + "width limit:[0, displayWidth], height limit:[0, 70 percent of displayHeight]!"); + return; + } else if (ret == ErrorCode::ERROR_INVALID_PANEL_TYPE) { + IMSA_HILOGE("only used for SOFT_KEYBOARD panel"); + taihe::set_business_error(JsUtils::Convert(ret), "only used for SOFT_KEYBOARD panel"); + return; + } +} + +uint32_t PanelImpl::GetDisplayIdSync() +{ + uint64_t displayId = 0; + if (inputMethodPanel_ == nullptr) { + IMSA_HILOGE("panel is nullptr!"); + taihe::set_business_error(JsUtils::Convert(ErrorCode::ERROR_IME), "panel is nullptr!"); + return static_cast(displayId); + } + auto ret = inputMethodPanel_->GetDisplayId(displayId); + if (ret != ErrorCode::NO_ERROR) { + IMSA_HILOGE("failed get displayId!"); + taihe::set_business_error(JsUtils::Convert(ret), "failed get displayId!"); + return static_cast(displayId); + } + if (displayId > UINT32_MAX) { + IMSA_HILOGE("displayId is too large, displayId: %{public}" PRIu64 "", displayId); + taihe::set_business_error(JsUtils::Convert(ErrorCode::ERROR_WINDOW_MANAGER), "displayId is too large"); + return static_cast(displayId); + } + IMSA_HILOGI("get displayId success!"); + return static_cast(displayId); +} + +ImmersiveMode_t PanelImpl::GetImmersiveMode() +{ + if (inputMethodPanel_ == nullptr) { + IMSA_HILOGE("panel is nullptr!"); + taihe::set_business_error(JsUtils::Convert(ErrorCode::ERROR_IME), "failed to get ImmersiveMode, panel is nullptr!"); + return CommonConvert::ConvertMode(ImmersiveMode::NONE_IMMERSIVE); + } + auto immersiveMode = inputMethodPanel_->GetImmersiveMode(); + IMSA_HILOGI("get Immersive mode success!"); + return CommonConvert::ConvertMode(immersiveMode); +} + +bool PanelImpl::IsVaildImmersiveMode(ImmersiveMode mode) +{ + if (mode == ImmersiveMode::NONE_IMMERSIVE || + mode == ImmersiveMode::LIGHT_IMMERSIVE || + mode == ImmersiveMode::DARK_IMMERSIVE) { + return true; + } + return false; +} + +void PanelImpl::SetImmersiveMode(ImmersiveMode_t mode) +{ + if (inputMethodPanel_ == nullptr) { + IMSA_HILOGE("panel is nullptr!"); + taihe::set_business_error(JsUtils::Convert(ErrorCode::ERROR_IME), "failed to set ImmersiveMode, panel is nullptr!"); + return; + } + ImmersiveMode immersiveMode = static_cast(mode.get_value()); + if (!IsVaildImmersiveMode(immersiveMode)) { + IMSA_HILOGE("immersiveMode type must be ImmersiveMode and can not be IMMERSIVE"); + taihe::set_business_error(IMFErrorCode::EXCEPTION_PARAMCHECK, + "immersiveMode type must be ImmersiveMode and can not be IMMERSIVE"); + return; + } + auto ret = inputMethodPanel_->SetImmersiveMode(immersiveMode); + if (ret != ErrorCode::NO_ERROR) { + IMSA_HILOGE("SetImmersiveMode failed!"); + taihe::set_business_error(JsUtils::Convert(ret), "SetImmersiveMode failed!"); + return; + } + IMSA_HILOGI("set Immersive mode success!"); +} + +void PanelImpl::SetPrivacyMode(bool isPrivacyMode) +{ + if (inputMethodPanel_ == nullptr) { + IMSA_HILOGE("panel is nullptr!"); + taihe::set_business_error(JsUtils::Convert(ErrorCode::ERROR_IME), "failed to set PrivacyMode, panel is nullptr!"); + return; + } + auto ret = inputMethodPanel_->SetPrivacyMode(isPrivacyMode); + if (ret == static_cast(WMError::WM_ERROR_INVALID_PERMISSION)) { + IMSA_HILOGE("permission denied"); + taihe::set_business_error(JsUtils::Convert(ErrorCode::ERROR_STATUS_PERMISSION_DENIED), + "ohos.permission.PRIVACY_WINDOW permission denied"); + return; + } else if (ret != ErrorCode::NO_ERROR) { + IMSA_HILOGE("failed to SetPrivacyMode!"); + return; + } + IMSA_HILOGI("set Privacy mode success!"); +} + +void PanelImpl::ChangeFlag(PanelFlag_t flag) +{ + if (inputMethodPanel_ == nullptr) { + IMSA_HILOGE("panel is nullptr!"); + taihe::set_business_error(JsUtils::Convert(ErrorCode::ERROR_IME), "failed to ChangeFlag, panel is nullptr!"); + return; + } + auto panelFlag = static_cast(flag.get_value()); + auto ret = inputMethodPanel_->ChangePanelFlag(panelFlag); + if (ret != ErrorCode::NO_ERROR) { + IMSA_HILOGE("failed to ChangePanelFlag!"); + return; + } + IMSA_HILOGI("change flag success!"); +} + +void PanelImpl::MoveToAsync(int32_t x, int32_t y) +{ + if (inputMethodPanel_ == nullptr) { + IMSA_HILOGE("panel is nullptr!"); + taihe::set_business_error(JsUtils::Convert(ErrorCode::ERROR_IME), "failed to MoveTo, panel is nullptr!"); + return; + } + auto code = inputMethodPanel_->MoveTo(x, y); + if (code == ErrorCode::ERROR_PARAMETER_CHECK_FAILED) { + IMSA_HILOGE("moveTo failed!"); + taihe::set_business_error(JsUtils::Convert(code), "failed to moveTo!"); + return; + } + IMSA_HILOGI("moveTo success!"); +} + +void PanelImpl::ResizeAsync(uint32_t width, uint32_t height) +{ + if (inputMethodPanel_ == nullptr) { + IMSA_HILOGE("panel is nullptr!"); + taihe::set_business_error(JsUtils::Convert(ErrorCode::ERROR_IME), "failed to Resize, panel is nullptr!"); + return; + } + auto code = inputMethodPanel_->Resize(width, height); + if (code != ErrorCode::NO_ERROR) { + IMSA_HILOGE("Resize failed!"); + taihe::set_business_error(JsUtils::Convert(code), "failed to Resize!"); + return; + } + IMSA_HILOGE("Resize success!"); +} + +void PanelImpl::SetUiContentAsync(taihe::string_view path) +{ + if (inputMethodPanel_ == nullptr) { + IMSA_HILOGE("panel is nullptr!"); + taihe::set_business_error(JsUtils::Convert(ErrorCode::ERROR_IME), "failed to SetUiContent, panel is nullptr!"); + return; + } + // env 类型有问题 lmq 方案 + // ani_env *env = taihe::get_env(); + // std::string contentInfo = std::string(path); + int32_t code = ErrorCode::NO_ERROR; + // auto code = inputMethodPanel_->SetUiContent(contentInfo, env, nullptr); + if (code == ErrorCode::ERROR_PARAMETER_CHECK_FAILED) { + IMSA_HILOGE("path should be a path to specific page."); + taihe::set_business_error(JsUtils::Convert(code), "path should be a path to specific page."); + return; + } else if (code != ErrorCode::NO_ERROR) { + IMSA_HILOGE("SetUiContent failed!"); + taihe::set_business_error(JsUtils::Convert(code), "SetUiContent failed!"); + return; + } + IMSA_HILOGI("SetUiContent success!"); +} + +void PanelImpl::HideAsync() +{ + if (inputMethodPanel_ == nullptr) { + IMSA_HILOGE("panel is nullptr!"); + taihe::set_business_error(JsUtils::Convert(ErrorCode::ERROR_IME), "failed to Hide, panel is nullptr!"); + return; + } + auto code = InputMethodAbility::GetInstance().HidePanel(inputMethodPanel_); + if (code != ErrorCode::NO_ERROR) { + IMSA_HILOGE("Hide failed!"); + taihe::set_business_error(JsUtils::Convert(code), "Hide failed!"); + return; + } + IMSA_HILOGI("Hide success!"); +} + +void PanelImpl::ShowAsync() +{ + if (inputMethodPanel_ == nullptr) { + IMSA_HILOGE("panel is nullptr!"); + taihe::set_business_error(JsUtils::Convert(ErrorCode::ERROR_IME), "failed to Show, panel is nullptr!"); + return; + } + auto code = InputMethodAbility::GetInstance().ShowPanel(inputMethodPanel_); + if (code != ErrorCode::NO_ERROR) { + IMSA_HILOGE("Show failed!"); + taihe::set_business_error(JsUtils::Convert(code), "Show failed!"); + return; + } + IMSA_HILOGI("Show success!"); +} + +void PanelImpl::RegisterListener(std::string const &type, callbackTypes &&cb, uintptr_t opq) +{ + IMSA_HILOGD("PanelImpl start."); + if (!EventChecker::IsValidEventType(EventSubscribeModule::PANEL, type)) { + IMSA_HILOGE("RegisterListener failed, type: %{public}s!", type.c_str()); + return; + } + IMSA_HILOGD("RegisterListener type: %{public}s.", type.c_str()); + if (type == "sizeUpdate") { + if (!InputMethodAbility::GetInstance().IsSystemApp()) { + taihe::set_business_error(IMFErrorCode::EXCEPTION_SYSTEM_PERMISSION, "not system application."); + return; + } + } + if (inputMethodPanel_ == nullptr) { + IMSA_HILOGE("inputMethodPanel is nullptr!"); + return; + } + std::shared_ptr observer = InputMethodPanelListener::GetInstance(); + observer->Subscribe(inputMethodPanel_->windowId_, type, std::forward(cb), opq); + bool ret = inputMethodPanel_->SetPanelStatusListener(observer, type); + if (!ret) { + IMSA_HILOGE("failed to subscribe %{public}s!", type.c_str()); + observer->RemoveInfo(inputMethodPanel_->windowId_, type); + return; + } +} + +void PanelImpl::UnRegisterListener(std::string const &type, taihe::optional_view opq) +{ + if (!EventChecker::IsValidEventType(EventSubscribeModule::PANEL, type)) { + IMSA_HILOGE("RegisterListener failed, type: %{public}s!", type.c_str()); + return; + } + IMSA_HILOGD("RegisterListener type: %{public}s.", type.c_str()); + if (type == "sizeUpdate") { + if (!InputMethodAbility::GetInstance().IsSystemApp()) { + taihe::set_business_error(IMFErrorCode::EXCEPTION_SYSTEM_PERMISSION, "not system application."); + return; + } + } + if (inputMethodPanel_ == nullptr) { + IMSA_HILOGE("inputMethodPanel is nullptr!"); + return; + } + std::shared_ptr observer = InputMethodPanelListener::GetInstance(); + observer->RemoveInfo(inputMethodPanel_->windowId_, type); + inputMethodPanel_->ClearPanelListener(type); +} +} // namespace MiscServices +} // namespace OHOS \ No newline at end of file diff --git a/frameworks/ets/taihe/inputMethod/src/input_method_panel_listener.cpp b/frameworks/ets/taihe/inputMethod/src/input_method_panel_listener.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0373b0a1eaec7cd945d9ad10a211c47cd74a803f --- /dev/null +++ b/frameworks/ets/taihe/inputMethod/src/input_method_panel_listener.cpp @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2023 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_panel_listener.h" + +namespace OHOS { +namespace MiscServices { +std::mutex InputMethodPanelListener::listenerMutex_; +std::shared_ptr InputMethodPanelListener::instance_{ nullptr }; + +std::shared_ptr InputMethodPanelListener::GetInstance() +{ + if (instance_ == nullptr) { + std::lock_guard lock(listenerMutex_); + if (instance_ == nullptr) { + instance_ = std::make_shared(); + } + } + return instance_; +} + +InputMethodPanelListener::~InputMethodPanelListener() {} + +void InputMethodPanelListener::Subscribe(uint32_t windowId, const std::string &type, callbackTypes &&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("ani_env is nullptr or GlobalReference_Create failed, type: %{public}s!", type.c_str()); + return; + } + // 1 : n : n -> 1: n(1: 1)[] + auto subscriptionAction = [&type, &env, &callbackRef, &cb](uint32_t windowId, + std::map> &cbs) -> bool { + if (cbs.find(type) != cbs.end()) { + env->GlobalReference_Delete(callbackRef); + IMSA_HILOGI("callback already registered, type: %{public}s!", type.c_str()); + return true; + } + auto result = cbs.try_emplace(type, std::make_unique(cb, callbackRef)); + bool inserted = result.second; + + if (inserted) { + IMSA_HILOGI("start to subscribe type: %{public}s of windowId: %{public}u.", + type.c_str(), windowId); + } else { + IMSA_HILOGD("type: %{public}s of windowId: %{public}u already subscribed.", + type.c_str(), windowId); + } + return !cbs.empty(); + }; + jsCbMap_.Compute(windowId, subscriptionAction); +} + +void InputMethodPanelListener::RemoveInfo(uint32_t windowId, const std::string &type) +{ + std::lock_guard lock(mutex_); + jsCbMap_.ComputeIfPresent(windowId, + [&type](auto windowId, std::map> &cbs) { + cbs.erase(type); + return !cbs.empty(); + }); +} + +std::unique_ptr InputMethodPanelListener::GetCallback(uint32_t windowId, const std::string &type) +{ + std::unique_ptr callBack = nullptr; + jsCbMap_.ComputeIfPresent(windowId, + [&type, &callBack](auto windowId, std::map> &cbs) { + auto it = cbs.find(type); + if (it == cbs.end()) { + return !cbs.empty(); + } + callBack = std::move(it->second); + return !cbs.empty(); + }); + return callBack; +} + +void InputMethodPanelListener::OnPanelStatus(uint32_t windowId, bool isShow) +{ + std::string type = isShow ? "show" : "hide"; + std::lock_guard lock(mutex_); + auto cbVec = GetCallback(windowId, type); + if (cbVec == nullptr) { + IMSA_HILOGE("callBack is nullptr!"); + return; + } + auto &func = std::get>(cbVec->callback); + func(); +} + +void InputMethodPanelListener::OnSizeChange(uint32_t windowId, const WindowSize &size) +{ + std::string type = "sizeChange"; + std::lock_guard lock(mutex_); + auto cbVec = GetCallback(windowId, type); + if (cbVec == nullptr) { + IMSA_HILOGE("callBack is nullptr!"); + return; + } + auto &func = std::get)>>(cbVec->callback); + // WindowSize -> uintptr_t 、PanelAdjustInfo -> KeyboardArea_t + //func(); +} + +void InputMethodPanelListener::OnSizeChange(uint32_t windowId, const WindowSize &size, + const PanelAdjustInfo &keyboardArea, const std::string &event) +{ + std::string type = "sizeUpdate"; + std::lock_guard lock(mutex_); + auto cbVec = GetCallback(windowId, type); + if (cbVec == nullptr) { + IMSA_HILOGE("callBack is nullptr!"); + return; + } + auto &func = std::get>(cbVec->callback); + // WindowSize -> uintptr_t 、PanelAdjustInfo -> KeyboardArea_t + // func(); +} + +void InputMethodPanelListener::SetEventHandler(std::shared_ptr handler) +{ + std::unique_lock lock(eventHandlerMutex_); + handler_ = handler; +} +} // namespace MiscServices +} // namespace OHOS \ No newline at end of file diff --git a/frameworks/ets/taihe/inputMethod/src/ohos.inputMethodEngine.impl.cpp b/frameworks/ets/taihe/inputMethod/src/ohos.inputMethodEngine.impl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a161a75a74310c72fe3098f0f93b32b856d2d207 --- /dev/null +++ b/frameworks/ets/taihe/inputMethod/src/ohos.inputMethodEngine.impl.cpp @@ -0,0 +1,455 @@ +#include "ohos.inputMethodEngine.proj.hpp" +#include "ohos.inputMethodEngine.impl.hpp" +#include "taihe/runtime.hpp" +#include "stdexcept" + +#include "ani_common_engine.h" +#include "panel_info.h" +#include "input_attribute.h" +#include "input_method_utils.h" +#include "input_method_ability.h" +#include "js_utils.h" +#include "input_method_panel_impl.h" +#include "input_method_ability_impl.h" +#include "input_method_keyboard_delegate_impl.h" + +using namespace OHOS::MiscServices; +namespace { +// To be implemented. + + + +class TextInputClientImpl { + public: + TextInputClientImpl() { + // Don't forget to implement the constructor. + } +}; + +class InputMethodEngineImpl { + public: + InputMethodEngineImpl() { + // Don't forget to implement the constructor. + } + + void OnInputStart(taihe::callback_view callback, uintptr_t opq) { + TH_THROW(std::runtime_error, "OnInputStart not implemented"); + } + + void OffInputStart(taihe::optional_view opq) { + TH_THROW(std::runtime_error, "OffInputStart not implemented"); + } + + void OnKeyboard(taihe::string_view type, taihe::callback_view callback, uintptr_t opq) { + TH_THROW(std::runtime_error, "OnKeyboard not implemented"); + } + + void OffKeyboard(taihe::string_view type, taihe::optional_view opq) { + TH_THROW(std::runtime_error, "OffKeyboard not implemented"); + } +}; + +class InputClientImpl { +public: + InputClientImpl() + { + } + + void SelectByMovementAsync(Movement_t const& movement) + { + } + + void SelectByRangeAsync(Range_t const& range) + { + + } + + void MoveCursorAsync(int32_t direction) + { + } + + taihe::string GetBackwardAsync(int32_t length) + { + taihe::string result = ""; + return result; + } + + taihe::string GetForwardAsync(int32_t length) + { + taihe::string result = ""; + return result; + } + + bool InsertTextAsync(taihe::string_view text) + { + return false; + } + + bool DeleteBackwardAsync(int32_t length) + { + return false; + } + + bool DeleteForwardAsync(int32_t length) + { + return false; + } + + bool SendKeyFunctionAsync(int32_t action) + { + return false; + } + + int32_t GetTextIndexAtCursorAsync() + { + return 0; + } + + EditorAttribute_t GetEditorAttributeAsync() + { + EditorAttribute_t result {}; + TextTotalConfig config; + int32_t ret = InputMethodAbility::GetInstance().GetTextConfig(config); + if (ret == ErrorCode::NO_ERROR) { + IMSA_HILOGD("inputPattern: %{public}d, enterKeyType: %{public}d, isTextPreviewSupported: %{public}d", + config.inputAttribute.inputPattern, config.inputAttribute.enterKeyType, + config.inputAttribute.isTextPreviewSupported); + } else { + IMSA_HILOGE("failed to get text config: %{public}d!", ret); + taihe::set_business_error(IMFErrorCode::EXCEPTION_IMCLIENT, "failed to get text config!"); + return result; + } + result = CommonConvert::NativeAttributeToAni(config.inputAttribute); + return result; + } + + void SendMessageAsync(taihe::string_view msgId, taihe::optional_view> msgParam) + { + ArrayBuffer arrayBuffer {}; + arrayBuffer.msgId = std::string(msgId); + arrayBuffer.jsArgc = 1; + if (msgParam.has_value()) { + arrayBuffer.jsArgc = 2; + arrayBuffer.msgParam.resize(msgParam.value().size()); + auto const &value = msgParam.value(); + for (size_t i = 0; i < value.size(); ++i) { + arrayBuffer.msgParam[i] = value[i]; + } + } + if (!ArrayBuffer::IsSizeValid(arrayBuffer)) { + IMSA_HILOGE("msgId limit 256B and msgParam limit 128KB."); + taihe::set_business_error(IMFErrorCode::EXCEPTION_PARAMCHECK, "msgId limit 256B and msgParam limit 128KB."); + return; + } + int32_t ret = InputMethodAbility::GetInstance().SendMessage(arrayBuffer); + if (ret != ErrorCode::NO_ERROR) { + IMSA_HILOGE("failed to get text config: %{public}d!", ret); + taihe::set_business_error(IMFErrorCode::EXCEPTION_IMCLIENT, "failed to get text config!"); + } + } + + void RecvMessage(taihe::optional_view<::ohos::inputMethodEngine::MessageHandler> msgHandler) + { + if (msgHandler.has_value()) { + IMSA_HILOGI("RecvMessage on."); + // 有问题 + // ani_env *env = taihe::get_env(); + // std::shared_ptr()>taiheCallbackOnTerminated = std::make_shared()>(msgHandler.OnTerminated); + // std::shared_ptr onTerminatedCB = std::reinterpret_pointer_cast(taiheCallbackOnTerminated); + // std::shared_ptr()>taiheCallbackOnMessage = std::make_shared()>(msgHandler.onMessage); + // std::shared_ptr onMessageCB = std::reinterpret_pointer_cast(taiheCallbackOnMessage); + // std::shared_ptr callback = std::make_shared(env, onTerminatedCB, onMessageCB); + //InputMethodAbility::GetInstance().RegisterMsgHandler(callback); + } else { + IMSA_HILOGI("RecvMessage off."); + InputMethodAbility::GetInstance().RegisterMsgHandler(); + } + } + + void SetPreviewTextAsync(taihe::string_view text, Range_t const& range) + { + } + + void FinishTextPreviewSync() + { + IMSA_HILOGD("Taihe TextInputClientEngine in"); + int32_t ret = InputMethodAbility::GetInstance().FinishTextPreview(); + if (ret != ErrorCode::NO_ERROR) { + taihe::set_business_error(JsUtils::Convert(ret), "failed to finish text preview!"); + } + } + + ::ohos::inputMethodEngine::WindowInfo GetCallingWindowInfoAsync() + { + ::ohos::inputMethodEngine::WindowInfo result {}; + CallingWindowInfo windowInfo {}; + int32_t ret = InputMethodAbility::GetInstance().GetCallingWindowInfo(windowInfo); + if (ret != ErrorCode::NO_ERROR) { + IMSA_HILOGI("GetCallingWindowInfo failed."); + taihe::set_business_error(JsUtils::Convert(ret), "GetCallingWindowInfo failed."); + return result; + } + // windowInfo -> ohos::inputMethodEngine::WindowInfo; + return result; + } + + void SendPrivateCommandAsync(taihe::map_view commandData) + { + ValueMap privateCommand = CommonConvert::AniConvertPCommandToNative(commandData); + if (!TextConfig::IsPrivateCommandValid(privateCommand)) { + IMSA_HILOGE("privateCommand invaild."); + taihe::set_business_error(IMFErrorCode::EXCEPTION_PARAMCHECK, "commandData size limit 32KB, count limit 5."); + return; + } + int32_t ret = InputMethodAbility::GetInstance().SendPrivateCommand(privateCommand); + if (ret != ErrorCode::NO_ERROR) { + IMSA_HILOGE("SendPrivateCommand failed."); + taihe::set_business_error(JsUtils::Convert(ret), "SendPrivateCommand failed."); + return; + } + IMSA_HILOGI("SendPrivateCommand success."); + } + + void FinishTextPreviewAsync() + { + } + + void SetPreviewTextSync(taihe::string_view text, Range_t const& range) + { + } + + ::ohos::inputMethodEngine::Panel CreatePanelAsync(uintptr_t ctx, ohos::inputMethodEngine::PanelInfo const& info) + { + // 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 taihe::make_holder(ctx, info); + } + + void SendExtendActionAsync(ohos::inputMethodEngine::ExtendAction action) + { + } + + void SelectByMovementSync(Movement_t const& movement) + { + IMSA_HILOGD("run in"); + int32_t direction = movement.direction.get_value(); + IMSA_HILOGD("direction: %{public}d.", direction); + int32_t ret = InputMethodAbility::GetInstance().SelectByMovement(direction); + if (ret != ErrorCode::NO_ERROR) { + IMSA_HILOGI("failed to select by movement!"); + taihe::set_business_error(JsUtils::Convert(ret), "failed to select by movement!"); + } + } + + void SelectByRangeSync(Range_t const& range) + { + IMSA_HILOGD("SelectByRangeSync"); + Range tmpRange {}; + tmpRange.start = range.start; + tmpRange.end = range.end; + int32_t ret = InputMethodAbility::GetInstance().SelectByRange(tmpRange.start, tmpRange.end); + if (ret != ErrorCode::NO_ERROR) { + IMSA_HILOGI("failed to select by range!"); + taihe::set_business_error(JsUtils::Convert(ret), "failed to select by range!"); + } + } + + void MoveCursorSync(int32_t direction) + { + if (direction < 0) { + IMSA_HILOGE("direction should be no less than 0!"); + taihe::set_business_error(JsUtils::Convert(ErrorCode::ERROR_PARAMETER_CHECK_FAILED), + "direction should be no less than 0!"); + return; + } + IMSA_HILOGD("moveCursor, direction: %{public}d", direction); + int32_t ret = InputMethodAbility::GetInstance().MoveCursor(direction); + if (ret != ErrorCode::NO_ERROR) { + IMSA_HILOGI("failed to move cursor!"); + taihe::set_business_error(JsUtils::Convert(ret), "failed to move cursor!"); + } + } + + taihe::string GetBackwardSync(int32_t length) + { + taihe::string result = ""; + if (length < 0) { + IMSA_HILOGE("length should be no less than 0!"); + taihe::set_business_error(JsUtils::Convert(ErrorCode::ERROR_PARAMETER_CHECK_FAILED), + "length should be no less than 0!"); + return result; + } + IMSA_HILOGD("get backward, length: %{public}d.", length); + std::u16string text; + int32_t ret = InputMethodAbility::GetInstance().GetTextAfterCursor(length, text); + if (ret != ErrorCode::NO_ERROR) { + IMSA_HILOGE("failed to get backward!"); + taihe::set_business_error(JsUtils::Convert(ret), "failed to get backward!"); + return result; + } + IMSA_HILOGI("get backward success!"); + result = std::string(OHOS::Str16ToStr8(text)); + return result; + } + + taihe::string GetForwardSync(int32_t length) + { + taihe::string result = ""; + if (length < 0) { + IMSA_HILOGE("length should be no less than 0!"); + taihe::set_business_error(JsUtils::Convert(ErrorCode::ERROR_PARAMETER_CHECK_FAILED), + "length should be no less than 0!"); + return result; + } + IMSA_HILOGD("get forward, length: %{public}d.", length); + std::u16string text; + int32_t ret = InputMethodAbility::GetInstance().GetTextBeforeCursor(length, text); + if (ret != ErrorCode::NO_ERROR) { + IMSA_HILOGE("failed to get forward!"); + taihe::set_business_error(JsUtils::Convert(ret), "failed to get forward!"); + return result; + } + IMSA_HILOGI("get forward success!"); + result = std::string(OHOS::Str16ToStr8(text)); + return result; + } + + void InsertTextSync(taihe::string_view text) + { + std::string tmpText = std::string(text); + int32_t ret = InputMethodAbility::GetInstance().InsertText(tmpText); + if (ret != ErrorCode::NO_ERROR) { + IMSA_HILOGE("failed to insert text!"); + taihe::set_business_error(JsUtils::Convert(ret), "failed to insert text!"); + } + IMSA_HILOGI("insert text success!"); + } + + void DeleteBackwardSync(int32_t length) + { + if (length < 0) { + IMSA_HILOGE("length should be no less than 0!"); + taihe::set_business_error(JsUtils::Convert(ErrorCode::ERROR_PARAMETER_CHECK_FAILED), + "length should be no less than 0!"); + return; + } + IMSA_HILOGD("delete backward, length: %{public}d.", length); + int32_t ret = InputMethodAbility::GetInstance().DeleteBackward(length); + if (ret != ErrorCode::NO_ERROR) { + IMSA_HILOGE("failed to delete backward"); + taihe::set_business_error(JsUtils::Convert(ret), "failed to delete backward"); + } + IMSA_HILOGI("delete backward success!"); + } + + void DeleteForwardSync(int32_t length) + { + if (length < 0) { + IMSA_HILOGE("length should be no less than 0!"); + taihe::set_business_error(JsUtils::Convert(ErrorCode::ERROR_PARAMETER_CHECK_FAILED), + "length should be no less than 0!"); + return; + } + IMSA_HILOGD("delete forward, length: %{public}d.", length); + int32_t ret = InputMethodAbility::GetInstance().DeleteForward(length); + if (ret != ErrorCode::NO_ERROR) { + IMSA_HILOGE("failed to delete forward"); + taihe::set_business_error(JsUtils::Convert(ret), "failed to delete forward"); + } + IMSA_HILOGI("delete forward success!"); + } + + EditorAttribute_t GetEditorAttributeSync() + { + TextTotalConfig config; + EditorAttribute_t result; + int32_t ret = InputMethodAbility::GetInstance().GetTextConfig(config); + if (ret != ErrorCode::NO_ERROR) { + IMSA_HILOGE("failed to get text config: %{public}d!", ret); + taihe::set_business_error(IMFErrorCode::EXCEPTION_IMCLIENT, "failed to get text config!"); + return result; + } + IMSA_HILOGD("inputPattern: %{public}d, enterKeyType: %{public}d, isTextPreviewSupported: %{public}d.", + config.inputAttribute.inputPattern, config.inputAttribute.enterKeyType, + config.inputAttribute.isTextPreviewSupported); + IMSA_HILOGI("get editor attribute success!"); + result = CommonConvert::NativeAttributeToAni(config.inputAttribute); + return result; + } + + int32_t GetTextIndexAtCursorSync() + { + IMSA_HILOGD("start."); + int32_t index = 0; + int32_t ret = InputMethodAbility::GetInstance().GetTextIndexAtCursor(index); + if (ret != ErrorCode::NO_ERROR) { + IMSA_HILOGE("failed to get text index at cursor: %{public}d!", ret); + taihe::set_business_error(JsUtils::Convert(ret), "failed to get text index at cursor!"); + return index; + } + IMSA_HILOGI("get text index at cursor success"); + return index; + } +}; + +class KeyboardControllerImpl { + public: + KeyboardControllerImpl() + { + // Don't forget to implement the constructor. + } + + void HideAsync() + { + int32_t ret = InputMethodAbility::GetInstance().HideKeyboardSelf(); + if (ret != ErrorCode::NO_ERROR) { + IMSA_HILOGE("failed to hide keyboard self: %{public}d!", ret); + taihe::set_business_error(JsUtils::Convert(ret), "failed to hide keyboard self!"); + return; + } + } + + void ExitCurrentInputTypeAsync() + { + int32_t ret = InputMethodAbility::GetInstance().ExitCurrentInputType(); + if (ret != ErrorCode::NO_ERROR) { + IMSA_HILOGE("failed to exit current input type: %{public}d!", ret); + taihe::set_business_error(JsUtils::Convert(ret), "failed to exit current input type!"); + return; + } + } +}; + +class MessageHandlerImpl { + public: + MessageHandlerImpl() + { + } + + void OnMessage(taihe::string_view msgId, taihe::optional_view> msgParam) + { + } + + void OnTerminated() + { + } +}; + +ohos::inputMethodEngine::KeyboardDelegate GetKeyboardDelegate() { + // 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 taihe::make_holder(); +} + +ohos::inputMethodEngine::InputMethodAbility GetInputMethodAbility() { + // 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 taihe::make_holder(); +} +} // namespace + +// Since these macros are auto-generate, lint will cause false positive. +// NOLINTBEGIN +TH_EXPORT_CPP_API_GetKeyboardDelegate(GetKeyboardDelegate); +TH_EXPORT_CPP_API_GetInputMethodAbility(GetInputMethodAbility); +// NOLINTEND