mirror of
https://github.com/zjs81/meshcore-open.git
synced 2026-04-20 22:13:48 +00:00
Merge remote-tracking branch 'origin/main' into enhancement/bluetooth-disabled-warning
This commit is contained in:
commit
739d9475c0
36 changed files with 807 additions and 222 deletions
|
|
@ -1575,7 +1575,6 @@
|
|||
"notification_newNodesCount": "{count} {count, plural, =1{нов възел} other{нови възли}}",
|
||||
"notification_newTypeDiscovered": "Открит нов {contactType}",
|
||||
"notification_receivedNewMessage": "Получено ново съобщение",
|
||||
"contacts_contactAdvertCopyFailed": "Копирането на обявата в клипборда не успя.",
|
||||
"settings_gpxExportContactsSubtitle": "Експортира спътници с местоположение в GPX файл.",
|
||||
"settings_gpxExportRepeatersSubtitle": "Изпраща повторители / roomserver с местоположение в GPX файл.",
|
||||
"settings_gpxExportAll": "Експортирай всички контакти в GPX",
|
||||
|
|
@ -1591,6 +1590,10 @@
|
|||
"settings_gpxExportAllContacts": "Местоположения на всички контакти",
|
||||
"settings_gpxExportShareText": "Картинни данни изнесени от meshcore-open",
|
||||
"settings_gpxExportShareSubject": "meshcore-open износ на данни за карта в формат GPX",
|
||||
"pathTrace_someHopsNoLocation": "Един или повече от хмелите липсва местоположение!"
|
||||
|
||||
"pathTrace_someHopsNoLocation": "Един или повече от хмелите липсва местоположение!",
|
||||
"map_pathTraceCancelled": "Отменен е следването на пътя.",
|
||||
"pathTrace_clearTooltip": "Изчисти пътя",
|
||||
"map_removeLast": "Премахни Последно",
|
||||
"map_runTrace": "Изпълни Път на Следване",
|
||||
"map_tapToAdd": "Натиснете върху възлите, за да ги добавите към пътя."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1569,34 +1569,40 @@
|
|||
"contacts_zeroHopContactAdvertSent": "Kontakt über Anzeige gesendet",
|
||||
"contacts_contactAdvertCopied": "Anzeige in die Zwischenablage kopiert.",
|
||||
"contacts_contactAdvertCopyFailed": "Kopieren der Ankündigung in die Zwischenablage fehlgeschlagen.",
|
||||
|
||||
"notification_activityTitle": "MeshCore Aktivität",
|
||||
"notification_messagesCount": "{count} {count, plural, =1{Nachricht} other{Nachrichten}}",
|
||||
"@notification_messagesCount": {
|
||||
"placeholders": {
|
||||
"count": {"type": "int"}
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"notification_channelMessagesCount": "{count} {count, plural, =1{Kanalnachricht} other{Kanalnachrichten}}",
|
||||
"@notification_channelMessagesCount": {
|
||||
"placeholders": {
|
||||
"count": {"type": "int"}
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"notification_newNodesCount": "{count} {count, plural, =1{neuer Knoten} other{neue Knoten}}",
|
||||
"@notification_newNodesCount": {
|
||||
"placeholders": {
|
||||
"count": {"type": "int"}
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"notification_newTypeDiscovered": "Neuer {contactType} entdeckt",
|
||||
"@notification_newTypeDiscovered": {
|
||||
"placeholders": {
|
||||
"contactType": {"type": "String"}
|
||||
"contactType": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"notification_receivedNewMessage": "Neue Nachricht empfangen",
|
||||
"contacts_contactAdvertCopyFailed": "Kopieren der Ankündigung in die Zwischenablage fehlgeschlagen.",
|
||||
"settings_gpxExportAll": "Alle Knoten als GPX exportieren",
|
||||
"settings_gpxExportAllSubtitle": "Exportiert alle Knoten mit einem Standort in eine GPX-Datei.",
|
||||
"settings_gpxExportRepeaters": "Repeater und Raumserver als GPX exportieren",
|
||||
|
|
@ -1612,6 +1618,10 @@
|
|||
"settings_gpxExportAllContacts": "Alle Kontaktstandorte",
|
||||
"settings_gpxExportShareSubject": "GPX-Kartendaten aus meshcore-open exportieren",
|
||||
"settings_gpxExportShareText": "GPX-Kartendaten aus meshcore-open exportiert",
|
||||
"pathTrace_someHopsNoLocation": "Bei einer oder mehreren Knoten fehlt der Standort!"
|
||||
|
||||
"pathTrace_someHopsNoLocation": "Bei einer oder mehreren Knoten fehlt der Standort!",
|
||||
"map_removeLast": "Letztes Entfernen",
|
||||
"map_tapToAdd": "Tippen Sie auf Knoten, um sie zum Pfad hinzuzufügen.",
|
||||
"map_runTrace": "Pfadverlauf ausführen",
|
||||
"pathTrace_clearTooltip": "Pfad löschen",
|
||||
"map_pathTraceCancelled": "Pfadverfolgung abgebrochen."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -621,6 +621,10 @@
|
|||
"map_sharedPin": "Shared pin",
|
||||
"map_joinRoom": "Join Room",
|
||||
"map_manageRepeater": "Manage Repeater",
|
||||
"map_tapToAdd": "Tap on nodes to add them to the path.",
|
||||
"map_runTrace": "Run Path Trace",
|
||||
"map_removeLast": "Remove Last",
|
||||
"map_pathTraceCancelled": "Path trace cancelled.",
|
||||
"mapCache_title": "Offline Map Cache",
|
||||
"mapCache_selectAreaFirst": "Select an area to cache first",
|
||||
"mapCache_noTilesToDownload": "No tiles to download for this area",
|
||||
|
|
@ -1319,6 +1323,7 @@
|
|||
"pathTrace_notAvailable": "Path trace not available.",
|
||||
"pathTrace_refreshTooltip": "Refresh Path Trace.",
|
||||
"pathTrace_someHopsNoLocation": "One or more of the hops is missing a location!",
|
||||
"pathTrace_clearTooltip": "Clear path.",
|
||||
"contacts_pathTrace": "Path Trace",
|
||||
"contacts_ping": "Ping",
|
||||
"contacts_repeaterPathTrace": "Path trace to repeater",
|
||||
|
|
|
|||
|
|
@ -1569,34 +1569,40 @@
|
|||
"contacts_zeroHopContactAdvertSent": "Envió contacto por anuncio.",
|
||||
"contacts_contactAdvertCopied": "Anuncio copiado al Portapapeles.",
|
||||
"contacts_contactAdvertCopyFailed": "Copiar anuncio al Portapapeles ha fallado.",
|
||||
|
||||
"notification_activityTitle": "Actividad de MeshCore",
|
||||
"notification_messagesCount": "{count} {count, plural, =1{mensaje} other{mensajes}}",
|
||||
"@notification_messagesCount": {
|
||||
"placeholders": {
|
||||
"count": {"type": "int"}
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"notification_channelMessagesCount": "{count} {count, plural, =1{mensaje de canal} other{mensajes de canal}}",
|
||||
"@notification_channelMessagesCount": {
|
||||
"placeholders": {
|
||||
"count": {"type": "int"}
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"notification_newNodesCount": "{count} {count, plural, =1{nuevo nodo} other{nuevos nodos}}",
|
||||
"@notification_newNodesCount": {
|
||||
"placeholders": {
|
||||
"count": {"type": "int"}
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"notification_newTypeDiscovered": "Nuevo {contactType} descubierto",
|
||||
"@notification_newTypeDiscovered": {
|
||||
"placeholders": {
|
||||
"contactType": {"type": "String"}
|
||||
"contactType": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"notification_receivedNewMessage": "Nuevo mensaje recibido",
|
||||
"contacts_contactAdvertCopyFailed": "Copiar anuncio al Portapapeles ha fallado.",
|
||||
"settings_gpxExportContactsSubtitle": "Exporta compañeros con una ubicación a archivo GPX.",
|
||||
"settings_gpxExportRepeaters": "Exportar repetidores / servidor de sala a GPX",
|
||||
"settings_gpxExportSuccess": "Archivo GPX exportado con éxito.",
|
||||
|
|
@ -1612,6 +1618,10 @@
|
|||
"settings_gpxExportAllContacts": "Todas las ubicaciones de contactos",
|
||||
"settings_gpxExportShareText": "Datos del mapa exportados desde meshcore-open",
|
||||
"settings_gpxExportShareSubject": "meshcore-open exportación de datos de mapa GPX",
|
||||
"pathTrace_someHopsNoLocation": "Uno o más de los lúpulos carecen de una ubicación"
|
||||
|
||||
"pathTrace_someHopsNoLocation": "Uno o más de los lúpulos carecen de una ubicación",
|
||||
"pathTrace_clearTooltip": "Borrar ruta",
|
||||
"map_runTrace": "Ejecutar Rastreo de Ruta",
|
||||
"map_tapToAdd": "Pulse en los nodos para agregarlos al camino.",
|
||||
"map_removeLast": "Eliminar último",
|
||||
"map_pathTraceCancelled": "Rastreo de ruta cancelado."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1590,5 +1590,10 @@
|
|||
"settings_gpxExportAllContacts": "Tous les emplacements des contacts",
|
||||
"settings_gpxExportShareText": "Données de carte exportées à partir de meshcore-open",
|
||||
"settings_gpxExportShareSubject": "meshcore-open exporter les données de carte GPX",
|
||||
"pathTrace_someHopsNoLocation": "Un ou plusieurs des sauts manquent d'une localisation !"
|
||||
"pathTrace_someHopsNoLocation": "Un ou plusieurs des sauts manquent d'une localisation !",
|
||||
"map_tapToAdd": "Appuyez sur les nœuds pour les ajouter au chemin.",
|
||||
"pathTrace_clearTooltip": "Effacer le chemin",
|
||||
"map_pathTraceCancelled": "Traçage de chemin annulé",
|
||||
"map_removeLast": "Supprimer le dernier",
|
||||
"map_runTrace": "Exécuter la traçage de chemin"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1575,7 +1575,6 @@
|
|||
"notification_newNodesCount": "{count} {count, plural, =1{nuovo nodo} other{nuovi nodi}}",
|
||||
"notification_newTypeDiscovered": "Nuovo {contactType} scoperto",
|
||||
"notification_receivedNewMessage": "Nuovo messaggio ricevuto",
|
||||
"contacts_contactAdvertCopied": "Annuncio copiato negli Appunti.",
|
||||
"settings_gpxExportRepeaters": "Esporta ripetitori / server di stanza in GPX",
|
||||
"settings_gpxExportContacts": "Esporta compagni in GPX",
|
||||
"settings_gpxExportSuccess": "Esportazione del file GPX completata con successo.",
|
||||
|
|
@ -1591,6 +1590,10 @@
|
|||
"settings_gpxExportAllContacts": "Tutte le posizioni dei contatti",
|
||||
"settings_gpxExportShareText": "Dati mappa esportati da meshcore-open",
|
||||
"settings_gpxExportShareSubject": "meshcore-open esportazione dati mappa GPX",
|
||||
"pathTrace_someHopsNoLocation": "Uno o più dei luppoli mancano di una posizione!"
|
||||
|
||||
"pathTrace_someHopsNoLocation": "Uno o più dei luppoli mancano di una posizione!",
|
||||
"map_removeLast": "Rimuovi ultimo",
|
||||
"map_pathTraceCancelled": "Tracciamento del percorso annullato.",
|
||||
"pathTrace_clearTooltip": "Pulisci percorso",
|
||||
"map_runTrace": "Esegui Path Trace",
|
||||
"map_tapToAdd": "Tocca i nodi per aggiungerli al percorso."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2518,6 +2518,30 @@ abstract class AppLocalizations {
|
|||
/// **'Manage Repeater'**
|
||||
String get map_manageRepeater;
|
||||
|
||||
/// No description provided for @map_tapToAdd.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Tap on nodes to add them to the path.'**
|
||||
String get map_tapToAdd;
|
||||
|
||||
/// No description provided for @map_runTrace.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Run Path Trace'**
|
||||
String get map_runTrace;
|
||||
|
||||
/// No description provided for @map_removeLast.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Remove Last'**
|
||||
String get map_removeLast;
|
||||
|
||||
/// No description provided for @map_pathTraceCancelled.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Path trace cancelled.'**
|
||||
String get map_pathTraceCancelled;
|
||||
|
||||
/// No description provided for @mapCache_title.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
|
@ -4730,6 +4754,12 @@ abstract class AppLocalizations {
|
|||
/// **'One or more of the hops is missing a location!'**
|
||||
String get pathTrace_someHopsNoLocation;
|
||||
|
||||
/// No description provided for @pathTrace_clearTooltip.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Clear path.'**
|
||||
String get pathTrace_clearTooltip;
|
||||
|
||||
/// No description provided for @contacts_pathTrace.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
|
|
|||
|
|
@ -1363,6 +1363,19 @@ class AppLocalizationsBg extends AppLocalizations {
|
|||
@override
|
||||
String get map_manageRepeater => 'Управление на Повтарящ се Елемент';
|
||||
|
||||
@override
|
||||
String get map_tapToAdd =>
|
||||
'Натиснете върху възлите, за да ги добавите към пътя.';
|
||||
|
||||
@override
|
||||
String get map_runTrace => 'Изпълни Път на Следване';
|
||||
|
||||
@override
|
||||
String get map_removeLast => 'Премахни Последно';
|
||||
|
||||
@override
|
||||
String get map_pathTraceCancelled => 'Отменен е следването на пътя.';
|
||||
|
||||
@override
|
||||
String get mapCache_title => 'Кеш на офлайн карти';
|
||||
|
||||
|
|
@ -2699,6 +2712,9 @@ class AppLocalizationsBg extends AppLocalizations {
|
|||
String get pathTrace_someHopsNoLocation =>
|
||||
'Един или повече от хмелите липсва местоположение!';
|
||||
|
||||
@override
|
||||
String get pathTrace_clearTooltip => 'Изчисти пътя';
|
||||
|
||||
@override
|
||||
String get contacts_pathTrace => 'Пътен проследяване';
|
||||
|
||||
|
|
|
|||
|
|
@ -1362,6 +1362,19 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
@override
|
||||
String get map_manageRepeater => 'Repeater verwalten';
|
||||
|
||||
@override
|
||||
String get map_tapToAdd =>
|
||||
'Tippen Sie auf Knoten, um sie zum Pfad hinzuzufügen.';
|
||||
|
||||
@override
|
||||
String get map_runTrace => 'Pfadverlauf ausführen';
|
||||
|
||||
@override
|
||||
String get map_removeLast => 'Letztes Entfernen';
|
||||
|
||||
@override
|
||||
String get map_pathTraceCancelled => 'Pfadverfolgung abgebrochen.';
|
||||
|
||||
@override
|
||||
String get mapCache_title => 'Offline-Karten-Cache';
|
||||
|
||||
|
|
@ -2704,6 +2717,9 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
String get pathTrace_someHopsNoLocation =>
|
||||
'Bei einer oder mehreren Knoten fehlt der Standort!';
|
||||
|
||||
@override
|
||||
String get pathTrace_clearTooltip => 'Pfad löschen';
|
||||
|
||||
@override
|
||||
String get contacts_pathTrace => 'Pfadverfolgung';
|
||||
|
||||
|
|
|
|||
|
|
@ -1342,6 +1342,18 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
@override
|
||||
String get map_manageRepeater => 'Manage Repeater';
|
||||
|
||||
@override
|
||||
String get map_tapToAdd => 'Tap on nodes to add them to the path.';
|
||||
|
||||
@override
|
||||
String get map_runTrace => 'Run Path Trace';
|
||||
|
||||
@override
|
||||
String get map_removeLast => 'Remove Last';
|
||||
|
||||
@override
|
||||
String get map_pathTraceCancelled => 'Path trace cancelled.';
|
||||
|
||||
@override
|
||||
String get mapCache_title => 'Offline Map Cache';
|
||||
|
||||
|
|
@ -2659,6 +2671,9 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
String get pathTrace_someHopsNoLocation =>
|
||||
'One or more of the hops is missing a location!';
|
||||
|
||||
@override
|
||||
String get pathTrace_clearTooltip => 'Clear path.';
|
||||
|
||||
@override
|
||||
String get contacts_pathTrace => 'Path Trace';
|
||||
|
||||
|
|
|
|||
|
|
@ -1360,6 +1360,18 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||
@override
|
||||
String get map_manageRepeater => 'Gestionar Repetidor';
|
||||
|
||||
@override
|
||||
String get map_tapToAdd => 'Pulse en los nodos para agregarlos al camino.';
|
||||
|
||||
@override
|
||||
String get map_runTrace => 'Ejecutar Rastreo de Ruta';
|
||||
|
||||
@override
|
||||
String get map_removeLast => 'Eliminar último';
|
||||
|
||||
@override
|
||||
String get map_pathTraceCancelled => 'Rastreo de ruta cancelado.';
|
||||
|
||||
@override
|
||||
String get mapCache_title => 'Caché de Mapa Offline';
|
||||
|
||||
|
|
@ -2698,6 +2710,9 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||
String get pathTrace_someHopsNoLocation =>
|
||||
'Uno o más de los lúpulos carecen de una ubicación';
|
||||
|
||||
@override
|
||||
String get pathTrace_clearTooltip => 'Borrar ruta';
|
||||
|
||||
@override
|
||||
String get contacts_pathTrace => 'Rastreo de caminos';
|
||||
|
||||
|
|
|
|||
|
|
@ -1367,6 +1367,19 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||
@override
|
||||
String get map_manageRepeater => 'Gérer le répéteur';
|
||||
|
||||
@override
|
||||
String get map_tapToAdd =>
|
||||
'Appuyez sur les nœuds pour les ajouter au chemin.';
|
||||
|
||||
@override
|
||||
String get map_runTrace => 'Exécuter la traçage de chemin';
|
||||
|
||||
@override
|
||||
String get map_removeLast => 'Supprimer le dernier';
|
||||
|
||||
@override
|
||||
String get map_pathTraceCancelled => 'Traçage de chemin annulé';
|
||||
|
||||
@override
|
||||
String get mapCache_title => 'Cache de Carte Hors Ligne';
|
||||
|
||||
|
|
@ -2714,6 +2727,9 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||
String get pathTrace_someHopsNoLocation =>
|
||||
'Un ou plusieurs des sauts manquent d\'une localisation !';
|
||||
|
||||
@override
|
||||
String get pathTrace_clearTooltip => 'Effacer le chemin';
|
||||
|
||||
@override
|
||||
String get contacts_pathTrace => 'Traçage de chemin';
|
||||
|
||||
|
|
|
|||
|
|
@ -1359,6 +1359,18 @@ class AppLocalizationsIt extends AppLocalizations {
|
|||
@override
|
||||
String get map_manageRepeater => 'Gestisci Ripetitore';
|
||||
|
||||
@override
|
||||
String get map_tapToAdd => 'Tocca i nodi per aggiungerli al percorso.';
|
||||
|
||||
@override
|
||||
String get map_runTrace => 'Esegui Path Trace';
|
||||
|
||||
@override
|
||||
String get map_removeLast => 'Rimuovi ultimo';
|
||||
|
||||
@override
|
||||
String get map_pathTraceCancelled => 'Tracciamento del percorso annullato.';
|
||||
|
||||
@override
|
||||
String get mapCache_title => 'Cache Mappa Offline';
|
||||
|
||||
|
|
@ -2699,6 +2711,9 @@ class AppLocalizationsIt extends AppLocalizations {
|
|||
String get pathTrace_someHopsNoLocation =>
|
||||
'Uno o più dei luppoli mancano di una posizione!';
|
||||
|
||||
@override
|
||||
String get pathTrace_clearTooltip => 'Pulisci percorso';
|
||||
|
||||
@override
|
||||
String get contacts_pathTrace => 'Traccia Percorso';
|
||||
|
||||
|
|
|
|||
|
|
@ -1355,6 +1355,19 @@ class AppLocalizationsNl extends AppLocalizations {
|
|||
@override
|
||||
String get map_manageRepeater => 'Beheer Repeater';
|
||||
|
||||
@override
|
||||
String get map_tapToAdd =>
|
||||
'Tik op knooppunten om ze toe te voegen aan het pad';
|
||||
|
||||
@override
|
||||
String get map_runTrace => 'Padeshulp traceren';
|
||||
|
||||
@override
|
||||
String get map_removeLast => 'Verwijder Laatste';
|
||||
|
||||
@override
|
||||
String get map_pathTraceCancelled => 'Pad traceren geannuleerd';
|
||||
|
||||
@override
|
||||
String get mapCache_title => 'Offline Kaarten Cache';
|
||||
|
||||
|
|
@ -2689,6 +2702,9 @@ class AppLocalizationsNl extends AppLocalizations {
|
|||
String get pathTrace_someHopsNoLocation =>
|
||||
'Een of meer van de hops ontbreken een locatie!';
|
||||
|
||||
@override
|
||||
String get pathTrace_clearTooltip => 'Weg wissen';
|
||||
|
||||
@override
|
||||
String get contacts_pathTrace => 'Pad Traceren';
|
||||
|
||||
|
|
|
|||
|
|
@ -1361,6 +1361,18 @@ class AppLocalizationsPl extends AppLocalizations {
|
|||
@override
|
||||
String get map_manageRepeater => 'Zarządzaj Powtórzami';
|
||||
|
||||
@override
|
||||
String get map_tapToAdd => 'Kliknij na węzły, aby dodać je do ścieżki.';
|
||||
|
||||
@override
|
||||
String get map_runTrace => 'Uruchom ślad ścieżki';
|
||||
|
||||
@override
|
||||
String get map_removeLast => 'Usuń ostatni';
|
||||
|
||||
@override
|
||||
String get map_pathTraceCancelled => 'Śledzenie ścieżki anulowano.';
|
||||
|
||||
@override
|
||||
String get mapCache_title => 'Bufor Map Offline';
|
||||
|
||||
|
|
@ -2697,6 +2709,9 @@ class AppLocalizationsPl extends AppLocalizations {
|
|||
String get pathTrace_someHopsNoLocation =>
|
||||
'Jeden lub więcej z chmieli nie ma określonej lokalizacji!';
|
||||
|
||||
@override
|
||||
String get pathTrace_clearTooltip => 'Wyczyść ścieżkę';
|
||||
|
||||
@override
|
||||
String get contacts_pathTrace => 'Śledzenie Ścieżek';
|
||||
|
||||
|
|
|
|||
|
|
@ -1361,6 +1361,18 @@ class AppLocalizationsPt extends AppLocalizations {
|
|||
@override
|
||||
String get map_manageRepeater => 'Gerenciar Repetidor';
|
||||
|
||||
@override
|
||||
String get map_tapToAdd => 'Toque nos nós para adicioná-los ao caminho.';
|
||||
|
||||
@override
|
||||
String get map_runTrace => 'Executar Traçado de Caminho';
|
||||
|
||||
@override
|
||||
String get map_removeLast => 'Remover Último';
|
||||
|
||||
@override
|
||||
String get map_pathTraceCancelled => 'Rastreamento de caminho cancelado.';
|
||||
|
||||
@override
|
||||
String get mapCache_title => 'Cache de Mapa Offline';
|
||||
|
||||
|
|
@ -2700,6 +2712,9 @@ class AppLocalizationsPt extends AppLocalizations {
|
|||
String get pathTrace_someHopsNoLocation =>
|
||||
'Um ou mais dos lúpulos estão sem localização!';
|
||||
|
||||
@override
|
||||
String get pathTrace_clearTooltip => 'Limpar caminho';
|
||||
|
||||
@override
|
||||
String get contacts_pathTrace => 'Traçado de Caminho';
|
||||
|
||||
|
|
|
|||
|
|
@ -1362,6 +1362,18 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||
@override
|
||||
String get map_manageRepeater => 'Управление репитером';
|
||||
|
||||
@override
|
||||
String get map_tapToAdd => 'Нажимайте на узлы, чтобы добавить их в путь.';
|
||||
|
||||
@override
|
||||
String get map_runTrace => 'Запустить трассировку пути';
|
||||
|
||||
@override
|
||||
String get map_removeLast => 'Удалить последний';
|
||||
|
||||
@override
|
||||
String get map_pathTraceCancelled => 'Отмена трассировки пути';
|
||||
|
||||
@override
|
||||
String get mapCache_title => 'Кэш офлайн-карты';
|
||||
|
||||
|
|
@ -2702,6 +2714,9 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||
String get pathTrace_someHopsNoLocation =>
|
||||
'Одному или нескольким хмелям не указано местоположение!';
|
||||
|
||||
@override
|
||||
String get pathTrace_clearTooltip => 'Очистить путь';
|
||||
|
||||
@override
|
||||
String get contacts_pathTrace => 'Трассировка пути';
|
||||
|
||||
|
|
|
|||
|
|
@ -1356,6 +1356,18 @@ class AppLocalizationsSk extends AppLocalizations {
|
|||
@override
|
||||
String get map_manageRepeater => 'Spravovať Opakovanie';
|
||||
|
||||
@override
|
||||
String get map_tapToAdd => 'Kliknite na uzly, aby ste ich pridali k ceste.';
|
||||
|
||||
@override
|
||||
String get map_runTrace => 'Spustiť trasovaním cesty';
|
||||
|
||||
@override
|
||||
String get map_removeLast => 'Odstrániť posledný';
|
||||
|
||||
@override
|
||||
String get map_pathTraceCancelled => 'Zrušenie stopáže cesty bolo zrušené.';
|
||||
|
||||
@override
|
||||
String get mapCache_title => 'Offline Mapa Pamäť';
|
||||
|
||||
|
|
@ -2685,6 +2697,9 @@ class AppLocalizationsSk extends AppLocalizations {
|
|||
String get pathTrace_someHopsNoLocation =>
|
||||
'Jedna alebo viac chmeľov chýba lokalita!';
|
||||
|
||||
@override
|
||||
String get pathTrace_clearTooltip => 'Zmazať cestu';
|
||||
|
||||
@override
|
||||
String get contacts_pathTrace => 'Sledovanie lúčov';
|
||||
|
||||
|
|
|
|||
|
|
@ -1352,6 +1352,18 @@ class AppLocalizationsSl extends AppLocalizations {
|
|||
@override
|
||||
String get map_manageRepeater => 'Upravljajte Ponovitve';
|
||||
|
||||
@override
|
||||
String get map_tapToAdd => 'Pritisnite na vozlišča, da jih dodate poti.';
|
||||
|
||||
@override
|
||||
String get map_runTrace => 'Zaženi sledenje poti';
|
||||
|
||||
@override
|
||||
String get map_removeLast => 'Odstrani Zadnji';
|
||||
|
||||
@override
|
||||
String get map_pathTraceCancelled => 'Spremljanje poti je prekinjeno.';
|
||||
|
||||
@override
|
||||
String get mapCache_title =>
|
||||
'Omrezni predpomnilnik zemljeških zemljejevskih slik';
|
||||
|
|
@ -2688,6 +2700,9 @@ class AppLocalizationsSl extends AppLocalizations {
|
|||
String get pathTrace_someHopsNoLocation =>
|
||||
'Ena ali več hmelju manjka lokacija!';
|
||||
|
||||
@override
|
||||
String get pathTrace_clearTooltip => 'Počisti pot';
|
||||
|
||||
@override
|
||||
String get contacts_pathTrace => 'Sledenje poti';
|
||||
|
||||
|
|
|
|||
|
|
@ -1348,6 +1348,18 @@ class AppLocalizationsSv extends AppLocalizations {
|
|||
@override
|
||||
String get map_manageRepeater => 'Hantera Upprepare';
|
||||
|
||||
@override
|
||||
String get map_tapToAdd => 'Tryck på noder för att lägga till dem i banan.';
|
||||
|
||||
@override
|
||||
String get map_runTrace => 'Kör spårsökning';
|
||||
|
||||
@override
|
||||
String get map_removeLast => 'Ta bort sista';
|
||||
|
||||
@override
|
||||
String get map_pathTraceCancelled => 'Sökvägsspårning avbruten.';
|
||||
|
||||
@override
|
||||
String get mapCache_title => 'Offline Kartcache';
|
||||
|
||||
|
|
@ -2673,6 +2685,9 @@ class AppLocalizationsSv extends AppLocalizations {
|
|||
String get pathTrace_someHopsNoLocation =>
|
||||
'En eller flera av humlen saknar en plats!';
|
||||
|
||||
@override
|
||||
String get pathTrace_clearTooltip => 'Rensa väg';
|
||||
|
||||
@override
|
||||
String get contacts_pathTrace => 'Path Trace';
|
||||
|
||||
|
|
|
|||
|
|
@ -1361,6 +1361,18 @@ class AppLocalizationsUk extends AppLocalizations {
|
|||
@override
|
||||
String get map_manageRepeater => 'Керувати ретранслятором';
|
||||
|
||||
@override
|
||||
String get map_tapToAdd => 'Натисніть на вузли, щоб додати їх до шляху';
|
||||
|
||||
@override
|
||||
String get map_runTrace => 'Виконати трасування шляху';
|
||||
|
||||
@override
|
||||
String get map_removeLast => 'Видалити останній';
|
||||
|
||||
@override
|
||||
String get map_pathTraceCancelled => 'Відмінується трасування шляху';
|
||||
|
||||
@override
|
||||
String get mapCache_title => 'Офлайн-кеш карти';
|
||||
|
||||
|
|
@ -2709,6 +2721,9 @@ class AppLocalizationsUk extends AppLocalizations {
|
|||
String get pathTrace_someHopsNoLocation =>
|
||||
'Одне або більше хмелів відсутнє місце розташування!';
|
||||
|
||||
@override
|
||||
String get pathTrace_clearTooltip => 'Очистити шлях';
|
||||
|
||||
@override
|
||||
String get contacts_pathTrace => 'Трасування шляхів';
|
||||
|
||||
|
|
|
|||
|
|
@ -1301,6 +1301,18 @@ class AppLocalizationsZh extends AppLocalizations {
|
|||
@override
|
||||
String get map_manageRepeater => '管理重复器';
|
||||
|
||||
@override
|
||||
String get map_tapToAdd => '点击节点将其添加到路径中';
|
||||
|
||||
@override
|
||||
String get map_runTrace => '运行路径跟踪';
|
||||
|
||||
@override
|
||||
String get map_removeLast => '删除最后一个';
|
||||
|
||||
@override
|
||||
String get map_pathTraceCancelled => '路径跟踪已取消';
|
||||
|
||||
@override
|
||||
String get mapCache_title => '离线地图缓存';
|
||||
|
||||
|
|
@ -2557,6 +2569,9 @@ class AppLocalizationsZh extends AppLocalizations {
|
|||
@override
|
||||
String get pathTrace_someHopsNoLocation => '其中一个或多个啤酒花缺少位置!';
|
||||
|
||||
@override
|
||||
String get pathTrace_clearTooltip => '清除路径';
|
||||
|
||||
@override
|
||||
String get contacts_pathTrace => '路径追踪';
|
||||
|
||||
|
|
|
|||
|
|
@ -1575,7 +1575,6 @@
|
|||
"notification_newNodesCount": "{count} {count, plural, =1{nieuw knooppunt} other{nieuwe knooppunten}}",
|
||||
"notification_newTypeDiscovered": "Nieuw {contactType} ontdekt",
|
||||
"notification_receivedNewMessage": "Nieuw bericht ontvangen",
|
||||
"contacts_zeroHopContactAdvertFailed": "Mislukt om contact te verzenden",
|
||||
"settings_gpxExportRepeatersSubtitle": "Exporteert repeaters / roomserver met een locatie naar GPX-bestand.",
|
||||
"settings_gpxExportRepeaters": "Exporteer repeaters / roomserver naar GPX",
|
||||
"settings_gpxExportSuccess": "Succesvol GPX-bestand geëxporteerd.",
|
||||
|
|
@ -1591,6 +1590,10 @@
|
|||
"settings_gpxExportAllContacts": "Alle contactlocaties",
|
||||
"settings_gpxExportShareText": "Kaartgegevens geëxporteerd uit meshcore-open",
|
||||
"settings_gpxExportShareSubject": "meshcore-open GPX kaartgegevens exporteren",
|
||||
"pathTrace_someHopsNoLocation": "Een of meer van de hops ontbreken een locatie!"
|
||||
|
||||
"pathTrace_someHopsNoLocation": "Een of meer van de hops ontbreken een locatie!",
|
||||
"map_removeLast": "Verwijder Laatste",
|
||||
"pathTrace_clearTooltip": "Weg wissen",
|
||||
"map_pathTraceCancelled": "Pad traceren geannuleerd",
|
||||
"map_tapToAdd": "Tik op knooppunten om ze toe te voegen aan het pad",
|
||||
"map_runTrace": "Padeshulp traceren"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1575,7 +1575,6 @@
|
|||
"notification_newNodesCount": "{count} {count, plural, =1{nowy węzeł} few{nowe węzły} many{nowych węzłów} other{nowych węzłów}}",
|
||||
"notification_newTypeDiscovered": "Nowy {contactType} wykryty",
|
||||
"notification_receivedNewMessage": "Otrzymano nową wiadomość",
|
||||
"contacts_zeroHopContactAdvertFailed": "Nie udało się wysłać kontaktu.",
|
||||
"settings_gpxExportContacts": "Eksportuj towarzyszy do GPX",
|
||||
"settings_gpxExportRepeaters": "Eksportuj powtórki / serwer pokojowy do GPX",
|
||||
"settings_gpxExportRepeatersSubtitle": "Eksportuje powtarzacze / roomserver z lokalizacją do pliku GPX.",
|
||||
|
|
@ -1591,6 +1590,10 @@
|
|||
"settings_gpxExportChat": "Lokalizacje towarzyszy",
|
||||
"settings_gpxExportShareText": "Dane mapy wyeksportowane z meshcore-open",
|
||||
"settings_gpxExportShareSubject": "Eksport danych mapy GPX meshcore-open",
|
||||
"pathTrace_someHopsNoLocation": "Jeden lub więcej z chmieli nie ma określonej lokalizacji!"
|
||||
|
||||
"pathTrace_someHopsNoLocation": "Jeden lub więcej z chmieli nie ma określonej lokalizacji!",
|
||||
"map_pathTraceCancelled": "Śledzenie ścieżki anulowano.",
|
||||
"map_runTrace": "Uruchom ślad ścieżki",
|
||||
"pathTrace_clearTooltip": "Wyczyść ścieżkę",
|
||||
"map_removeLast": "Usuń ostatni",
|
||||
"map_tapToAdd": "Kliknij na węzły, aby dodać je do ścieżki."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1575,7 +1575,6 @@
|
|||
"notification_newNodesCount": "{count} {count, plural, =1{novo nó} other{novos nós}}",
|
||||
"notification_newTypeDiscovered": "Novo {contactType} descoberto",
|
||||
"notification_receivedNewMessage": "Nova mensagem recebida",
|
||||
"contacts_zeroHopContactAdvertFailed": "Falha ao enviar contato.",
|
||||
"settings_gpxExportRepeaters": "Exportar repetidores / servidor de sala para GPX",
|
||||
"settings_gpxExportRepeatersSubtitle": "Exporta repetidores / roomserver com localização para arquivo GPX.",
|
||||
"settings_gpxExportSuccess": "Arquivo GPX exportado com sucesso.",
|
||||
|
|
@ -1591,6 +1590,10 @@
|
|||
"settings_gpxExportAllContacts": "Todos os locais de contatos",
|
||||
"settings_gpxExportShareText": "Dados do mapa exportados do meshcore-open",
|
||||
"settings_gpxExportShareSubject": "meshcore-open exportação de dados de mapa GPX",
|
||||
"pathTrace_someHopsNoLocation": "Um ou mais dos lúpulos estão sem localização!"
|
||||
|
||||
"pathTrace_someHopsNoLocation": "Um ou mais dos lúpulos estão sem localização!",
|
||||
"map_runTrace": "Executar Traçado de Caminho",
|
||||
"map_pathTraceCancelled": "Rastreamento de caminho cancelado.",
|
||||
"pathTrace_clearTooltip": "Limpar caminho",
|
||||
"map_removeLast": "Remover Último",
|
||||
"map_tapToAdd": "Toque nos nós para adicioná-los ao caminho."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -815,7 +815,6 @@
|
|||
"notification_newNodesCount": "{count} {count, plural, =1{новый узел} few{новых узла} many{новых узлов} other{новых узлов}}",
|
||||
"notification_newTypeDiscovered": "Обнаружен новый {contactType}",
|
||||
"notification_receivedNewMessage": "Получено новое сообщение",
|
||||
"contacts_zeroHopContactAdvertSent": "Отправлено сообщение по объявлению.",
|
||||
"settings_gpxExportRepeaters": "Экспортировать рипитеры / сервер комнаты в GPX",
|
||||
"settings_gpxExportRepeatersSubtitle": "Экспортирует ретрансляторы / сервер комнат с местоположением в файл GPX.",
|
||||
"settings_gpxExportContacts": "Экспортировать спутников в GPX",
|
||||
|
|
@ -831,6 +830,10 @@
|
|||
"settings_gpxExportNoContacts": "Нет контактов для экспорта.",
|
||||
"settings_gpxExportShareText": "Данные карты экспортированы из meshcore-open",
|
||||
"settings_gpxExportShareSubject": "meshcore-open экспорт данных карты GPX",
|
||||
"pathTrace_someHopsNoLocation": "Одному или нескольким хмелям не указано местоположение!"
|
||||
|
||||
"pathTrace_someHopsNoLocation": "Одному или нескольким хмелям не указано местоположение!",
|
||||
"map_tapToAdd": "Нажимайте на узлы, чтобы добавить их в путь.",
|
||||
"map_removeLast": "Удалить последний",
|
||||
"map_pathTraceCancelled": "Отмена трассировки пути",
|
||||
"pathTrace_clearTooltip": "Очистить путь",
|
||||
"map_runTrace": "Запустить трассировку пути"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1575,7 +1575,6 @@
|
|||
"notification_newNodesCount": "{count} {count, plural, =1{nový uzol} few{nové uzly} other{nových uzlov}}",
|
||||
"notification_newTypeDiscovered": "Nový {contactType} objavený",
|
||||
"notification_receivedNewMessage": "Prijatá nová správa",
|
||||
"contacts_ShareContact": "Kopírovať kontakt do schránky",
|
||||
"settings_gpxExportRepeatersSubtitle": "Exportuje repeater / roomserver s lokalitou do súboru GPX.",
|
||||
"settings_gpxExportContacts": "Export sprievodcov do GPX",
|
||||
"settings_gpxExportSuccess": "Úspešne exportovaný súbor GPX.",
|
||||
|
|
@ -1591,6 +1590,10 @@
|
|||
"settings_gpxExportChat": "Lokácie sprievodcov",
|
||||
"settings_gpxExportShareText": "Mapové údaje exportované z meshcore-open",
|
||||
"settings_gpxExportShareSubject": "meshcore-open export dát GPX mapových údajov",
|
||||
"pathTrace_someHopsNoLocation": "Jedna alebo viac chmeľov chýba lokalita!"
|
||||
|
||||
"pathTrace_someHopsNoLocation": "Jedna alebo viac chmeľov chýba lokalita!",
|
||||
"pathTrace_clearTooltip": "Zmazať cestu",
|
||||
"map_tapToAdd": "Kliknite na uzly, aby ste ich pridali k ceste.",
|
||||
"map_removeLast": "Odstrániť posledný",
|
||||
"map_runTrace": "Spustiť trasovaním cesty",
|
||||
"map_pathTraceCancelled": "Zrušenie stopáže cesty bolo zrušené."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1575,7 +1575,6 @@
|
|||
"notification_newNodesCount": "{count} {count, plural, =1{novo vozlišče} =2{novi vozlišči} few{nova vozlišča} other{novih vozlišč}}",
|
||||
"notification_newTypeDiscovered": "Odkrito novo {contactType}",
|
||||
"notification_receivedNewMessage": "Prejeto novo sporočilo",
|
||||
"contacts_ShareContact": "Kopiraj stik v Odložišče",
|
||||
"settings_gpxExportAll": "Izvozi vse kontakte v GPX",
|
||||
"settings_gpxExportContacts": "Izvoz spremljevalcev v GPX",
|
||||
"settings_gpxExportRepeatersSubtitle": "Izvozi ponovljene oddajnike / strežnik sobe z lokacijo v datoteko GPX.",
|
||||
|
|
@ -1591,6 +1590,10 @@
|
|||
"settings_gpxExportNoContacts": "Ni stikov za izvoz.",
|
||||
"settings_gpxExportNotAvailable": "Ni podprto na vašem napravi/operacijskem sistemu",
|
||||
"settings_gpxExportShareSubject": "meshcore-open izvoz podatkov GPX karte",
|
||||
"pathTrace_someHopsNoLocation": "Ena ali več hmelju manjka lokacija!"
|
||||
|
||||
"pathTrace_someHopsNoLocation": "Ena ali več hmelju manjka lokacija!",
|
||||
"map_tapToAdd": "Pritisnite na vozlišča, da jih dodate poti.",
|
||||
"map_removeLast": "Odstrani Zadnji",
|
||||
"map_runTrace": "Zaženi sledenje poti",
|
||||
"pathTrace_clearTooltip": "Počisti pot",
|
||||
"map_pathTraceCancelled": "Spremljanje poti je prekinjeno."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1575,7 +1575,6 @@
|
|||
"notification_newNodesCount": "{count} {count, plural, =1{ny nod} other{nya noder}}",
|
||||
"notification_newTypeDiscovered": "Ny {contactType} upptäckt",
|
||||
"notification_receivedNewMessage": "Nytt meddelande mottaget",
|
||||
"contacts_ShareContactZeroHop": "Dela kontakt via annons",
|
||||
"settings_gpxExportAll": "Exportera alla kontakter till GPX",
|
||||
"settings_gpxExportRepeatersSubtitle": "Exporterar repeater / roomserver med plats till GPX-fil.",
|
||||
"settings_gpxExportSuccess": "Har exporterat GPX-fil med framgång",
|
||||
|
|
@ -1591,6 +1590,10 @@
|
|||
"settings_gpxExportAllContacts": "Alla kontakters platser",
|
||||
"settings_gpxExportShareSubject": "meshcore-open export av GPX-kartdata",
|
||||
"settings_gpxExportShareText": "Kartdata exporterad från meshcore-open",
|
||||
"pathTrace_someHopsNoLocation": "En eller flera av humlen saknar en plats!"
|
||||
|
||||
"pathTrace_someHopsNoLocation": "En eller flera av humlen saknar en plats!",
|
||||
"pathTrace_clearTooltip": "Rensa väg",
|
||||
"map_pathTraceCancelled": "Sökvägsspårning avbruten.",
|
||||
"map_runTrace": "Kör spårsökning",
|
||||
"map_tapToAdd": "Tryck på noder för att lägga till dem i banan.",
|
||||
"map_removeLast": "Ta bort sista"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1575,7 +1575,6 @@
|
|||
"notification_newNodesCount": "{count} {count, plural, =1{новий вузол} few{нових вузли} many{нових вузлів} other{нових вузлів}}",
|
||||
"notification_newTypeDiscovered": "Виявлено новий {contactType}",
|
||||
"notification_receivedNewMessage": "Отримано нове повідомлення",
|
||||
"contacts_ShareContactZeroHop": "Поділитися контактом за оголошенням",
|
||||
"settings_gpxExportRepeaters": "Експортувати ретранслятори / сервер кімнати до GPX",
|
||||
"settings_gpxExportRepeatersSubtitle": "Експортує ретранслятори / сервер кімнати з місцезнаходженням у файл GPX.",
|
||||
"settings_gpxExportSuccess": "Успішно експортовано файл GPX.",
|
||||
|
|
@ -1591,6 +1590,10 @@
|
|||
"settings_gpxExportShareText": "Дані карти експортовані з meshcore-open",
|
||||
"settings_gpxExportAllContacts": "Усі місця контактів",
|
||||
"settings_gpxExportShareSubject": "експорт даних карти meshcore-open у форматі GPX",
|
||||
"pathTrace_someHopsNoLocation": "Одне або більше хмелів відсутнє місце розташування!"
|
||||
|
||||
"pathTrace_someHopsNoLocation": "Одне або більше хмелів відсутнє місце розташування!",
|
||||
"map_tapToAdd": "Натисніть на вузли, щоб додати їх до шляху",
|
||||
"map_runTrace": "Виконати трасування шляху",
|
||||
"pathTrace_clearTooltip": "Очистити шлях",
|
||||
"map_removeLast": "Видалити останній",
|
||||
"map_pathTraceCancelled": "Відмінується трасування шляху"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1575,7 +1575,6 @@
|
|||
"notification_newNodesCount": "{count} 个新节点",
|
||||
"notification_newTypeDiscovered": "发现新 {contactType}",
|
||||
"notification_receivedNewMessage": "收到新消息",
|
||||
"contacts_contactAdvertCopyFailed": "将广告复制到剪贴板操作失败。",
|
||||
"settings_gpxExportRepeaters": "导出重复器/房间服务器到GPX",
|
||||
"settings_gpxExportRepeatersSubtitle": "导出带有位置的重复器/房间服务器到GPX文件。",
|
||||
"settings_gpxExportContactsSubtitle": "导出带有位置的伙伴到GPX文件。",
|
||||
|
|
@ -1591,6 +1590,10 @@
|
|||
"settings_gpxExportNoContacts": "没有联系人可导出",
|
||||
"settings_gpxExportShareText": "来自meshcore-open的导出地图数据",
|
||||
"settings_gpxExportShareSubject": "meshcore-open GPX 地图数据导出",
|
||||
"pathTrace_someHopsNoLocation": "其中一个或多个啤酒花缺少位置!"
|
||||
|
||||
"pathTrace_someHopsNoLocation": "其中一个或多个啤酒花缺少位置!",
|
||||
"map_tapToAdd": "点击节点将其添加到路径中",
|
||||
"pathTrace_clearTooltip": "清除路径",
|
||||
"map_pathTraceCancelled": "路径跟踪已取消",
|
||||
"map_removeLast": "删除最后一个",
|
||||
"map_runTrace": "运行路径跟踪"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -384,6 +384,7 @@ class AppSettingsScreen extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
|
||||
// Fixed rendering issues
|
||||
Widget _buildBatteryCard(
|
||||
BuildContext context,
|
||||
AppSettingsService settingsService,
|
||||
|
|
@ -399,6 +400,7 @@ class AppSettingsScreen extends StatelessWidget {
|
|||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 4),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 16, 16, 8),
|
||||
child: Text(
|
||||
|
|
@ -406,6 +408,8 @@ class AppSettingsScreen extends StatelessWidget {
|
|||
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
|
||||
// Main tile (icon + text only)
|
||||
ListTile(
|
||||
leading: const Icon(Icons.battery_full),
|
||||
title: Text(context.l10n.appSettings_batteryChemistry),
|
||||
|
|
@ -416,8 +420,19 @@ class AppSettingsScreen extends StatelessWidget {
|
|||
)
|
||||
: context.l10n.appSettings_batteryChemistryConnectFirst,
|
||||
),
|
||||
trailing: DropdownButton<String>(
|
||||
value: selection,
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
),
|
||||
|
||||
// Dropdown (separate full-width row)
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
|
||||
child: DropdownButtonFormField<String>(
|
||||
initialValue: selection,
|
||||
isExpanded: true,
|
||||
decoration: const InputDecoration(
|
||||
border: UnderlineInputBorder(),
|
||||
isDense: true,
|
||||
),
|
||||
onChanged: isConnected
|
||||
? (value) {
|
||||
if (value != null) {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_map/flutter_map.dart';
|
||||
import 'package:latlong2/latlong.dart';
|
||||
import 'package:meshcore_open/screens/path_trace_map.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../connector/meshcore_connector.dart';
|
||||
|
|
@ -48,9 +50,14 @@ class _MapScreenState extends State<MapScreen> {
|
|||
final MapMarkerService _markerService = MapMarkerService();
|
||||
final Set<String> _hiddenMarkerIds = {};
|
||||
Set<String> _removedMarkerIds = {};
|
||||
bool _isBuildingPathTrace = false;
|
||||
bool _isSelectingPoi = false;
|
||||
bool _hasInitializedMap = false;
|
||||
bool _removedMarkersLoaded = false;
|
||||
final List<int> _pathTrace = [];
|
||||
final List<LatLng> _points = [];
|
||||
final List<Polyline> _polylines = [];
|
||||
bool _legendExpanded = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
|
@ -147,6 +154,19 @@ class _MapScreenState extends State<MapScreen> {
|
|||
.where((c) => c.hasLocation)
|
||||
.toList();
|
||||
|
||||
_polylines.clear();
|
||||
_polylines.addAll(
|
||||
_points.length > 1
|
||||
? [
|
||||
Polyline(
|
||||
points: _points,
|
||||
strokeWidth: 4,
|
||||
color: Colors.blueAccent,
|
||||
),
|
||||
]
|
||||
: <Polyline>[],
|
||||
);
|
||||
|
||||
// Calculate center and zoom of all nodes, or default to (0, 0)
|
||||
LatLng center = const LatLng(0, 0);
|
||||
double initialZoom = 10.0;
|
||||
|
|
@ -247,6 +267,12 @@ class _MapScreenState extends State<MapScreen> {
|
|||
centerTitle: true,
|
||||
automaticallyImplyLeading: false,
|
||||
actions: [
|
||||
if (!_isBuildingPathTrace)
|
||||
IconButton(
|
||||
icon: const Icon(Icons.radar),
|
||||
onPressed: () => _startPath(),
|
||||
tooltip: context.l10n.contacts_pathTrace,
|
||||
),
|
||||
PopupMenuButton(
|
||||
itemBuilder: (context) => [
|
||||
PopupMenuItem(
|
||||
|
|
@ -334,6 +360,8 @@ class _MapScreenState extends State<MapScreen> {
|
|||
MapTileCacheService.userAgentPackageName,
|
||||
maxZoom: 19,
|
||||
),
|
||||
if (_polylines.isNotEmpty && _isBuildingPathTrace)
|
||||
PolylineLayer(polylines: _polylines),
|
||||
MarkerLayer(
|
||||
markers: [
|
||||
if (highlightPosition != null)
|
||||
|
|
@ -390,7 +418,12 @@ class _MapScreenState extends State<MapScreen> {
|
|||
),
|
||||
],
|
||||
),
|
||||
_buildLegend(contactsWithLocation.length, sharedMarkers.length),
|
||||
if (!_isBuildingPathTrace)
|
||||
_buildLegend(
|
||||
contactsWithLocation.length,
|
||||
sharedMarkers.length,
|
||||
),
|
||||
if (_isBuildingPathTrace) _buildPathTraceOverlay(),
|
||||
],
|
||||
),
|
||||
bottomNavigationBar: SafeArea(
|
||||
|
|
@ -434,7 +467,11 @@ class _MapScreenState extends State<MapScreen> {
|
|||
width: 35,
|
||||
height: 35,
|
||||
child: GestureDetector(
|
||||
onTap: () => _showNodeInfo(context, contact),
|
||||
onLongPress: () =>
|
||||
_isBuildingPathTrace ? _showNodeInfo(context, contact) : null,
|
||||
onTap: () => _isBuildingPathTrace
|
||||
? _addToPath(context, contact)
|
||||
: _showNodeInfo(context, contact),
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
|
|
@ -503,60 +540,102 @@ class _MapScreenState extends State<MapScreen> {
|
|||
top: 16,
|
||||
right: 16,
|
||||
child: Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
context.l10n.map_nodesCount(nodeCount),
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 14,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
InkWell(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_legendExpanded = !_legendExpanded;
|
||||
});
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(12, 10, 12, 10),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
context.l10n.map_nodesCount(nodeCount),
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
context.l10n.map_pinsCount(markerCount),
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
AnimatedRotation(
|
||||
turns: _legendExpanded ? 0.5 : 0,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
child: const Icon(Icons.expand_more, size: 20),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Text(
|
||||
context.l10n.map_pinsCount(markerCount),
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 12,
|
||||
),
|
||||
AnimatedCrossFade(
|
||||
firstChild: const SizedBox.shrink(),
|
||||
secondChild: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(12, 0, 12, 12),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 6),
|
||||
_buildLegendItem(
|
||||
Icons.person,
|
||||
context.l10n.map_chat,
|
||||
Colors.blue,
|
||||
),
|
||||
_buildLegendItem(
|
||||
Icons.router,
|
||||
context.l10n.map_repeater,
|
||||
Colors.green,
|
||||
),
|
||||
_buildLegendItem(
|
||||
Icons.meeting_room,
|
||||
context.l10n.map_room,
|
||||
Colors.purple,
|
||||
),
|
||||
_buildLegendItem(
|
||||
Icons.sensors,
|
||||
context.l10n.map_sensor,
|
||||
Colors.orange,
|
||||
),
|
||||
_buildLegendItem(
|
||||
Icons.flag,
|
||||
context.l10n.map_pinDm,
|
||||
Colors.blue,
|
||||
),
|
||||
_buildLegendItem(
|
||||
Icons.flag,
|
||||
context.l10n.map_pinPrivate,
|
||||
Colors.purple,
|
||||
),
|
||||
_buildLegendItem(
|
||||
Icons.flag,
|
||||
context.l10n.map_pinPublic,
|
||||
Colors.orange,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_buildLegendItem(
|
||||
Icons.person,
|
||||
context.l10n.map_chat,
|
||||
Colors.blue,
|
||||
),
|
||||
_buildLegendItem(
|
||||
Icons.router,
|
||||
context.l10n.map_repeater,
|
||||
Colors.green,
|
||||
),
|
||||
_buildLegendItem(
|
||||
Icons.meeting_room,
|
||||
context.l10n.map_room,
|
||||
Colors.purple,
|
||||
),
|
||||
_buildLegendItem(
|
||||
Icons.sensors,
|
||||
context.l10n.map_sensor,
|
||||
Colors.orange,
|
||||
),
|
||||
_buildLegendItem(Icons.flag, context.l10n.map_pinDm, Colors.blue),
|
||||
_buildLegendItem(
|
||||
Icons.flag,
|
||||
context.l10n.map_pinPrivate,
|
||||
Colors.purple,
|
||||
),
|
||||
_buildLegendItem(
|
||||
Icons.flag,
|
||||
context.l10n.map_pinPublic,
|
||||
Colors.orange,
|
||||
),
|
||||
],
|
||||
),
|
||||
crossFadeState: _legendExpanded
|
||||
? CrossFadeState.showSecond
|
||||
: CrossFadeState.showFirst,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
@ -564,7 +643,7 @@ class _MapScreenState extends State<MapScreen> {
|
|||
|
||||
Widget _buildLegendItem(IconData icon, String label, Color color) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4.0),
|
||||
padding: const EdgeInsets.symmetric(vertical: 1.0),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
|
|
@ -1412,6 +1491,114 @@ class _MapScreenState extends State<MapScreen> {
|
|||
return context.l10n.time_allTime;
|
||||
}
|
||||
}
|
||||
|
||||
void _addToPath(BuildContext context, Contact contact) {
|
||||
setState(() {
|
||||
_pathTrace.add(
|
||||
contact.publicKey[0],
|
||||
); // Add first 16 bytes of public key to path trace
|
||||
_points.add(LatLng(contact.latitude!, contact.longitude!));
|
||||
});
|
||||
}
|
||||
|
||||
void _startPath() {
|
||||
setState(() {
|
||||
_isBuildingPathTrace = true;
|
||||
_pathTrace.clear();
|
||||
_points.clear();
|
||||
_polylines.clear();
|
||||
});
|
||||
}
|
||||
|
||||
void _removePath() {
|
||||
setState(() {
|
||||
_pathTrace.removeLast(); // Remove last node from path trace
|
||||
_points.removeLast(); // Remove last point from points list
|
||||
_polylines.clear(); // Clear polylines
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildPathTraceOverlay() {
|
||||
final l10n = context.l10n;
|
||||
return Positioned(
|
||||
top: 16,
|
||||
left: 16,
|
||||
right: 16,
|
||||
child: Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
l10n.contacts_pathTrace,
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
if (_pathTrace.isEmpty) const SizedBox(height: 8),
|
||||
if (_pathTrace.isEmpty)
|
||||
Text(l10n.map_tapToAdd, style: TextStyle(fontSize: 12)),
|
||||
const SizedBox(height: 6),
|
||||
if (_pathTrace.isNotEmpty)
|
||||
Text(
|
||||
"${l10n.path_currentPathLabel} ${formatDistance(getPathDistanceMeters(_points))}",
|
||||
style: TextStyle(fontSize: 12, color: Colors.grey[700]),
|
||||
),
|
||||
SelectableText(
|
||||
_pathTrace
|
||||
.map((b) => b.toRadixString(16).padLeft(2, '0'))
|
||||
.join(','),
|
||||
style: TextStyle(fontSize: 18),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (_pathTrace.isNotEmpty)
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => PathTraceMapScreen(
|
||||
title: l10n.contacts_pathTrace,
|
||||
path: Uint8List.fromList(_pathTrace),
|
||||
),
|
||||
),
|
||||
);
|
||||
setState(() {
|
||||
_isBuildingPathTrace = false;
|
||||
});
|
||||
},
|
||||
child: Text(l10n.map_runTrace),
|
||||
),
|
||||
if (_pathTrace.isNotEmpty)
|
||||
ElevatedButton(
|
||||
onPressed: _removePath,
|
||||
child: Text(l10n.map_removeLast),
|
||||
),
|
||||
if (_pathTrace.isEmpty)
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_isBuildingPathTrace = false;
|
||||
_pathTrace.clear();
|
||||
_points.clear();
|
||||
_polylines.clear();
|
||||
});
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(l10n.map_pathTraceCancelled)),
|
||||
);
|
||||
},
|
||||
child: Text(l10n.common_cancel),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _MarkerPayload {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,23 @@ import 'package:meshcore_open/services/map_tile_cache_service.dart';
|
|||
import 'package:meshcore_open/widgets/snr_indicator.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
double getPathDistanceMeters(List<LatLng> points) {
|
||||
if (points.length <= 1) return 0.0;
|
||||
|
||||
double distanceMeters = 0.0;
|
||||
final distanceCalculator = Distance();
|
||||
|
||||
for (int i = 0; i < points.length - 1; i++) {
|
||||
distanceMeters += distanceCalculator(points[i], points[i + 1]);
|
||||
}
|
||||
|
||||
return distanceMeters;
|
||||
}
|
||||
|
||||
String formatDistance(double distanceMeters) {
|
||||
return '(${(distanceMeters / 1609.34).toStringAsFixed(2)} Miles / ${(distanceMeters / 1000).toStringAsFixed(2)} Km)';
|
||||
}
|
||||
|
||||
class PathTraceData {
|
||||
final Uint8List pathData;
|
||||
final Uint8List snrData;
|
||||
|
|
@ -50,7 +67,6 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
|
|||
bool _isLoading = false;
|
||||
bool _failed2Loaded = false;
|
||||
bool _hasData = false;
|
||||
bool _noLocationErr = false;
|
||||
PathTraceData? _traceData;
|
||||
List<LatLng> _points = <LatLng>[];
|
||||
List<Polyline> _polylines = [];
|
||||
|
|
@ -58,7 +74,7 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
|
|||
double _initialZoom = 2.0;
|
||||
LatLngBounds? _bounds;
|
||||
ValueKey<String> _mapKey = const ValueKey('initial');
|
||||
double _pathDistance = 0.0;
|
||||
double _pathDistanceMeters = 0.0;
|
||||
|
||||
String _formatPathPrefixes(Uint8List pathBytes) {
|
||||
return pathBytes
|
||||
|
|
@ -93,23 +109,11 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
|
|||
return traceBytes;
|
||||
}
|
||||
|
||||
double getPathDistance() {
|
||||
double totalDistance = 0.0;
|
||||
final distanceCalculator = Distance();
|
||||
|
||||
for (int i = 0; i < _points.length - 1; i++) {
|
||||
totalDistance += distanceCalculator(_points[i], _points[i + 1]);
|
||||
}
|
||||
|
||||
return totalDistance;
|
||||
}
|
||||
|
||||
Future<void> _doPathTrace() async {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
_failed2Loaded = false;
|
||||
_noLocationErr = false;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -160,6 +164,14 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
|
|||
});
|
||||
}
|
||||
|
||||
if (code == respCodeErr) {
|
||||
_timeoutTimer?.cancel();
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
_failed2Loaded = true;
|
||||
});
|
||||
}
|
||||
// Check if it's a binary response
|
||||
if (frame.length > 8 &&
|
||||
code == pushCodeTraceData &&
|
||||
|
|
@ -215,8 +227,6 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
|
|||
contact.latitude != null &&
|
||||
contact.longitude != null) {
|
||||
_points.add(LatLng(contact.latitude!, contact.longitude!));
|
||||
} else {
|
||||
_noLocationErr = true;
|
||||
}
|
||||
}
|
||||
_polylines = _points.length > 1
|
||||
|
|
@ -235,7 +245,7 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
|
|||
_mapKey = ValueKey(
|
||||
'${context.l10n.pathTrace_you},${_formatPathPrefixes(_traceData!.pathData)}',
|
||||
);
|
||||
_pathDistance = getPathDistance();
|
||||
_pathDistanceMeters = getPathDistanceMeters(_points);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -279,20 +289,7 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
|
|||
top: false,
|
||||
child: Stack(
|
||||
children: [
|
||||
if (_noLocationErr)
|
||||
Center(
|
||||
child: Card(
|
||||
color: Colors.red,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(12),
|
||||
child: Text(
|
||||
context.l10n.pathTrace_someHopsNoLocation,
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (!_hasData && !_noLocationErr)
|
||||
if (!_hasData)
|
||||
Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
|
|
@ -304,43 +301,11 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
|
|||
],
|
||||
),
|
||||
),
|
||||
if (_hasData && !_noLocationErr)
|
||||
FlutterMap(
|
||||
key: _mapKey,
|
||||
options: MapOptions(
|
||||
initialCenter: _initialCenter!,
|
||||
initialZoom: _initialZoom,
|
||||
initialCameraFit: _bounds == null
|
||||
? null
|
||||
: CameraFit.bounds(
|
||||
bounds: _bounds!,
|
||||
padding: const EdgeInsets.all(64),
|
||||
maxZoom: 16,
|
||||
),
|
||||
minZoom: 2.0,
|
||||
maxZoom: 18.0,
|
||||
),
|
||||
children: [
|
||||
TileLayer(
|
||||
urlTemplate: kMapTileUrlTemplate,
|
||||
tileProvider: tileCache.tileProvider,
|
||||
userAgentPackageName:
|
||||
MapTileCacheService.userAgentPackageName,
|
||||
maxZoom: 19,
|
||||
),
|
||||
if (_polylines.isNotEmpty)
|
||||
PolylineLayer(polylines: _polylines),
|
||||
if (_traceData!.pathData.isNotEmpty)
|
||||
MarkerLayer(
|
||||
markers: _buildHopMarkers(_traceData!.pathData),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (_hasData) _buildMapPathTrace(context, tileCache),
|
||||
if (_points.isEmpty &&
|
||||
!_hasData &&
|
||||
!_isLoading &&
|
||||
!_failed2Loaded &&
|
||||
!_noLocationErr)
|
||||
!_failed2Loaded)
|
||||
Center(
|
||||
child: Card(
|
||||
color: Colors.white.withValues(alpha: 0.9),
|
||||
|
|
@ -352,8 +317,7 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
|
|||
),
|
||||
),
|
||||
),
|
||||
if (_hasData && !_noLocationErr)
|
||||
_buildLegendCard(context, _traceData!),
|
||||
if (_hasData) _buildLegendCard(context, _traceData!),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
@ -365,7 +329,8 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
|
|||
List<Marker> _buildHopMarkers(List<int> pathData) {
|
||||
return [
|
||||
for (final hop in pathData)
|
||||
if (_traceData!.pathContacts[hop]!.hasLocation)
|
||||
if (_traceData!.pathContacts[hop] != null &&
|
||||
_traceData!.pathContacts[hop]!.hasLocation)
|
||||
Marker(
|
||||
point: LatLng(
|
||||
_traceData!.pathContacts[hop]!.latitude!,
|
||||
|
|
@ -453,7 +418,9 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
|
|||
.toRadixString(16)
|
||||
.padLeft(2, '0')
|
||||
.toUpperCase();
|
||||
return contactName != null ? "$hex: $contactName" : hex;
|
||||
return contactName != null
|
||||
? "$hex: $contactName"
|
||||
: "$hex: ${context.l10n.channelPath_unknownRepeater}";
|
||||
}
|
||||
} else {
|
||||
final contactName =
|
||||
|
|
@ -462,7 +429,9 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
|
|||
.toRadixString(16)
|
||||
.padLeft(2, '0')
|
||||
.toUpperCase();
|
||||
return contactName != null ? "$hex: $contactName" : hex;
|
||||
return contactName != null
|
||||
? "$hex: $contactName"
|
||||
: "$hex: ${context.l10n.channelPath_unknownRepeater}";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -475,7 +444,9 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
|
|||
.toRadixString(16)
|
||||
.padLeft(2, '0')
|
||||
.toUpperCase();
|
||||
return contactName != null ? "$hex: $contactName" : hex;
|
||||
return contactName != null
|
||||
? "$hex: $contactName"
|
||||
: "$hex: ${context.l10n.channelPath_unknownRepeater}";
|
||||
} else {
|
||||
return context.l10n.pathTrace_you;
|
||||
}
|
||||
|
|
@ -486,10 +457,46 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
|
|||
.toRadixString(16)
|
||||
.padLeft(2, '0')
|
||||
.toUpperCase();
|
||||
return contactName != null ? "$hex: $contactName" : hex;
|
||||
return contactName != null
|
||||
? "$hex: $contactName"
|
||||
: "$hex: ${context.l10n.channelPath_unknownRepeater}";
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildMapPathTrace(
|
||||
BuildContext context,
|
||||
MapTileCacheService tileCache,
|
||||
) {
|
||||
return FlutterMap(
|
||||
key: _mapKey,
|
||||
options: MapOptions(
|
||||
interactionOptions: InteractionOptions(flags: ~InteractiveFlag.rotate),
|
||||
initialCenter: _initialCenter!,
|
||||
initialZoom: _initialZoom,
|
||||
initialCameraFit: _bounds == null
|
||||
? null
|
||||
: CameraFit.bounds(
|
||||
bounds: _bounds!,
|
||||
padding: const EdgeInsets.all(64),
|
||||
maxZoom: 16,
|
||||
),
|
||||
minZoom: 2.0,
|
||||
maxZoom: 18.0,
|
||||
),
|
||||
children: [
|
||||
TileLayer(
|
||||
urlTemplate: kMapTileUrlTemplate,
|
||||
tileProvider: tileCache.tileProvider,
|
||||
userAgentPackageName: MapTileCacheService.userAgentPackageName,
|
||||
maxZoom: 19,
|
||||
),
|
||||
if (_polylines.isNotEmpty) PolylineLayer(polylines: _polylines),
|
||||
if (_traceData!.pathData.isNotEmpty)
|
||||
MarkerLayer(markers: _buildHopMarkers(_traceData!.pathData)),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLegendCard(BuildContext context, PathTraceData pathTraceData) {
|
||||
final l10n = context.l10n;
|
||||
final maxHeight = MediaQuery.of(context).size.height * 0.35;
|
||||
|
|
@ -509,7 +516,7 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
|
|||
Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Text(
|
||||
'${l10n.channelPath_repeaterHops} (${(_pathDistance / 1609.34).toStringAsFixed(2)} Miles / ${(_pathDistance / 1000).toStringAsFixed(2)} Km)',
|
||||
'${l10n.channelPath_repeaterHops} ${formatDistance(_pathDistanceMeters)}',
|
||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ class SettingsScreen extends StatefulWidget {
|
|||
|
||||
class _SettingsScreenState extends State<SettingsScreen> {
|
||||
bool _showBatteryVoltage = false;
|
||||
bool _deviceInfoExpanded = false;
|
||||
String _appVersion = '';
|
||||
|
||||
@override
|
||||
|
|
@ -74,43 +75,84 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||
MeshCoreConnector connector,
|
||||
) {
|
||||
final l10n = context.l10n;
|
||||
|
||||
return Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
l10n.settings_deviceInfo,
|
||||
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_buildInfoRow(l10n.settings_infoName, connector.deviceDisplayName),
|
||||
_buildInfoRow(l10n.settings_infoId, connector.deviceIdLabel),
|
||||
_buildInfoRow(
|
||||
l10n.settings_infoStatus,
|
||||
connector.isConnected
|
||||
? l10n.common_connected
|
||||
: l10n.common_disconnected,
|
||||
),
|
||||
_buildBatteryInfoRow(context, connector),
|
||||
if (connector.selfName != null)
|
||||
_buildInfoRow(l10n.settings_nodeName, connector.selfName!),
|
||||
if (connector.selfPublicKey != null)
|
||||
_buildInfoRow(
|
||||
l10n.settings_infoPublicKey,
|
||||
'${pubKeyToHex(connector.selfPublicKey!).substring(0, 16)}...',
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
InkWell(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_deviceInfoExpanded = !_deviceInfoExpanded;
|
||||
});
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 16, 16, 16),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
l10n.settings_deviceInfo,
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
AnimatedRotation(
|
||||
turns: _deviceInfoExpanded ? 0.5 : 0,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
child: const Icon(Icons.expand_more),
|
||||
),
|
||||
],
|
||||
),
|
||||
_buildInfoRow(
|
||||
l10n.settings_infoContactsCount,
|
||||
'${connector.contacts.length}',
|
||||
),
|
||||
_buildInfoRow(
|
||||
l10n.settings_infoChannelCount,
|
||||
'${connector.channels.length}',
|
||||
),
|
||||
|
||||
AnimatedCrossFade(
|
||||
firstChild: const SizedBox.shrink(),
|
||||
secondChild: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildInfoRow(
|
||||
l10n.settings_infoName,
|
||||
connector.deviceDisplayName,
|
||||
),
|
||||
_buildInfoRow(l10n.settings_infoId, connector.deviceIdLabel),
|
||||
_buildInfoRow(
|
||||
l10n.settings_infoStatus,
|
||||
connector.isConnected
|
||||
? l10n.common_connected
|
||||
: l10n.common_disconnected,
|
||||
),
|
||||
_buildBatteryInfoRow(context, connector),
|
||||
if (connector.selfName != null)
|
||||
_buildInfoRow(l10n.settings_nodeName, connector.selfName!),
|
||||
if (connector.selfPublicKey != null)
|
||||
_buildInfoRow(
|
||||
l10n.settings_infoPublicKey,
|
||||
'${pubKeyToHex(connector.selfPublicKey!).substring(0, 16)}...',
|
||||
),
|
||||
_buildInfoRow(
|
||||
l10n.settings_infoContactsCount,
|
||||
'${connector.contacts.length}',
|
||||
),
|
||||
_buildInfoRow(
|
||||
l10n.settings_infoChannelCount,
|
||||
'${connector.channels.length}',
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
crossFadeState: _deviceInfoExpanded
|
||||
? CrossFadeState.showSecond
|
||||
: CrossFadeState.showFirst,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -355,22 +397,33 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||
Color? valueColor,
|
||||
VoidCallback? onTap,
|
||||
}) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
final row = Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
if (leading != null) ...[leading, const SizedBox(width: 8)],
|
||||
Text(label, style: TextStyle(color: Colors.grey[600])),
|
||||
Expanded(
|
||||
child: Text(
|
||||
label,
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Flexible(
|
||||
child: Text(
|
||||
value,
|
||||
style: TextStyle(fontWeight: FontWeight.w500, color: valueColor),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
value,
|
||||
style: theme.textTheme.bodyLarge?.copyWith(
|
||||
color: valueColor,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
@ -379,11 +432,12 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||
|
||||
if (onTap != null) {
|
||||
return InkWell(
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
onTap: onTap,
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
child: row,
|
||||
);
|
||||
}
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
|||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
# In Windows, build-name is used as the major, minor, and patch parts
|
||||
# of the product and file versions while build-number is used as the build suffix.
|
||||
version: 6.0.0+1
|
||||
version: 6.0.0+7
|
||||
|
||||
environment:
|
||||
sdk: ^3.9.2
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue