From 4c406500a924e95b4dde29918fdd10a1abbba92a Mon Sep 17 00:00:00 2001 From: qiin2333 <414382190@qq.com> Date: Thu, 3 Sep 2026 11:29:54 +0800 Subject: [PATCH 1/4] fix: prevent three-finger IME from dismissing on focus loss --- entry/src/main/ets/pages/StreamPage.ets | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/entry/src/main/ets/pages/StreamPage.ets b/entry/src/main/ets/pages/StreamPage.ets index 2d9a749..1788893 100644 --- a/entry/src/main/ets/pages/StreamPage.ets +++ b/entry/src/main/ets/pages/StreamPage.ets @@ -2146,8 +2146,17 @@ struct StreamPage { if (result === ThreeFingerImeGestureResult.TRIGGERED) { this.touchInputHandler.cancelActiveInput(); this.panZoomHandler.cancelActiveGesture(); - this.showVirtualKeyboard = false; - this.imeManager.show(); + if (this.imeManager.isVisible) { + // 三指再次触发时主动关闭;hide() 会同步解除锁定。 + this.imeManager.hide(); + } else { + this.showVirtualKeyboard = false; + // 三指呼出的 IME 默认锁定:画面失焦事件只能触发重新显示, + // 仅再次三指或工具条关闭按钮可以主动关闭。 + this.imePanelLocked = true; + this.imeManager.setLocked(true); + this.imeManager.show(); + } } return result !== ThreeFingerImeGestureResult.PASS_THROUGH; } From c85f1195bf36c8f79b5e6a800a10df155a27d650 Mon Sep 17 00:00:00 2001 From: qiin2333 <414382190@qq.com> Date: Thu, 3 Sep 2026 13:52:24 +0800 Subject: [PATCH 2/4] fix: use actual IME visibility for gesture toggle --- entry/src/main/ets/pages/StreamPage.ets | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/entry/src/main/ets/pages/StreamPage.ets b/entry/src/main/ets/pages/StreamPage.ets index 1788893..6ce7a19 100644 --- a/entry/src/main/ets/pages/StreamPage.ets +++ b/entry/src/main/ets/pages/StreamPage.ets @@ -2146,10 +2146,10 @@ struct StreamPage { if (result === ThreeFingerImeGestureResult.TRIGGERED) { this.touchInputHandler.cancelActiveInput(); this.panZoomHandler.cancelActiveGesture(); - if (this.imeManager.isVisible) { + if (this.showKeyboardPanel) { // 三指再次触发时主动关闭;hide() 会同步解除锁定。 this.imeManager.hide(); - } else { + } else if (!this.imeManager.isVisible) { this.showVirtualKeyboard = false; // 三指呼出的 IME 默认锁定:画面失焦事件只能触发重新显示, // 仅再次三指或工具条关闭按钮可以主动关闭。 From 919aa3756bc3344b131b5eeafff6044a54e43e10 Mon Sep 17 00:00:00 2001 From: qiin2333 <414382190@qq.com> Date: Thu, 3 Sep 2026 14:23:07 +0800 Subject: [PATCH 3/4] fix: prevent concurrent IME show requests --- entry/src/main/ets/pages/StreamPage.ets | 2 +- .../main/ets/service/streaming/StreamIMEManager.ets | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/entry/src/main/ets/pages/StreamPage.ets b/entry/src/main/ets/pages/StreamPage.ets index 6ce7a19..7bffc3c 100644 --- a/entry/src/main/ets/pages/StreamPage.ets +++ b/entry/src/main/ets/pages/StreamPage.ets @@ -2149,7 +2149,7 @@ struct StreamPage { if (this.showKeyboardPanel) { // 三指再次触发时主动关闭;hide() 会同步解除锁定。 this.imeManager.hide(); - } else if (!this.imeManager.isVisible) { + } else if (!this.imeManager.isOpening && !this.imeManager.isVisible) { this.showVirtualKeyboard = false; // 三指呼出的 IME 默认锁定:画面失焦事件只能触发重新显示, // 仅再次三指或工具条关闭按钮可以主动关闭。 diff --git a/entry/src/main/ets/service/streaming/StreamIMEManager.ets b/entry/src/main/ets/service/streaming/StreamIMEManager.ets index 5cb3220..87ddaac 100644 --- a/entry/src/main/ets/service/streaming/StreamIMEManager.ets +++ b/entry/src/main/ets/service/streaming/StreamIMEManager.ets @@ -32,6 +32,7 @@ export interface IMECallbacks { export class StreamIMEManager { // ── 输入法控制器 ── private imeController: inputMethod.InputMethodController | null = null; + private showInProgress: boolean = false; private imeReadyTime: number = 0; /** 锁定模式:开启后点击其他区域不会收起 IME / IME bar,HIDE 事件会被拦截并重新唤起软键盘 */ private locked: boolean = false; @@ -67,6 +68,11 @@ export class StreamIMEManager { return this.imeController !== null; } + /** 当前是否正在异步显示 IME */ + get isOpening(): boolean { + return this.showInProgress; + } + /** * 当前是否处于锁定模式 */ @@ -88,7 +94,9 @@ export class StreamIMEManager { */ async show(): Promise { const session = this.viewModel?.getSession(); - if (!session) return; + if (!session || this.showInProgress || this.imeController) return; + + this.showInProgress = true; try { // 监听键盘高度变化 @@ -147,6 +155,8 @@ export class StreamIMEManager { this.imeReadyTime = Date.now(); } catch (err) { console.error(`[StreamPage] Failed to show IME: ${JSON.stringify(err)}`); + } finally { + this.showInProgress = false; } } From b3ee9837962f8d86df39149ed32c489b6c9dd8ca Mon Sep 17 00:00:00 2001 From: qiin2333 <414382190@qq.com> Date: Thu, 3 Sep 2026 15:13:08 +0800 Subject: [PATCH 4/4] fix: clean up failed IME initialization --- entry/src/main/ets/pages/StreamPage.ets | 2 +- .../service/streaming/StreamIMEManager.ets | 48 +++++++++++++------ 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/entry/src/main/ets/pages/StreamPage.ets b/entry/src/main/ets/pages/StreamPage.ets index 7bffc3c..d0768db 100644 --- a/entry/src/main/ets/pages/StreamPage.ets +++ b/entry/src/main/ets/pages/StreamPage.ets @@ -2149,7 +2149,7 @@ struct StreamPage { if (this.showKeyboardPanel) { // 三指再次触发时主动关闭;hide() 会同步解除锁定。 this.imeManager.hide(); - } else if (!this.imeManager.isOpening && !this.imeManager.isVisible) { + } else { this.showVirtualKeyboard = false; // 三指呼出的 IME 默认锁定:画面失焦事件只能触发重新显示, // 仅再次三指或工具条关闭按钮可以主动关闭。 diff --git a/entry/src/main/ets/service/streaming/StreamIMEManager.ets b/entry/src/main/ets/service/streaming/StreamIMEManager.ets index 87ddaac..5a49e93 100644 --- a/entry/src/main/ets/service/streaming/StreamIMEManager.ets +++ b/entry/src/main/ets/service/streaming/StreamIMEManager.ets @@ -33,6 +33,7 @@ export class StreamIMEManager { // ── 输入法控制器 ── private imeController: inputMethod.InputMethodController | null = null; private showInProgress: boolean = false; + private showRequestId: number = 0; private imeReadyTime: number = 0; /** 锁定模式:开启后点击其他区域不会收起 IME / IME bar,HIDE 事件会被拦截并重新唤起软键盘 */ private locked: boolean = false; @@ -61,18 +62,6 @@ export class StreamIMEManager { return this.imeReadyTime; } - /** - * 当前 IME 是否正在显示 - */ - get isVisible(): boolean { - return this.imeController !== null; - } - - /** 当前是否正在异步显示 IME */ - get isOpening(): boolean { - return this.showInProgress; - } - /** * 当前是否处于锁定模式 */ @@ -96,17 +85,20 @@ export class StreamIMEManager { const session = this.viewModel?.getSession(); if (!session || this.showInProgress || this.imeController) return; + const requestId = ++this.showRequestId; this.showInProgress = true; + let win: window.Window | null = null; + let controller: inputMethod.InputMethodController | null = null; try { // 监听键盘高度变化 - const win = await window.getLastWindow(this.context); + win = await window.getLastWindow(this.context); + if (requestId !== this.showRequestId) return; win.on('keyboardHeightChange', (height: number) => { this.callbacks?.onKeyboardHeightChanged(px2vp(height)); }); - const controller = inputMethod.getController(); - this.imeController = controller; + controller = inputMethod.getController(); // 先绑定输入法(必须先 attach 才能注册回调) const textConfig: inputMethod.TextConfig = { @@ -116,6 +108,11 @@ export class StreamIMEManager { } }; await controller.attach(true, textConfig); + if (requestId !== this.showRequestId) { + controller.detach(); + win.off('keyboardHeightChange'); + return; + } // attach 成功后注册输入回调 controller.on('insertText', (text: string) => { @@ -151,10 +148,30 @@ export class StreamIMEManager { } }); + // 完成 attach 和全部回调注册后再发布 controller,避免暴露半初始化状态。 + this.imeController = controller; this.callbacks?.onKeyboardVisibilityChanged(true); this.imeReadyTime = Date.now(); } catch (err) { console.error(`[StreamPage] Failed to show IME: ${JSON.stringify(err)}`); + if (controller) { + try { + controller.detach(); + } catch (cleanupErr) { + console.warn(`[StreamIME] Failed to detach after show error: ${JSON.stringify(cleanupErr)}`); + } + } + if (win) { + try { + win.off('keyboardHeightChange'); + } catch (cleanupErr) { + console.warn(`[StreamIME] Failed to remove height listener: ${JSON.stringify(cleanupErr)}`); + } + } + this.imeController = null; + this.locked = false; + this.callbacks?.onKeyboardVisibilityChanged(false); + this.callbacks?.onKeyboardHeightChanged(0); } finally { this.showInProgress = false; } @@ -165,6 +182,7 @@ export class StreamIMEManager { */ hide(): void { // 主动 hide 时释放锁定状态,避免下次 show 时锁定误传递 + this.showRequestId++; this.locked = false; try { if (this.imeController) {