diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index 40890f228..a8446de20 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -57,9 +57,7 @@ class _RemotePageState extends State { final TextEditingController _textController = TextEditingController(text: initText); - // This timer is used to check the composing status of the soft keyboard. - // It is used for Android, Korean(and other similar) input method. - Timer? _composingTimer; + bool _lastComposingChangeValid = false; _RemotePageState(String id) { initSharedStates(id); @@ -99,6 +97,9 @@ class _RemotePageState extends State { showToast(translate('Automatically record outgoing sessions')); } }); + if (isAndroid) { + _textController.addListener(textAndroidListener); + } } @override @@ -114,7 +115,6 @@ class _RemotePageState extends State { _physicalFocusNode.dispose(); await gFFI.close(); _timer?.cancel(); - _composingTimer?.cancel(); gFFI.dialogManager.dismissAll(); await SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: SystemUiOverlay.values); @@ -127,6 +127,16 @@ class _RemotePageState extends State { // The inner logic of `on_voice_call_closed` will check if the voice call is active. // Only one client is considered here for now. gFFI.chatModel.onVoiceCallClosed("End connetion"); + if (isAndroid) { + _textController.removeListener(textAndroidListener); + } + } + + // This listener is used to handle the composing region changes for Android soft keyboard input. + void textAndroidListener() { + if (_lastComposingChangeValid) { + _handleNonIOSSoftKeyboardInput(_textController.text); + } } // to-do: It should be better to use transparent color instead of the bgColor. @@ -150,7 +160,6 @@ class _RemotePageState extends State { gFFI.ffiModel.pi.version.isNotEmpty) { gFFI.invokeMethod("enable_soft_keyboard", false); } - _composingTimer?.cancel(); } else { _timer?.cancel(); _timer = Timer(kMobileDelaySoftKeyboardFocus, () { @@ -214,11 +223,8 @@ class _RemotePageState extends State { } void _handleNonIOSSoftKeyboardInput(String newValue) { - _composingTimer?.cancel(); - if (_textController.value.isComposingRangeValid) { - _composingTimer = Timer(Duration(milliseconds: 25), () { - _handleNonIOSSoftKeyboardInput(_textController.value.text); - }); + _lastComposingChangeValid = _textController.value.isComposingRangeValid; + if (_lastComposingChangeValid) { return; } var oldValue = _value; diff --git a/flutter/lib/models/input_model.dart b/flutter/lib/models/input_model.dart index c7e1e6131..2b00098ca 100644 --- a/flutter/lib/models/input_model.dart +++ b/flutter/lib/models/input_model.dart @@ -544,7 +544,25 @@ class InputModel { handleKeyDownEventModifiers(e); } - if (isMobile || (isDesktop || isWebDesktop) && keyboardMode == kKeyMapMode) { + // The physicalKey.usbHidUsage may be not correct for soft keyboard on Android. + // iOS does not have this issue. + // 1. Open the soft keyboard on Android + // 2. Switch to input method like zh/ko/ja + // 3. Click Backspace and Enter on the soft keyboard or physical keyboard + // 4. The physicalKey.usbHidUsage is not correct. + // PhysicalKeyboardKey#8ac83(usbHidUsage: "0x1100000042", debugName: "Key with ID 0x1100000042") + // LogicalKeyboardKey#2604c(keyId: "0x10000000d", keyLabel: "Enter", debugName: "Enter") + // + // The correct PhysicalKeyboardKey should be + // PhysicalKeyboardKey#e14a9(usbHidUsage: "0x00070028", debugName: "Enter") + // https://github.com/flutter/flutter/issues/157771 + final isKeyMatch = + isIOS || isAndroid && e.logicalKey.debugName == e.physicalKey.debugName; + final isMobileAndPeerNotAndroid = + isMobile && peerPlatform != kPeerPlatformAndroid; + final isDesktopAndMapMode = + isDesktop || isWebDesktop && keyboardMode == kKeyMapMode; + if (isKeyMatch && (isMobileAndPeerNotAndroid || isDesktopAndMapMode)) { // FIXME: e.character is wrong for dead keys, eg: ^ in de newKeyboardMode( e.character ?? '',