diff --git a/lib/new-ui/widgets/send_page/send_address_input.dart b/lib/new-ui/widgets/send_page/send_address_input.dart index d02b71052..26ae69865 100644 --- a/lib/new-ui/widgets/send_page/send_address_input.dart +++ b/lib/new-ui/widgets/send_page/send_address_input.dart @@ -88,6 +88,8 @@ class _NewSendAddressInputState extends State { children: [ TextField( focusNode: widget.focusNode, + autocorrect: false, + enableSuggestions: false, onSubmitted: (val) => FocusScope.of(context).unfocus(), onChanged: state.didChange, onEditingComplete: () { diff --git a/lib/new-ui/widgets/send_page/send_amount_input.dart b/lib/new-ui/widgets/send_page/send_amount_input.dart index a9576d131..2ed9b9398 100644 --- a/lib/new-ui/widgets/send_page/send_amount_input.dart +++ b/lib/new-ui/widgets/send_page/send_amount_input.dart @@ -71,6 +71,8 @@ class _NewSendAmountInputState extends State { signed: false, decimal: widget.maxDecimals > 0, ), + autocorrect: false, + enableSuggestions: false, inputFormatters: [ DecimalInputFormatter(maxDecimals: widget.maxDecimals), ], @@ -87,7 +89,11 @@ class _NewSendAmountInputState extends State { onPressed: () async { final data = await Clipboard.getData(Clipboard.kTextPlain); if (data != null && data.text != null) { - widget.amountController.text = data.text!; + final text = data.text!; + widget.amountController.value = TextEditingValue( + text: text, + selection: TextSelection.collapsed(offset: text.length), + ); } }), ], diff --git a/lib/src/screens/connect_device/connect_device_page.dart b/lib/src/screens/connect_device/connect_device_page.dart index 3e3bda042..6aeb36a25 100644 --- a/lib/src/screens/connect_device/connect_device_page.dart +++ b/lib/src/screens/connect_device/connect_device_page.dart @@ -149,8 +149,8 @@ class ConnectDevicePageBodyState extends State { bleDevices.add(device); if (longWait) longWait = false; })) - ..onError((e) { - throw e.toString(); + ..onError((Object e) { + printV(e); }); _bleRefreshTimer?.cancel(); _bleRefreshTimer = null; diff --git a/lib/utils/decimal_input_formatter.dart b/lib/utils/decimal_input_formatter.dart index 18a2b5586..b45a554e8 100644 --- a/lib/utils/decimal_input_formatter.dart +++ b/lib/utils/decimal_input_formatter.dart @@ -11,7 +11,12 @@ class DecimalInputFormatter extends TextInputFormatter { final text = newValue.text; if (text.isEmpty) return newValue; - if (S.current.all.startsWith(text)) return TextEditingValue(text: ""); + if (S.current.all.startsWith(text)) { + return const TextEditingValue( + text: "", + selection: TextSelection.collapsed(offset: 0), + ); + } final regex = maxDecimals == 0 ? RegExp(r'^\d*$') : RegExp('^\\d*([.,]\\d{0,$maxDecimals})?\$'); return regex.hasMatch(text) ? newValue.copyWith(text: text.replaceAll(',', '.')) : oldValue; diff --git a/lib/utils/exception_handler.dart b/lib/utils/exception_handler.dart index eee768c61..abbef80cb 100644 --- a/lib/utils/exception_handler.dart +++ b/lib/utils/exception_handler.dart @@ -277,6 +277,7 @@ class ExceptionHandler { "invalid signature", "invalid password", "NetworkImage._loadAsync", + "Invalid image data", "SSLV3_ALERT_BAD_RECORD_MAC", "PlatformException(already_active, File picker is already active", // SVG-related errors @@ -296,6 +297,10 @@ class ExceptionHandler { "Wrong Device Status: 0x5515 (UNKNOWN)", "Command handling failed. With error: hostUnreachable", + // Android IME/Gboard occasionally reports a caret offset past the end of + // the text on the platform text-input channel while typing. Non-fatal + // framework<->platform desync; the app keeps working. + "invalid selection start", "FocusScopeNode was used after being disposed", "_getDismissibleFlushbar", "_QueuedFuture.execute (package:universal_ble/src/queue.dart:65)", @@ -456,6 +461,19 @@ class ExceptionHandler { } } + if (errorDetails.exception.toString().contains("Cannot add event after closing")) { + // grpc-dart teardown race (e.g. the MWEB channel on litecoin): a buffered outgoing + // frame is delivered to the http2 stream's sink after the call/channel was + // terminated. The message alone is too generic to ignore, so require the exact + // shape of grpc's forwarding chain: .map().map().handleError().listen(sink.add). + final stack = errorDetails.stack.toString(); + if (stack.contains("_StreamSinkWrapper.add") && + stack.contains("_MapStream._handleData") && + stack.contains("_HandleErrorStream._handleData")) { + return true; + } + } + return false; } }