Enhance BLE connection handling and improve USB connection messaging

- Wrapped BLE scan and connection methods in try-catch blocks to handle errors gracefully and provide debug output.
- Added retry logic for service discovery on web platforms after transient disconnections.
- Updated USB connection messages in multiple languages to reflect active support on Android and desktop platforms.
- Improved loading indicators for contacts screen to show a spinner during data loading.
chore/offband-rebrand
just_stuff_tm 5 months ago committed by just-stuff-tm
parent ee3af52c0f
commit 2d1160d992

@ -37,6 +37,7 @@ class MainActivity : FlutterActivity() {
private var usbConnection: UsbDeviceConnection? = null private var usbConnection: UsbDeviceConnection? = null
private var usbPort: UsbSerialPort? = null private var usbPort: UsbSerialPort? = null
private var ioManager: SerialInputOutputManager? = null private var ioManager: SerialInputOutputManager? = null
private var connectedDeviceName: String? = null
private var pendingConnectResult: MethodChannel.Result? = null private var pendingConnectResult: MethodChannel.Result? = null
private var pendingConnectPortName: String? = null private var pendingConnectPortName: String? = null
@ -45,7 +46,19 @@ class MainActivity : FlutterActivity() {
private val permissionReceiver = private val permissionReceiver =
object : BroadcastReceiver() { object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) { override fun onReceive(context: Context?, intent: Intent?) {
if (intent?.action != usbPermissionAction) { when (intent?.action) {
UsbManager.ACTION_USB_DEVICE_DETACHED -> {
handleUsbDetached(intent)
return
}
usbPermissionAction -> {
}
else -> {
return
}
}
if (intent.action != usbPermissionAction) {
return return
} }
@ -116,12 +129,19 @@ class MainActivity : FlutterActivity() {
override fun onDestroy() { override fun onDestroy() {
closeUsbConnection() closeUsbConnection()
usbIoExecutor.shutdownNow() usbIoExecutor.shutdownNow()
unregisterReceiver(permissionReceiver) try {
unregisterReceiver(permissionReceiver)
} catch (_: IllegalArgumentException) {
}
super.onDestroy() super.onDestroy()
} }
private fun registerUsbPermissionReceiver() { private fun registerUsbPermissionReceiver() {
val filter = IntentFilter(usbPermissionAction) val filter =
IntentFilter().apply {
addAction(usbPermissionAction)
addAction(UsbManager.ACTION_USB_DEVICE_DETACHED)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
registerReceiver(permissionReceiver, filter, RECEIVER_NOT_EXPORTED) registerReceiver(permissionReceiver, filter, RECEIVER_NOT_EXPORTED)
} else { } else {
@ -256,6 +276,7 @@ class MainActivity : FlutterActivity() {
usbConnection = connection usbConnection = connection
usbPort = port usbPort = port
connectedDeviceName = device.deviceName
ioManager = ioManager =
SerialInputOutputManager( SerialInputOutputManager(
@ -311,6 +332,38 @@ class MainActivity : FlutterActivity() {
} catch (_: Exception) { } catch (_: Exception) {
} }
usbConnection = null usbConnection = null
connectedDeviceName = null
}
private fun handleUsbDetached(intent: Intent) {
val detachedDevice =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
intent.getParcelableExtra(UsbManager.EXTRA_DEVICE, UsbDevice::class.java)
} else {
@Suppress("DEPRECATION")
intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)
}
val detachedName = detachedDevice?.deviceName ?: return
if (pendingConnectPortName == detachedName) {
pendingConnectResult?.error(
"usb_device_detached",
"USB device was removed before the connection completed",
null,
)
pendingConnectResult = null
pendingConnectPortName = null
}
if (connectedDeviceName == detachedName) {
closeUsbConnection()
eventSink?.error(
"usb_device_detached",
"USB device was disconnected",
null,
)
}
} }
private fun pendingIntentFlags(): Int { private fun pendingIntentFlags(): Int {

@ -738,12 +738,18 @@ class MeshCoreConnector extends ChangeNotifier {
notifyListeners(); notifyListeners();
}); });
await FlutterBluePlus.startScan( try {
withKeywords: ["MeshCore-", "Whisper-"], await FlutterBluePlus.startScan(
webOptionalServices: [Guid(MeshCoreUuids.service)], withKeywords: ["MeshCore-", "Whisper-"],
timeout: timeout, webOptionalServices: [Guid(MeshCoreUuids.service)],
androidScanMode: AndroidScanMode.lowLatency, timeout: timeout,
); androidScanMode: AndroidScanMode.lowLatency,
);
} catch (error) {
debugPrint('[BLE Scan] Scan/picker failure: $error');
_setState(MeshCoreConnectionState.disconnected);
rethrow;
}
await Future.delayed(timeout); await Future.delayed(timeout);
await stopScan(); await stopScan();
@ -791,17 +797,24 @@ class MeshCoreConnector extends ChangeNotifier {
notifyListeners(); notifyListeners();
try { try {
final connectLabel = _deviceDisplayName ?? _deviceId;
debugPrint('[BLE Connect] Starting connect to $connectLabel');
_connectionSubscription = device.connectionState.listen((state) { _connectionSubscription = device.connectionState.listen((state) {
if (state == BluetoothConnectionState.disconnected && isConnected) { if (state == BluetoothConnectionState.disconnected && isConnected) {
_handleDisconnection(); _handleDisconnection();
} }
}); });
await device.connect( try {
timeout: const Duration(seconds: 15), await device.connect(
mtu: null, timeout: const Duration(seconds: 15),
license: License.free, mtu: null,
); license: License.free,
);
} catch (error) {
debugPrint('[BLE Connect] device.connect() failure: $error');
rethrow;
}
// Request larger MTU only on native platforms; web does not support it. // Request larger MTU only on native platforms; web does not support it.
if (!PlatformInfo.isWeb) { if (!PlatformInfo.isWeb) {
@ -813,7 +826,27 @@ class MeshCoreConnector extends ChangeNotifier {
} }
} }
List<BluetoothService> services = await device.discoverServices(); late final List<BluetoothService> services;
try {
services = await device.discoverServices();
} catch (error) {
debugPrint('[BLE Connect] service discovery failure: $error');
if (PlatformInfo.isWeb &&
error.toString().contains('GATT Server is disconnected')) {
debugPrint(
'[BLE Connect] retrying service discovery after transient web disconnect',
);
await Future<void>.delayed(const Duration(milliseconds: 300));
await device.connect(
timeout: const Duration(seconds: 15),
mtu: null,
license: License.free,
);
services = await device.discoverServices();
} else {
rethrow;
}
}
BluetoothService? uartService; BluetoothService? uartService;
for (var service in services) { for (var service in services) {
@ -847,6 +880,7 @@ class MeshCoreConnector extends ChangeNotifier {
try { try {
await _txCharacteristic!.setNotifyValue(true); await _txCharacteristic!.setNotifyValue(true);
} catch (error) { } catch (error) {
debugPrint('[BLE Connect] notify failure (web, ignored): $error');
debugPrint('Web setNotifyValue error (ignoring): $error'); debugPrint('Web setNotifyValue error (ignoring): $error');
} }
}()); }());
@ -861,6 +895,7 @@ class MeshCoreConnector extends ChangeNotifier {
await _txCharacteristic!.setNotifyValue(true); await _txCharacteristic!.setNotifyValue(true);
notifySet = true; notifySet = true;
} catch (e) { } catch (e) {
debugPrint('[BLE Connect] notify failure: $e');
debugPrint('setNotifyValue attempt ${attempt + 1}/3 failed: $e'); debugPrint('setNotifyValue attempt ${attempt + 1}/3 failed: $e');
if (attempt == 2) rethrow; if (attempt == 2) rethrow;
} }
@ -1231,6 +1266,15 @@ class MeshCoreConnector extends ChangeNotifier {
_selfInfoRetryTimer?.cancel(); _selfInfoRetryTimer?.cancel();
if (PlatformInfo.isWeb && if (PlatformInfo.isWeb &&
_activeTransport == MeshCoreTransportType.bluetooth) { _activeTransport == MeshCoreTransportType.bluetooth) {
_selfInfoRetryTimer = Timer(const Duration(seconds: 10), () {
if (!isConnected || !_awaitingSelfInfo) {
return;
}
if (_isLoadingContacts || _isSyncingChannels || _channelSyncInFlight) {
return;
}
unawaited(sendFrame(buildAppStartFrame()));
});
return; return;
} }
_selfInfoRetryTimer = Timer.periodic(const Duration(milliseconds: 3500), ( _selfInfoRetryTimer = Timer.periodic(const Duration(milliseconds: 3500), (

@ -1806,9 +1806,9 @@
"connectionChoiceBluetoothLabel": "Bluetooth", "connectionChoiceBluetoothLabel": "Bluetooth",
"connectionChoiceTitle": "Изберете метода на връзка.", "connectionChoiceTitle": "Изберете метода на връзка.",
"connectionChoiceSubtitle": "Изберете как искате да получите вашия устройство MeshCore.", "connectionChoiceSubtitle": "Изберете как искате да получите вашия устройство MeshCore.",
"usbScreenTitle": "Връзката чрез USB ще бъде налична скоро.", "usbScreenNote": "USB серийната връзка е активна на поддържаните Android устройства и настолни платформи.",
"usbScreenSubtitle": "Създаваме път за комуникация, базиран на последователно предаване на данни, за Android и настолни компютри.", "usbScreenStatus": "Изберете USB устройство",
"usbScreenStatus": "Ще бъде достъпно скоро", "usbScreenTitle": "Свързване чрез USB",
"usbScreenNote": "След като бъде внедрена поддръжката за USB, ще изберете сериен порт и ще се свържете директно към вашето устройство MeshCore.", "usbScreenSubtitle": "Изберете открития сериен уред и свържете директно към вашия MeshCore възел.",
"usbScreenEmptyState": "Няма открити USB устройства. Включете едно и опитайте отново." "usbScreenEmptyState": "Няма открити USB устройства. Включете едно и опитайте отново."
} }

@ -1834,9 +1834,9 @@
"connectionChoiceUsbLabel": "USB", "connectionChoiceUsbLabel": "USB",
"connectionChoiceBluetoothLabel": "Bluetooth", "connectionChoiceBluetoothLabel": "Bluetooth",
"connectionChoiceTitle": "Wählen Sie Ihre bevorzugte Verbindungsmethode.", "connectionChoiceTitle": "Wählen Sie Ihre bevorzugte Verbindungsmethode.",
"usbScreenTitle": "Die USB-Verbindung wird bald verfügbar sein.", "usbScreenSubtitle": "Wählen Sie ein erkannten serielles Gerät aus und verbinden Sie es direkt mit Ihrem MeshCore-Knoten.",
"usbScreenSubtitle": "Wir entwickeln eine Verbindung, die sowohl für Android- als auch für Desktop-Geräte geeignet ist und auf einer seriellen Schnittstelle basiert.", "usbScreenNote": "USB-Serielle Schnittstelle ist auf unterstützten Android-Geräten und Desktop-Plattformen aktiv.",
"usbScreenStatus": "Bald verfügbar", "usbScreenTitle": "Über USB verbinden",
"usbScreenNote": "Sobald die USB-Unterstützung implementiert ist, wählen Sie einen seriellen Anschluss und verbinden Sie ihn direkt mit Ihrem MeshCore-Gerät.", "usbScreenStatus": "Wählen Sie ein USB-Gerät aus",
"usbScreenEmptyState": "Keine USB-Geräte gefunden. Schließen Sie eines an und aktualisieren Sie." "usbScreenEmptyState": "Keine USB-Geräte gefunden. Schließen Sie eines an und aktualisieren Sie."
} }

@ -1834,9 +1834,9 @@
"connectionChoiceSubtitle": "Seleccione la forma en que desea acceder a su dispositivo MeshCore.", "connectionChoiceSubtitle": "Seleccione la forma en que desea acceder a su dispositivo MeshCore.",
"connectionChoiceBluetoothLabel": "Bluetooth", "connectionChoiceBluetoothLabel": "Bluetooth",
"connectionChoiceUsbLabel": "USB", "connectionChoiceUsbLabel": "USB",
"usbScreenTitle": "La conexión USB estará disponible próximamente.", "usbScreenStatus": "Seleccione un dispositivo USB",
"usbScreenSubtitle": "Estamos creando una conexión en serie para dispositivos Android y de escritorio.", "usbScreenNote": "La comunicación serial a través de USB está activa en dispositivos Android compatibles y en plataformas de escritorio.",
"usbScreenStatus": "Próximamente", "usbScreenTitle": "Conecte mediante USB",
"usbScreenNote": "Una vez que se implemente el soporte para USB, seleccionará un puerto serie y se conectará directamente a su dispositivo MeshCore.", "usbScreenSubtitle": "Seleccione un dispositivo de serie detectado y conéctelo directamente a su nodo MeshCore.",
"usbScreenEmptyState": "No se detectaron dispositivos USB. Conecte uno y vuelva a intentar." "usbScreenEmptyState": "No se encontraron dispositivos USB. Conecte uno y vuelva a cargar."
} }

@ -1806,9 +1806,9 @@
"connectionChoiceBluetoothLabel": "Bluetooth", "connectionChoiceBluetoothLabel": "Bluetooth",
"connectionChoiceUsbLabel": "USB", "connectionChoiceUsbLabel": "USB",
"connectionChoiceSubtitle": "Choisissez la méthode de livraison que vous préférez pour votre appareil MeshCore.", "connectionChoiceSubtitle": "Choisissez la méthode de livraison que vous préférez pour votre appareil MeshCore.",
"usbScreenTitle": "La connexion USB sera disponible prochainement.", "usbScreenStatus": "Sélectionnez un périphérique USB",
"usbScreenSubtitle": "Nous mettons en place un chemin de connexion basé sur une série pour les appareils Android et les ordinateurs de bureau.", "usbScreenSubtitle": "Sélectionnez un périphérique série détecté et connectez-vous directement à votre nœud MeshCore.",
"usbScreenStatus": "Bientôt", "usbScreenNote": "La communication série USB est active sur les appareils Android et les plateformes de bureau pris en charge.",
"usbScreenNote": "Une fois que le support USB sera disponible, vous sélectionnerez un port série et vous connecterez directement à votre appareil MeshCore.", "usbScreenTitle": "Connectez via USB",
"usbScreenEmptyState": "Aucun périphérique USB n'a été trouvé. Connectez-en un et rafraîchissez." "usbScreenEmptyState": "Aucun périphérique USB n'a été trouvé. Veuillez connecter un périphérique et rafraîchir la page."
} }

@ -1806,9 +1806,9 @@
"connectionChoiceSubtitle": "Seleziona il metodo che preferisci per accedere al tuo dispositivo MeshCore.", "connectionChoiceSubtitle": "Seleziona il metodo che preferisci per accedere al tuo dispositivo MeshCore.",
"connectionChoiceBluetoothLabel": "Bluetooth", "connectionChoiceBluetoothLabel": "Bluetooth",
"connectionChoiceUsbLabel": "USB", "connectionChoiceUsbLabel": "USB",
"usbScreenTitle": "La connessione USB sarà disponibile a breve.", "usbScreenNote": "La comunicazione seriale USB è attiva sui dispositivi Android supportati e sulle piattaforme desktop.",
"usbScreenSubtitle": "Stiamo sviluppando un percorso di connessione basato su serie per Android e per i desktop.", "usbScreenStatus": "Seleziona un dispositivo USB",
"usbScreenStatus": "Arriverà presto", "usbScreenSubtitle": "Seleziona il dispositivo seriale rilevato e connettilo direttamente al tuo nodo MeshCore.",
"usbScreenNote": "Una volta che il supporto USB sarà disponibile, selezionerete una porta seriale e vi connetterete direttamente al vostro dispositivo MeshCore.", "usbScreenTitle": "Connessione tramite USB",
"usbScreenEmptyState": "Nessun dispositivo USB rilevato. Collegare uno e riavviare." "usbScreenEmptyState": "Nessun dispositivo USB rilevato. Collegare uno e riavviare."
} }

