Sync full Android text after composition settles
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@@ -42,7 +43,8 @@ class _InputSyncPageState extends State<InputSyncPage> {
|
|||||||
final inputFocusNode = FocusNode();
|
final inputFocusNode = FocusNode();
|
||||||
|
|
||||||
WebSocketChannel? channel;
|
WebSocketChannel? channel;
|
||||||
String lastText = '';
|
Timer? syncDebounce;
|
||||||
|
String lastSyncedText = '';
|
||||||
bool connected = false;
|
bool connected = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -54,6 +56,7 @@ class _InputSyncPageState extends State<InputSyncPage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
syncDebounce?.cancel();
|
||||||
channel?.sink.close();
|
channel?.sink.close();
|
||||||
hostController.dispose();
|
hostController.dispose();
|
||||||
portController.dispose();
|
portController.dispose();
|
||||||
@@ -85,10 +88,12 @@ class _InputSyncPageState extends State<InputSyncPage> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
connected = true;
|
connected = true;
|
||||||
});
|
});
|
||||||
|
_sendCurrentText(force: true);
|
||||||
inputFocusNode.requestFocus();
|
inputFocusNode.requestFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _disconnect() {
|
void _disconnect() {
|
||||||
|
syncDebounce?.cancel();
|
||||||
channel?.sink.close();
|
channel?.sink.close();
|
||||||
channel = null;
|
channel = null;
|
||||||
setState(() {
|
setState(() {
|
||||||
@@ -97,23 +102,40 @@ class _InputSyncPageState extends State<InputSyncPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _syncTextChange() {
|
void _syncTextChange() {
|
||||||
final currentText = inputController.text;
|
if (!connected || channel == null) {
|
||||||
if (!connected || channel == null || currentText == lastText) {
|
|
||||||
lastText = currentText;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentText.length > lastText.length &&
|
final value = inputController.value;
|
||||||
currentText.startsWith(lastText)) {
|
if (value.composing.isValid) {
|
||||||
_send('insertText', text: currentText.substring(lastText.length));
|
return;
|
||||||
} else if (lastText.length == currentText.length + 1 &&
|
|
||||||
lastText.startsWith(currentText)) {
|
|
||||||
_send('backspace');
|
|
||||||
} else {
|
|
||||||
_send('insertText', text: currentText);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lastText = currentText;
|
final currentText = value.text;
|
||||||
|
if (currentText == lastSyncedText) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
syncDebounce?.cancel();
|
||||||
|
syncDebounce = Timer(const Duration(milliseconds: 800), _sendCurrentText);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _sendCurrentText({bool force = false}) {
|
||||||
|
if (!connected || channel == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final latestValue = inputController.value;
|
||||||
|
if (latestValue.composing.isValid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!force && latestValue.text == lastSyncedText) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastSyncedText = latestValue.text;
|
||||||
|
_send('replaceAll', text: latestValue.text);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _send(String action, {String? text}) {
|
void _send(String action, {String? text}) {
|
||||||
|
|||||||
Reference in New Issue
Block a user