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.
This commit is contained in:
just_stuff_tm 2026-03-02 02:34:46 -05:00 committed by just-stuff-tm
parent ee3af52c0f
commit 2d1160d992
31 changed files with 240 additions and 140 deletions

View file

@ -37,6 +37,7 @@ class MainActivity : FlutterActivity() {
private var usbConnection: UsbDeviceConnection? = null
private var usbPort: UsbSerialPort? = null
private var ioManager: SerialInputOutputManager? = null
private var connectedDeviceName: String? = null
private var pendingConnectResult: MethodChannel.Result? = null
private var pendingConnectPortName: String? = null
@ -45,7 +46,19 @@ class MainActivity : FlutterActivity() {
private val permissionReceiver =
object : BroadcastReceiver() {
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
}
@ -116,12 +129,19 @@ class MainActivity : FlutterActivity() {
override fun onDestroy() {
closeUsbConnection()
usbIoExecutor.shutdownNow()
unregisterReceiver(permissionReceiver)
try {
unregisterReceiver(permissionReceiver)
} catch (_: IllegalArgumentException) {
}
super.onDestroy()
}
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) {
registerReceiver(permissionReceiver, filter, RECEIVER_NOT_EXPORTED)
} else {
@ -256,6 +276,7 @@ class MainActivity : FlutterActivity() {
usbConnection = connection
usbPort = port
connectedDeviceName = device.deviceName
ioManager =
SerialInputOutputManager(
@ -311,6 +332,38 @@ class MainActivity : FlutterActivity() {
} catch (_: Exception) {
}
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 {

View file

@ -738,12 +738,18 @@ class MeshCoreConnector extends ChangeNotifier {
notifyListeners();
});
await FlutterBluePlus.startScan(
withKeywords: ["MeshCore-", "Whisper-"],
webOptionalServices: [Guid(MeshCoreUuids.service)],
timeout: timeout,
androidScanMode: AndroidScanMode.lowLatency,
);
try {
await FlutterBluePlus.startScan(
withKeywords: ["MeshCore-", "Whisper-"],
webOptionalServices: [Guid(MeshCoreUuids.service)],
timeout: timeout,
androidScanMode: AndroidScanMode.lowLatency,
);
} catch (error) {
debugPrint('[BLE Scan] Scan/picker failure: $error');
_setState(MeshCoreConnectionState.disconnected);
rethrow;
}
await Future.delayed(timeout);
await stopScan();
@ -791,17 +797,24 @@ class MeshCoreConnector extends ChangeNotifier {
notifyListeners();
try {
final connectLabel = _deviceDisplayName ?? _deviceId;
debugPrint('[BLE Connect] Starting connect to $connectLabel');
_connectionSubscription = device.connectionState.listen((state) {
if (state == BluetoothConnectionState.disconnected && isConnected) {
_handleDisconnection();
}
});
await device.connect(
timeout: const Duration(seconds: 15),
mtu: null,
license: License.free,
);
try {
await device.connect(
timeout: const Duration(seconds: 15),
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.
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;
for (var service in services) {
@ -847,6 +880,7 @@ class MeshCoreConnector extends ChangeNotifier {
try {
await _txCharacteristic!.setNotifyValue(true);
} catch (error) {
debugPrint('[BLE Connect] notify failure (web, ignored): $error');
debugPrint('Web setNotifyValue error (ignoring): $error');
}
}());
@ -861,6 +895,7 @@ class MeshCoreConnector extends ChangeNotifier {
await _txCharacteristic!.setNotifyValue(true);
notifySet = true;
} catch (e) {
debugPrint('[BLE Connect] notify failure: $e');
debugPrint('setNotifyValue attempt ${attempt + 1}/3 failed: $e');
if (attempt == 2) rethrow;
}
@ -1231,6 +1266,15 @@ class MeshCoreConnector extends ChangeNotifier {
_selfInfoRetryTimer?.cancel();
if (PlatformInfo.isWeb &&
_activeTransport == MeshCoreTransportType.bluetooth) {
_selfInfoRetryTimer = Timer(const Duration(seconds: 10), () {
if (!isConnected || !_awaitingSelfInfo) {
return;
}
if (_isLoadingContacts || _isSyncingChannels || _channelSyncInFlight) {
return;
}
unawaited(sendFrame(buildAppStartFrame()));
});
return;
}
_selfInfoRetryTimer = Timer.periodic(const Duration(milliseconds: 3500), (

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -123,18 +123,18 @@ class AppLocalizationsDe extends AppLocalizations {
String get connectionChoiceBluetoothLabel => 'Bluetooth';
@override
String get usbScreenTitle => 'Die USB-Verbindung wird bald verfügbar sein.';
String get usbScreenTitle => 'Über USB verbinden';
@override
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
String get usbScreenStatus => 'Bald verfügbar';
String get usbScreenStatus => 'Wählen Sie ein USB-Gerät aus';
@override
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
String get usbScreenEmptyState =>

View file

@ -122,23 +122,22 @@ class AppLocalizationsEs extends AppLocalizations {
String get connectionChoiceBluetoothLabel => 'Bluetooth';
@override
String get usbScreenTitle =>
'La conexión USB estará disponible próximamente.';
String get usbScreenTitle => 'Conecte mediante USB';
@override
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
String get usbScreenStatus => 'Próximamente';
String get usbScreenStatus => 'Seleccione un dispositivo USB';
@override
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
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
String get scanner_scanning => 'Escaneando dispositivos...';

View file

@ -122,23 +122,22 @@ class AppLocalizationsFr extends AppLocalizations {
String get connectionChoiceBluetoothLabel => 'Bluetooth';
@override
String get usbScreenTitle =>
'La connexion USB sera disponible prochainement.';
String get usbScreenTitle => 'Connectez via USB';
@override
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
String get usbScreenStatus => 'Bientôt';
String get usbScreenStatus => 'Sélectionnez un périphérique USB';
@override
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
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
String get scanner_scanning => 'Recherche de périphériques...';

View file

@ -123,18 +123,18 @@ class AppLocalizationsIt extends AppLocalizations {
String get connectionChoiceBluetoothLabel => 'Bluetooth';
@override
String get usbScreenTitle => 'La connessione USB sarà disponibile a breve.';
String get usbScreenTitle => 'Connessione tramite USB';
@override
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
String get usbScreenStatus => 'Arriverà presto';
String get usbScreenStatus => 'Seleziona un dispositivo USB';
@override
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
String get usbScreenEmptyState =>

View file

@ -122,18 +122,18 @@ class AppLocalizationsNl extends AppLocalizations {
String get connectionChoiceBluetoothLabel => 'Bluetooth';
@override
String get usbScreenTitle => 'USB-verbinding is binnenkort beschikbaar.';
String get usbScreenTitle => 'Verbind via USB';
@override
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
String get usbScreenStatus => 'Komende week';
String get usbScreenStatus => 'Selecteer een USB-apparaat';
@override
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
String get usbScreenEmptyState =>

View file

@ -122,18 +122,18 @@ class AppLocalizationsPl extends AppLocalizations {
String get connectionChoiceBluetoothLabel => 'Bluetooth';
@override
String get usbScreenTitle => 'Połączenie USB będzie dostępne wkrótce.';
String get usbScreenTitle => 'Połącz przez USB';
@override
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
String get usbScreenStatus => 'Wkrótce';
String get usbScreenStatus => 'Wybierz urządzenie USB';
@override
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
String get usbScreenEmptyState =>

View file

@ -122,18 +122,18 @@ class AppLocalizationsPt extends AppLocalizations {
String get connectionChoiceBluetoothLabel => 'Bluetooth';
@override
String get usbScreenTitle => 'A conexão USB estará disponível em breve.';
String get usbScreenTitle => 'Conecte via USB';
@override
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
String get usbScreenStatus => 'Em breve';
String get usbScreenStatus => 'Selecione um dispositivo USB';
@override
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
String get usbScreenEmptyState =>

View file

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

View file

@ -122,18 +122,18 @@ class AppLocalizationsSk extends AppLocalizations {
String get connectionChoiceBluetoothLabel => 'Bluetooth';
@override
String get usbScreenTitle => 'Pripojenie cez USB bude k dispozícii čoskoro.';
String get usbScreenTitle => 'Pripojte cez USB';
@override
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
String get usbScreenStatus => 'Čoskoro';
String get usbScreenStatus => 'Vyberte USB zariadenie';
@override
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
String get usbScreenEmptyState =>

View file

@ -122,22 +122,22 @@ class AppLocalizationsSl extends AppLocalizations {
String get connectionChoiceBluetoothLabel => 'Bluetooth';
@override
String get usbScreenTitle => 'Vnos preko USB-ja bo v kratkem na voljo.';
String get usbScreenTitle => 'Povežite preko USB';
@override
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
String get usbScreenStatus => 'Čez kratko časa';
String get usbScreenStatus => 'Izberite USB naprave.';
@override
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
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
String get scanner_scanning => 'Skeniram za naprave...';

View file

@ -122,18 +122,18 @@ class AppLocalizationsSv extends AppLocalizations {
String get connectionChoiceBluetoothLabel => 'Bluetooth';
@override
String get usbScreenTitle => 'USB-anslutning kommer snart';
String get usbScreenTitle => 'Anslut via USB';
@override
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
String get usbScreenStatus => 'Kommer snart';
String get usbScreenStatus => 'Välj en USB-enhet';
@override
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
String get usbScreenEmptyState =>

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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