@ -122,18 +122,18 @@ class AppLocalizationsBg extends AppLocalizations {
String get connectionChoiceBluetoothLabel => 'Bluetooth'; String get connectionChoiceBluetoothLabel => 'Bluetooth';
@override @override
String get usbScreenTitle => 'Връзката чрез USB ще бъде налична скоро.'; String get usbScreenTitle => 'Свързване чрез USB';
@override @override
String get usbScreenSubtitle => String get usbScreenSubtitle =>
'Създаваме път за комуникация, базиран на последователно предаване на данни, за Android и настолни компютри.'; 'Изберете открития сериен уред и свържете директно към вашия MeshCore възел.';
@override @override
String get usbScreenStatus => 'Ще бъде достъпно скоро'; String get usbScreenStatus => 'Изберете USB устройство';
@override @override
String get usbScreenNote => String get usbScreenNote =>
'След като бъде внедрена поддръжката за USB, ще изберете сериен порт и ще се свържете директно към вашето устройство MeshCore.'; 'USB серийната връзка е активна на поддържаните Android устройства и настолни платформи.';
@override @override
String get usbScreenEmptyState => String get usbScreenEmptyState =>

@ -123,18 +123,18 @@ class AppLocalizationsDe extends AppLocalizations {
String get connectionChoiceBluetoothLabel => 'Bluetooth'; String get connectionChoiceBluetoothLabel => 'Bluetooth';
@override @override
String get usbScreenTitle => 'Die USB-Verbindung wird bald verfügbar sein.'; String get usbScreenTitle => 'Über USB verbinden';
@override @override
String get usbScreenSubtitle => String get usbScreenSubtitle =>
'Wir entwickeln eine Verbindung, die sowohl für Android- als auch für Desktop-Geräte geeignet ist und auf einer seriellen Schnittstelle basiert.'; 'Wählen Sie ein erkannten serielles Gerät aus und verbinden Sie es direkt mit Ihrem MeshCore-Knoten.';
@override @override
String get usbScreenStatus => 'Bald verfügbar'; String get usbScreenStatus => 'Wählen Sie ein USB-Gerät aus';
@override @override
String get usbScreenNote => String get usbScreenNote =>
'Sobald die USB-Unterstützung implementiert ist, wählen Sie einen seriellen Anschluss und verbinden Sie ihn direkt mit Ihrem MeshCore-Gerät.'; 'USB-Serielle Schnittstelle ist auf unterstützten Android-Geräten und Desktop-Plattformen aktiv.';
@override @override
String get usbScreenEmptyState => String get usbScreenEmptyState =>

@ -122,23 +122,22 @@ class AppLocalizationsEs extends AppLocalizations {
String get connectionChoiceBluetoothLabel => 'Bluetooth'; String get connectionChoiceBluetoothLabel => 'Bluetooth';
@override @override
String get usbScreenTitle => String get usbScreenTitle => 'Conecte mediante USB';
'La conexión USB estará disponible próximamente.';
@override @override
String get usbScreenSubtitle => String get usbScreenSubtitle =>
'Estamos creando una conexión en serie para dispositivos Android y de escritorio.'; 'Seleccione un dispositivo de serie detectado y conéctelo directamente a su nodo MeshCore.';
@override @override
String get usbScreenStatus => 'Próximamente'; String get usbScreenStatus => 'Seleccione un dispositivo USB';
@override @override
String get usbScreenNote => String get usbScreenNote =>
'Una vez que se implemente el soporte para USB, seleccionará un puerto serie y se conectará directamente a su dispositivo MeshCore.'; 'La comunicación serial a través de USB está activa en dispositivos Android compatibles y en plataformas de escritorio.';
@override @override
String get usbScreenEmptyState => String get usbScreenEmptyState =>
'No se detectaron dispositivos USB. Conecte uno y vuelva a intentar.'; 'No se encontraron dispositivos USB. Conecte uno y vuelva a cargar.';
@override @override
String get scanner_scanning => 'Escaneando dispositivos...'; String get scanner_scanning => 'Escaneando dispositivos...';

@ -122,23 +122,22 @@ class AppLocalizationsFr extends AppLocalizations {
String get connectionChoiceBluetoothLabel => 'Bluetooth'; String get connectionChoiceBluetoothLabel => 'Bluetooth';
@override @override
String get usbScreenTitle => String get usbScreenTitle => 'Connectez via USB';
'La connexion USB sera disponible prochainement.';
@override @override
String get usbScreenSubtitle => String get usbScreenSubtitle =>
'Nous mettons en place un chemin de connexion basé sur une série pour les appareils Android et les ordinateurs de bureau.'; 'Sélectionnez un périphérique série détecté et connectez-vous directement à votre nœud MeshCore.';
@override @override
String get usbScreenStatus => 'Bientôt'; String get usbScreenStatus => 'Sélectionnez un périphérique USB';
@override @override
String get usbScreenNote => String get usbScreenNote =>
'Une fois que le support USB sera disponible, vous sélectionnerez un port série et vous connecterez directement à votre appareil MeshCore.'; 'La communication série USB est active sur les appareils Android et les plateformes de bureau pris en charge.';
@override @override
String get usbScreenEmptyState => String get usbScreenEmptyState =>
'Aucun périphérique USB n\'a été trouvé. Connectez-en un et rafraîchissez.'; 'Aucun périphérique USB n\'a été trouvé. Veuillez connecter un périphérique et rafraîchir la page.';
@override @override
String get scanner_scanning => 'Recherche de périphériques...'; String get scanner_scanning => 'Recherche de périphériques...';

@ -123,18 +123,18 @@ class AppLocalizationsIt extends AppLocalizations {
String get connectionChoiceBluetoothLabel => 'Bluetooth'; String get connectionChoiceBluetoothLabel => 'Bluetooth';
@override @override
String get usbScreenTitle => 'La connessione USB sarà disponibile a breve.'; String get usbScreenTitle => 'Connessione tramite USB';
@override @override
String get usbScreenSubtitle => String get usbScreenSubtitle =>
'Stiamo sviluppando un percorso di connessione basato su serie per Android e per i desktop.'; 'Seleziona il dispositivo seriale rilevato e connettilo direttamente al tuo nodo MeshCore.';
@override @override
String get usbScreenStatus => 'Arriverà presto'; String get usbScreenStatus => 'Seleziona un dispositivo USB';
@override @override
String get usbScreenNote => String get usbScreenNote =>
'Una volta che il supporto USB sarà disponibile, selezionerete una porta seriale e vi connetterete direttamente al vostro dispositivo MeshCore.'; 'La comunicazione seriale USB è attiva sui dispositivi Android supportati e sulle piattaforme desktop.';
@override @override
String get usbScreenEmptyState => String get usbScreenEmptyState =>

@ -122,18 +122,18 @@ class AppLocalizationsNl extends AppLocalizations {
String get connectionChoiceBluetoothLabel => 'Bluetooth'; String get connectionChoiceBluetoothLabel => 'Bluetooth';
@override @override
String get usbScreenTitle => 'USB-verbinding is binnenkort beschikbaar.'; String get usbScreenTitle => 'Verbind via USB';
@override @override
String get usbScreenSubtitle => String get usbScreenSubtitle =>
'We ontwikkelen een verbindingspad op basis van seriële communicatie, zowel voor Android als voor desktop-computers.'; 'Kies een gedetecteerd seriële apparaat en verbind deze direct met uw MeshCore-node.';
@override @override
String get usbScreenStatus => 'Komende week'; String get usbScreenStatus => 'Selecteer een USB-apparaat';
@override @override
String get usbScreenNote => String get usbScreenNote =>
'Zodra de USB-ondersteuning is geïnstalleerd, selecteert u een seriële poort en verbindt u direct met uw MeshCore-apparaat.'; 'USB-serieel is actief op ondersteunde Android-apparaten en desktop-platforms.';
@override @override
String get usbScreenEmptyState => String get usbScreenEmptyState =>

@ -122,18 +122,18 @@ class AppLocalizationsPl extends AppLocalizations {
String get connectionChoiceBluetoothLabel => 'Bluetooth'; String get connectionChoiceBluetoothLabel => 'Bluetooth';
@override @override
String get usbScreenTitle => 'Połączenie USB będzie dostępne wkrótce.'; String get usbScreenTitle => 'Połącz przez USB';
@override @override
String get usbScreenSubtitle => String get usbScreenSubtitle =>
'Tworzymy ścieżkę połączenia opartą na protokole szeregowym, przeznaczoną zarówno dla urządzeń z systemem Android, jak i dla komputerów stacjonarnych.'; 'Wybierz wykryty urządzenie szeregowe i podłącz je bezpośrednio do swojego węzła MeshCore.';
@override @override
String get usbScreenStatus => 'Wkrótce'; String get usbScreenStatus => 'Wybierz urządzenie USB';
@override @override
String get usbScreenNote => String get usbScreenNote =>
'Po wdrożeniu wsparcia dla USB, wybierzesz port szeregowy i połączysz się bezpośrednio z urządzeniem MeshCore.'; 'Port szeregowy USB jest aktywny na urządzeniach z Androidem i platformach stacjonarnych, które obsługują tę funkcję.';
@override @override
String get usbScreenEmptyState => String get usbScreenEmptyState =>

@ -122,18 +122,18 @@ class AppLocalizationsPt extends AppLocalizations {
String get connectionChoiceBluetoothLabel => 'Bluetooth'; String get connectionChoiceBluetoothLabel => 'Bluetooth';
@override @override
String get usbScreenTitle => 'A conexão USB estará disponível em breve.'; String get usbScreenTitle => 'Conecte via USB';
@override @override
String get usbScreenSubtitle => String get usbScreenSubtitle =>
'Estamos criando um caminho de conexão baseado em série para dispositivos Android e de desktop.'; 'Selecione o dispositivo serial detectado e conecte-o diretamente ao seu nó MeshCore.';
@override @override
String get usbScreenStatus => 'Em breve'; String get usbScreenStatus => 'Selecione um dispositivo USB';
@override @override
String get usbScreenNote => String get usbScreenNote =>
'Assim que o suporte USB for implementado, você poderá selecionar uma porta serial e conectar-se diretamente ao seu dispositivo MeshCore.'; 'A comunicação serial USB está ativa em dispositivos Android e plataformas de desktop compatíveis.';
@override @override
String get usbScreenEmptyState => String get usbScreenEmptyState =>

@ -122,23 +122,22 @@ class AppLocalizationsRu extends AppLocalizations {
String get connectionChoiceBluetoothLabel => 'Bluetooth'; String get connectionChoiceBluetoothLabel => 'Bluetooth';
@override @override
String get usbScreenTitle => String get usbScreenTitle => 'Подключение через USB';
'Подключение через USB будет доступно в ближайшее время.';
@override @override
String get usbScreenSubtitle => String get usbScreenSubtitle =>
'Мы создаем последовательную схему подключения для устройств на базе Android и настольных компьютеров.'; 'Выберите обнаруженное устройство с последовательным интерфейсом и подключите его напрямую к вашему узлу MeshCore.';
@override @override
String get usbScreenStatus => 'Скоро'; String get usbScreenStatus => 'Выберите USB-устройство';
@override @override
String get usbScreenNote => String get usbScreenNote =>
'Как только появится поддержка USB, вы сможете выбрать последовательный порт и напрямую подключиться к вашему устройству MeshCore.'; 'USB-серийный порт активен на поддерживаемых устройствах Android и на настольных платформах.';
@override @override
String get usbScreenEmptyState => String get usbScreenEmptyState =>
'Не обнаружено никаких устройств USB. Подключите одно из них и обновите список.'; 'Не обнаружено устройств USB. Подключите одно из них и обновите список.';
@override @override
String get scanner_scanning => 'Поиск устройств...'; String get scanner_scanning => 'Поиск устройств...';

@ -122,18 +122,18 @@ class AppLocalizationsSk extends AppLocalizations {
String get connectionChoiceBluetoothLabel => 'Bluetooth'; String get connectionChoiceBluetoothLabel => 'Bluetooth';
@override @override
String get usbScreenTitle => 'Pripojenie cez USB bude k dispozícii čoskoro.'; String get usbScreenTitle => 'Pripojte cez USB';
@override @override
String get usbScreenSubtitle => String get usbScreenSubtitle =>
'Vytvárajeme komunikačný systém založený na sériovej komunikácii pre Android a stolné počítače.'; 'Vyberte detekovaný sériový zariadenie a pripojte ho priamo k vašej MeshCore uzlu.';
@override @override
String get usbScreenStatus => 'Čoskoro'; String get usbScreenStatus => 'Vyberte USB zariadenie';
@override @override
String get usbScreenNote => String get usbScreenNote =>
'Po implementácii podpory pre USB, budete môcť vybrať sériový port a priamo sa pripojiť k vašmu zariadeniu MeshCore.'; 'USB sériová komunikácia je aktívna na podporovaných zariadeniach s Androidom a na desktopových platformách.';
@override @override
String get usbScreenEmptyState => String get usbScreenEmptyState =>

@ -122,22 +122,22 @@ class AppLocalizationsSl extends AppLocalizations {
String get connectionChoiceBluetoothLabel => 'Bluetooth'; String get connectionChoiceBluetoothLabel => 'Bluetooth';
@override @override
String get usbScreenTitle => 'Vnos preko USB-ja bo v kratkem na voljo.'; String get usbScreenTitle => 'Povežite preko USB';
@override @override
String get usbScreenSubtitle => String get usbScreenSubtitle =>
'Gradimo pot za serijsko povezavo za Android in računalnike.'; 'Izberite zaznano serijsko napravo in se neposredno povežite z vašim MeshCore-om.';
@override @override
String get usbScreenStatus => 'Čez kratko časa'; String get usbScreenStatus => 'Izberite USB naprave.';
@override @override
String get usbScreenNote => String get usbScreenNote =>
'Ko bo podpora za USB na voljo, boste izbrali serijsko vrata in se neposredno povezali z vašim napravem MeshCore.'; 'USB serijska povezava je aktivna na podprtih napravah Android in na desktop platformah.';
@override @override
String get usbScreenEmptyState => String get usbScreenEmptyState =>
'Niti en USB naprave niso bilo najdeno. Povežite eno in posodobite.'; 'Niti en USB naprave niso najdeni. Povežite eno in posodobite.';
@override @override
String get scanner_scanning => 'Skeniram za naprave...'; String get scanner_scanning => 'Skeniram za naprave...';

@ -122,18 +122,18 @@ class AppLocalizationsSv extends AppLocalizations {
String get connectionChoiceBluetoothLabel => 'Bluetooth'; String get connectionChoiceBluetoothLabel => 'Bluetooth';
@override @override
String get usbScreenTitle => 'USB-anslutning kommer snart'; String get usbScreenTitle => 'Anslut via USB';
@override @override
String get usbScreenSubtitle => String get usbScreenSubtitle =>
'Vi skapar en seriebaserad anslutningsväg för både Android- och skrivbordsenheter.'; 'Välj en detekterad seriell enhet och anslut direkt till din MeshCore-nod.';
@override @override
String get usbScreenStatus => 'Kommer snart'; String get usbScreenStatus => 'Välj en USB-enhet';
@override @override
String get usbScreenNote => String get usbScreenNote =>
'När USB-stöd är implementerat, kommer du att välja en seriell port och ansluta direkt till din MeshCore-enhet.'; 'USB-seriell kommunikation är aktiv på stöderliga Android-enheter och skrivbordsplattformar.';
@override @override
String get usbScreenEmptyState => String get usbScreenEmptyState =>

@ -122,19 +122,18 @@ class AppLocalizationsUk extends AppLocalizations {
String get connectionChoiceBluetoothLabel => 'Bluetooth'; String get connectionChoiceBluetoothLabel => 'Bluetooth';
@override @override
String get usbScreenTitle => String get usbScreenTitle => 'Підключити через USB';
'Підключення через USB буде доступне найближчим часом.';
@override @override
String get usbScreenSubtitle => String get usbScreenSubtitle =>
'Ми створюємо серійний шлях з\'єднання для Android та десктопних комп\'ютерів.'; 'Виберіть виявлене серійне пристрій і підключіть його безпосередньо до вашого вузла MeshCore.';
@override @override
String get usbScreenStatus => 'Скоро'; String get usbScreenStatus => 'Виберіть пристрій USB';
@override @override
String get usbScreenNote => String get usbScreenNote =>
'Після того, як буде реалізовано підтримку USB, ви виберете серійний порт і підключитесь безпосередньо до вашого пристрою MeshCore.'; 'USB-серіальний інтерфейс активний на підтримуваних пристроях на базі Android та на десктопних платформах.';
@override @override
String get usbScreenEmptyState => String get usbScreenEmptyState =>

@ -121,16 +121,16 @@ class AppLocalizationsZh extends AppLocalizations {
String get connectionChoiceBluetoothLabel => '蓝牙'; String get connectionChoiceBluetoothLabel => '蓝牙';
@override @override
String get usbScreenTitle => 'USB 连接即将推出'; String get usbScreenTitle => '通过USB连接';
@override @override
String get usbScreenSubtitle => '我们正在构建一个基于串行的连接路径用于Android和桌面设备'; String get usbScreenSubtitle => '选择已检测到的串行设备,并直接连接到您的 MeshCore 节点';
@override @override
String get usbScreenStatus => '即将推出'; String get usbScreenStatus => '选择一个 USB 设备';
@override @override
String get usbScreenNote => '一旦USB支持功能上线您就可以选择一个串口并直接连接到您的MeshCore设备'; String get usbScreenNote => '在支持的 Android 设备和桌面平台上USB 串行通信功能已启用';
@override @override
String get usbScreenEmptyState => '未找到任何 USB 设备。请插入一个,然后刷新。'; String get usbScreenEmptyState => '未找到任何 USB 设备。请插入一个,然后刷新。';

@ -1806,9 +1806,9 @@
"connectionChoiceUsbLabel": "USB", "connectionChoiceUsbLabel": "USB",
"connectionChoiceSubtitle": "Kies hoe u uw MeshCore-apparaat wilt bereiken.", "connectionChoiceSubtitle": "Kies hoe u uw MeshCore-apparaat wilt bereiken.",
"connectionChoiceBluetoothLabel": "Bluetooth", "connectionChoiceBluetoothLabel": "Bluetooth",
"usbScreenTitle": "USB-verbinding is binnenkort beschikbaar.", "usbScreenSubtitle": "Kies een gedetecteerd seriële apparaat en verbind deze direct met uw MeshCore-node.",
"usbScreenSubtitle": "We ontwikkelen een verbindingspad op basis van seriële communicatie, zowel voor Android als voor desktop-computers.", "usbScreenStatus": "Selecteer een USB-apparaat",
"usbScreenStatus": "Komende week", "usbScreenNote": "USB-serieel is actief op ondersteunde Android-apparaten en desktop-platforms.",
"usbScreenNote": "Zodra de USB-ondersteuning is geïnstalleerd, selecteert u een seriële poort en verbindt u direct met uw MeshCore-apparaat.", "usbScreenTitle": "Verbind via USB",
"usbScreenEmptyState": "Geen USB-apparaten gevonden. Sluit er een aan en herlaad." "usbScreenEmptyState": "Geen USB-apparaten gevonden. Sluit er een aan en herlaad."
} }

@ -1806,9 +1806,9 @@
"connectionChoiceSubtitle": "Wybierz, w jaki sposób chcesz uzyskać dostęp do swojego urządzenia MeshCore.", "connectionChoiceSubtitle": "Wybierz, w jaki sposób chcesz uzyskać dostęp do swojego urządzenia MeshCore.",
"connectionChoiceTitle": "Wybierz metodę połączenia.", "connectionChoiceTitle": "Wybierz metodę połączenia.",
"connectionChoiceUsbLabel": "USB", "connectionChoiceUsbLabel": "USB",
"usbScreenTitle": "Połączenie USB będzie dostępne wkrótce.", "usbScreenTitle": "Połącz przez USB",
"usbScreenSubtitle": "Tworzymy ścieżkę połączenia opartą na protokole szeregowym, przeznaczoną zarówno dla urządzeń z systemem Android, jak i dla komputerów stacjonarnych.", "usbScreenNote": "Port szeregowy USB jest aktywny na urządzeniach z Androidem i platformach stacjonarnych, które obsługują tę funkcję.",
"usbScreenStatus": "Wkrótce", "usbScreenSubtitle": "Wybierz wykryty urządzenie szeregowe i podłącz je bezpośrednio do swojego węzła MeshCore.",
"usbScreenNote": "Po wdrożeniu wsparcia dla USB, wybierzesz port szeregowy i połączysz się bezpośrednio z urządzeniem MeshCore.", "usbScreenStatus": "Wybierz urządzenie USB",
"usbScreenEmptyState": "Nie znaleziono żadnych urządzeń USB. Podłącz jedno i zaktualizuj." "usbScreenEmptyState": "Nie znaleziono żadnych urządzeń USB. Podłącz jedno i zaktualizuj."
} }

@ -1806,9 +1806,9 @@
"connectionChoiceUsbLabel": "USB", "connectionChoiceUsbLabel": "USB",
"connectionChoiceBluetoothLabel": "Bluetooth", "connectionChoiceBluetoothLabel": "Bluetooth",
"connectionChoiceTitle": "Escolha o método de conexão desejado.", "connectionChoiceTitle": "Escolha o método de conexão desejado.",
"usbScreenTitle": "A conexão USB estará disponível em breve.", "usbScreenNote": "A comunicação serial USB está ativa em dispositivos Android e plataformas de desktop compatíveis.",
"usbScreenSubtitle": "Estamos criando um caminho de conexão baseado em série para dispositivos Android e de desktop.", "usbScreenSubtitle": "Selecione o dispositivo serial detectado e conecte-o diretamente ao seu nó MeshCore.",
"usbScreenStatus": "Em breve", "usbScreenStatus": "Selecione um dispositivo USB",
"usbScreenNote": "Assim que o suporte USB for implementado, você poderá selecionar uma porta serial e conectar-se diretamente ao seu dispositivo MeshCore.", "usbScreenTitle": "Conecte via USB",
"usbScreenEmptyState": "Nenhum dispositivo USB encontrado. Conecte um e atualize." "usbScreenEmptyState": "Nenhum dispositivo USB encontrado. Conecte um e atualize."
} }

@ -1046,9 +1046,9 @@
"connectionChoiceTitle": "Выберите способ подключения", "connectionChoiceTitle": "Выберите способ подключения",
"connectionChoiceUsbLabel": "USB", "connectionChoiceUsbLabel": "USB",
"connectionChoiceBluetoothLabel": "Bluetooth", "connectionChoiceBluetoothLabel": "Bluetooth",
"usbScreenTitle": "Подключение через USB будет доступно в ближайшее время.", "usbScreenSubtitle": "Выберите обнаруженное устройство с последовательным интерфейсом и подключите его напрямую к вашему узлу MeshCore.",
"usbScreenSubtitle": "Мы создаем последовательную схему подключения для устройств на базе Android и настольных компьютеров.", "usbScreenNote": "USB-серийный порт активен на поддерживаемых устройствах Android и на настольных платформах.",
"usbScreenStatus": "Скоро", "usbScreenStatus": "Выберите USB-устройство",
"usbScreenNote": "Как только появится поддержка USB, вы сможете выбрать последовательный порт и напрямую подключиться к вашему устройству MeshCore.", "usbScreenTitle": "Подключение через USB",
"usbScreenEmptyState": "Не обнаружено никаких устройств USB. Подключите одно из них и обновите список." "usbScreenEmptyState": "Не обнаружено устройств USB. Подключите одно из них и обновите список."
} }

@ -1806,9 +1806,9 @@
"connectionChoiceUsbLabel": "USB", "connectionChoiceUsbLabel": "USB",
"connectionChoiceTitle": "Vyberte si metódu prepojenia.", "connectionChoiceTitle": "Vyberte si metódu prepojenia.",
"connectionChoiceSubtitle": "Vyberte si, ako chcete dosiahnuť váš zariadenie MeshCore.", "connectionChoiceSubtitle": "Vyberte si, ako chcete dosiahnuť váš zariadenie MeshCore.",
"usbScreenTitle": "Pripojenie cez USB bude k dispozícii čoskoro.", "usbScreenStatus": "Vyberte USB zariadenie",
"usbScreenSubtitle": "Vytvárajeme komunikačný systém založený na sériovej komunikácii pre Android a stolné počítače.", "usbScreenSubtitle": "Vyberte detekovaný sériový zariadenie a pripojte ho priamo k vašej MeshCore uzlu.",
"usbScreenStatus": "Čoskoro", "usbScreenNote": "USB sériová komunikácia je aktívna na podporovaných zariadeniach s Androidom a na desktopových platformách.",
"usbScreenNote": "Po implementácii podpory pre USB, budete môcť vybrať sériový port a priamo sa pripojiť k vašmu zariadeniu MeshCore.", "usbScreenTitle": "Pripojte cez USB",
"usbScreenEmptyState": "Nenašli sa žiadne USB zariadenia. Pripojte jedno a obnovte." "usbScreenEmptyState": "Nenašli sa žiadne USB zariadenia. Pripojte jedno a obnovte."
} }

@ -1806,9 +1806,9 @@
"connectionChoiceUsbLabel": "USB", "connectionChoiceUsbLabel": "USB",
"connectionChoiceTitle": "Izberite svoj način povezave.", "connectionChoiceTitle": "Izberite svoj način povezave.",
"connectionChoiceSubtitle": "Izberite, kako želite dostopati do svojega naprave MeshCore.", "connectionChoiceSubtitle": "Izberite, kako želite dostopati do svojega naprave MeshCore.",
"usbScreenTitle": "Vnos preko USB-ja bo v kratkem na voljo.", "usbScreenSubtitle": "Izberite zaznano serijsko napravo in se neposredno povežite z vašim MeshCore-om.",
"usbScreenSubtitle": "Gradimo pot za serijsko povezavo za Android in računalnike.", "usbScreenTitle": "Povežite preko USB",
"usbScreenStatus": "Čez kratko časa", "usbScreenStatus": "Izberite USB naprave.",
"usbScreenNote": "Ko bo podpora za USB na voljo, boste izbrali serijsko vrata in se neposredno povezali z vašim napravem MeshCore.", "usbScreenNote": "USB serijska povezava je aktivna na podprtih napravah Android in na desktop platformah.",
"usbScreenEmptyState": "Niti en USB naprave niso bilo najdeno. Povežite eno in posodobite." "usbScreenEmptyState": "Niti en USB naprave niso najdeni. Povežite eno in posodobite."
} }

@ -1806,9 +1806,9 @@
"connectionChoiceBluetoothLabel": "Bluetooth", "connectionChoiceBluetoothLabel": "Bluetooth",
"connectionChoiceSubtitle": "Välj hur du vill komma åt din MeshCore-enhet.", "connectionChoiceSubtitle": "Välj hur du vill komma åt din MeshCore-enhet.",
"connectionChoiceTitle": "Välj din anslutningsmetod", "connectionChoiceTitle": "Välj din anslutningsmetod",
"usbScreenTitle": "USB-anslutning kommer snart", "usbScreenTitle": "Anslut via USB",
"usbScreenSubtitle": "Vi skapar en seriebaserad anslutningsväg för både Android- och skrivbordsenheter.", "usbScreenNote": "USB-seriell kommunikation är aktiv på stöderliga Android-enheter och skrivbordsplattformar.",
"usbScreenStatus": "Kommer snart", "usbScreenSubtitle": "Välj en detekterad seriell enhet och anslut direkt till din MeshCore-nod.",
"usbScreenNote": "När USB-stöd är implementerat, kommer du att välja en seriell port och ansluta direkt till din MeshCore-enhet.", "usbScreenStatus": "Välj en USB-enhet",
"usbScreenEmptyState": "Inga USB-enheter hittades. Anslut en och uppdatera." "usbScreenEmptyState": "Inga USB-enheter hittades. Anslut en och uppdatera."
} }

@ -1806,9 +1806,9 @@
"connectionChoiceUsbLabel": "USB", "connectionChoiceUsbLabel": "USB",
"connectionChoiceTitle": "Виберіть спосіб зв'язку", "connectionChoiceTitle": "Виберіть спосіб зв'язку",
"connectionChoiceBluetoothLabel": "Bluetooth", "connectionChoiceBluetoothLabel": "Bluetooth",
"usbScreenTitle": "Підключення через USB буде доступне найближчим часом.", "usbScreenSubtitle": "Виберіть виявлене серійне пристрій і підключіть його безпосередньо до вашого вузла MeshCore.",
"usbScreenSubtitle": "Ми створюємо серійний шлях з'єднання для Android та десктопних комп'ютерів.", "usbScreenTitle": "Підключити через USB",
"usbScreenStatus": "Скоро", "usbScreenStatus": "Виберіть пристрій USB",
"usbScreenNote": "Після того, як буде реалізовано підтримку USB, ви виберете серійний порт і підключитесь безпосередньо до вашого пристрою MeshCore.", "usbScreenNote": "USB-серіальний інтерфейс активний на підтримуваних пристроях на базі Android та на десктопних платформах.",
"usbScreenEmptyState": "Не знайдено жодних пристроїв USB. Підключіть один і перезавантажте." "usbScreenEmptyState": "Не знайдено жодних пристроїв USB. Підключіть один і перезавантажте."
} }

@ -1811,9 +1811,9 @@
"connectionChoiceBluetoothLabel": "蓝牙", "connectionChoiceBluetoothLabel": "蓝牙",
"connectionChoiceTitle": "选择您的连接方式", "connectionChoiceTitle": "选择您的连接方式",
"connectionChoiceUsbLabel": "USB", "connectionChoiceUsbLabel": "USB",
"usbScreenTitle": "USB 连接即将推出", "usbScreenTitle": "通过USB连接",
"usbScreenSubtitle": "我们正在构建一个基于串行的连接路径用于Android和桌面设备。", "usbScreenSubtitle": "选择已检测到的串行设备,并直接连接到您的 MeshCore 节点。",
"usbScreenStatus": "即将推出", "usbScreenStatus": "选择一个 USB 设备",
"usbScreenNote": "一旦USB支持功能上线您就可以选择一个串口并直接连接到您的MeshCore设备。", "usbScreenNote": "在支持的 Android 设备和桌面平台上USB 串行通信功能已启用。",
"usbScreenEmptyState": "未找到任何 USB 设备。请插入一个,然后刷新。" "usbScreenEmptyState": "未找到任何 USB 设备。请插入一个,然后刷新。"
} }

@ -384,8 +384,15 @@ class _ContactsScreenState extends State<ContactsScreen>
Widget _buildContactsBody(BuildContext context, MeshCoreConnector connector) { Widget _buildContactsBody(BuildContext context, MeshCoreConnector connector) {
final contacts = connector.contacts; final contacts = connector.contacts;
final shouldShowStartupSpinner =
if (contacts.isEmpty && connector.isLoadingContacts && _groups.isEmpty) { contacts.isEmpty &&
_groups.isEmpty &&
connector.isConnected &&
(connector.isLoadingContacts ||
connector.isLoadingChannels ||
connector.selfPublicKey == null);
if (shouldShowStartupSpinner) {
return const Center(child: CircularProgressIndicator()); return const Center(child: CircularProgressIndicator());
} }

Loading…
Cancel
Save

Powered by TurnKey Linux.