mirror of
https://github.com/dz0ny/meshcore-sar.git
synced 2026-08-11 16:30:28 +00:00
feat: Enhance map functionality with download area selection and user location pointer
- Added download area selection feature in MapProvider with methods to enter, exit, and update bounds. - Implemented DownloadAreaOverlay widget for user interface during download area selection. - Integrated download area preview in MapManagementScreen with visual feedback. - Introduced LocationPointer widget to indicate user location and heading on the map. - Updated localization strings in app_sl.arb for improved clarity and accuracy. - Refactored map marker generation to utilize the new LocationPointer for user location representation. - Improved error handling in LocationTrackingService for initial position setting.
This commit is contained in:
@@ -69,7 +69,7 @@
|
||||
|
||||
"autoSystem": "Automatski (Sustav)",
|
||||
|
||||
"followSystemTheme": "Slijedi sistemsku temu",
|
||||
"followSystemTheme": "Slijedi temu sustava",
|
||||
|
||||
"showRxTxIndicators": "Prikaži RX/TX indikatore",
|
||||
|
||||
@@ -141,11 +141,11 @@
|
||||
|
||||
"technologiesUsed": "Korištene tehnologije:",
|
||||
|
||||
"technologiesList": "• Flutter za višeplatformski razvoj\n• BLE (Bluetooth Low Energy) za mesh mrežu\n• OpenStreetMap za kartografiju\n• Provider za upravljanje stanjem\n• SharedPreferences za lokalno pohranu",
|
||||
"technologiesList": "• Flutter za višeplatformski razvoj\n• BLE (Bluetooth Low Energy) za mesh mrežu\n• OpenStreetMap za kartografiju\n• Provider za upravljanje stanjem\n• SharedPreferences za lokalnu pohranu",
|
||||
|
||||
"moreInfo": "Više informacija",
|
||||
|
||||
"learnMoreAbout": "Saznajte više o MeshCore SAR",
|
||||
"learnMoreAbout": "Saznajte više o MeshCore SAR-u",
|
||||
|
||||
"developer": "Programer",
|
||||
|
||||
@@ -175,7 +175,7 @@
|
||||
|
||||
"locationBroadcast": "Emitiranje lokacije: {latitude}, {longitude}",
|
||||
|
||||
"defaultPinInfo": "Zadani PIN za uređaje bez zaslona je 123456. Problemi s uparivanjem? Zaboravite bluetooth uređaj u postavkama sustava.",
|
||||
"defaultPinInfo": "Zadani PIN za uređaje bez zaslona je 123456. Problemi s uparivanjem? Zaboravite Bluetooth uređaj u postavkama sustava.",
|
||||
|
||||
"noMessagesYet": "Još nema poruka",
|
||||
|
||||
@@ -217,7 +217,7 @@
|
||||
|
||||
"deleteDrawing": "Izbriši crtež",
|
||||
|
||||
"drawingTools": "Alati za crtanje",
|
||||
|
||||
|
||||
"drawLine": "Nacrtaj liniju",
|
||||
|
||||
@@ -433,7 +433,7 @@
|
||||
|
||||
"accuracy": "Točnost",
|
||||
|
||||
"distance": "Udaljenost",
|
||||
|
||||
|
||||
"bearing": "Azimut",
|
||||
|
||||
@@ -467,7 +467,7 @@
|
||||
|
||||
"degreesLeft": "{degrees}° lijevo",
|
||||
|
||||
"latLonFormat": "Ššir: {latitude} Dužina: {longitude}",
|
||||
"latLonFormat": "Šir: {latitude} Duž: {longitude}",
|
||||
|
||||
"noContactsYet": "Još nema kontakata",
|
||||
|
||||
@@ -487,7 +487,7 @@
|
||||
|
||||
"cacheSize": "Veličina predmemorije",
|
||||
|
||||
"storeName": "Ime pohrane",
|
||||
"storeName": "Naziv spremišta",
|
||||
|
||||
"noCacheStatistics": "Statistika predmemorije nije dostupna",
|
||||
|
||||
@@ -571,7 +571,7 @@
|
||||
|
||||
"resetMapRotationTooltip": "Vrati kartu na sjever",
|
||||
|
||||
"showMapDebugInfo": "Prikaži debug informacije karte",
|
||||
"showMapDebugInfo": "Prikaži informacije za otklanjanje pogrešaka karte",
|
||||
|
||||
"displayZoomLevelBounds": "Prikaži razinu zumiranja i granice",
|
||||
|
||||
@@ -585,7 +585,7 @@
|
||||
|
||||
"esriSatellite": "ESRI satelit",
|
||||
|
||||
"googleHybrid": "Google hibridno",
|
||||
"googleHybrid": "Google hibridna karta",
|
||||
|
||||
"googleRoadmap": "Google cestovna karta",
|
||||
|
||||
@@ -769,8 +769,6 @@
|
||||
|
||||
"trailHiddenRecording": "Putanja je skrivena (još se snima)",
|
||||
|
||||
"distance": "Udaljenost",
|
||||
|
||||
"duration": "Trajanje",
|
||||
|
||||
"points": "Točke",
|
||||
@@ -849,7 +847,7 @@
|
||||
|
||||
"lat": "Šir",
|
||||
|
||||
"lon": "Dug",
|
||||
"lon": "Duž",
|
||||
|
||||
"useCurrentLocation": "Koristi trenutnu lokaciju",
|
||||
|
||||
@@ -879,28 +877,27 @@
|
||||
"templateEmoji": "Emoji",
|
||||
"emojiRequired": "Emoji je obavezan",
|
||||
"nameRequired": "Ime je obavezno",
|
||||
"templateDescription": "Description (Optional)",
|
||||
"templateDescriptionHint": "Add additional context...",
|
||||
"templateColor": "Color",
|
||||
"previewFormat": "Preview (SAR Message Format)",
|
||||
"templateDescription": "Opis (neobavezno)",
|
||||
"templateDescriptionHint": "Dodajte dodatni kontekst...",
|
||||
"templateColor": "Boja",
|
||||
"previewFormat": "Pregled (format SAR poruke)",
|
||||
"importFromClipboard": "Uvezi",
|
||||
"exportToClipboard": "Izvezi",
|
||||
"deleteTemplateConfirmation": "Delete template '{name}'?",
|
||||
"templateAdded": "Template added",
|
||||
"templateUpdated": "Template updated",
|
||||
"templateDeleted": "Template deleted",
|
||||
"templatesImported": "{count, plural, =0{No templates imported} =1{Imported 1 template} other{Imported {count} templates}}",
|
||||
"templatesExported": "{count, plural, =1{Exported 1 template to clipboard} other{Exported {count} templates to clipboard}}",
|
||||
"importFailed": "Import failed: {error}",
|
||||
"exportFailed": "Export failed: {error}",
|
||||
"deleteTemplateConfirmation": "Izbrisati predložak '{name}'?",
|
||||
"templateAdded": "Predložak dodan",
|
||||
"templateUpdated": "Predložak ažuriran",
|
||||
"templateDeleted": "Predložak izbrisan",
|
||||
"templatesImported": "{count, plural, =0{Nema uvezenih predložaka} =1{Uvezen 1 predložak} other{Uvezeno {count} predložaka}}",
|
||||
"templatesExported": "{count, plural, =1{Izvezen 1 predložak u međuspremnik} other{Izvezeno {count} predložaka u međuspremnik}}",
|
||||
"importFailed": "Uvoz nije uspio: {error}",
|
||||
"exportFailed": "Izvoz nije uspio: {error}",
|
||||
"resetToDefaults": "Vrati na zadane",
|
||||
"resetToDefaultsConfirmation": "Ovo će izbrisati sve prilagođene predloške i vratiti 4 zadana predloška. Nastaviti?",
|
||||
"reset": "Vrati",
|
||||
"resetComplete": "Predlošci vraćeni na zadane",
|
||||
"noTemplates": "No templates available",
|
||||
"tapAddToCreate": "Tap + to create your first template",
|
||||
"noTemplates": "Nema dostupnih predložaka",
|
||||
"tapAddToCreate": "Dodirnite + za izradu prvog predloška",
|
||||
"ok": "OK",
|
||||
"delete": "Obriši",
|
||||
|
||||
"permissionsSection": "Dozvole",
|
||||
"locationPermission": "Dozvola za lokaciju",
|
||||
@@ -918,7 +915,7 @@
|
||||
"sarNavyBlueDescription": "Profesionalni/Operativni Način",
|
||||
|
||||
"selectRecipient": "Odaberi primatelja",
|
||||
"broadcastToAllNearby": "Emituj svima u blizini",
|
||||
"broadcastToAllNearby": "Emitiraj svima u blizini",
|
||||
"searchRecipients": "Pretraži primatelje...",
|
||||
"noContactsFound": "Nema kontakata",
|
||||
"noRoomsFound": "Nema soba",
|
||||
@@ -946,9 +943,9 @@
|
||||
"sampleObjectVehicle": " Napušteno vozilo - provjeriti vlasnika",
|
||||
"sampleObjectCamping": " Otkrivena oprema za kampiranje",
|
||||
"sampleObjectTrailMarker": " Oznaka staze pronađena izvan puta",
|
||||
"sampleMsgAllTeamsCheckIn": "Svi timovi se jave",
|
||||
"sampleMsgAllTeamsCheckIn": "Svi timovi, javite se",
|
||||
"sampleMsgWeatherUpdate": "Ažuriranje vremena: Vedro nebo, temp 18°C",
|
||||
"sampleMsgBaseCamp": "Bazni kamp uspostavljen na okupljalište",
|
||||
"sampleMsgBaseCamp": "Bazni kamp uspostavljen na okupljalištu",
|
||||
"sampleMsgTeamAlpha": "Tim se kreće prema sektoru 2",
|
||||
"sampleMsgRadioCheck": "Provjera radija - sve stanice odgovorite",
|
||||
"sampleMsgWaterSupply": "Opskrba vodom dostupna na kontrolnoj točki 3",
|
||||
@@ -985,33 +982,33 @@
|
||||
"rectangleDrawing": "Pravokutni crtež",
|
||||
"coordinatesCopiedToClipboard": "Koordinate kopirane u međuspremnik",
|
||||
|
||||
"drawingShared": "Crtež karte",
|
||||
"drawingShared": "Crtež podijeljen",
|
||||
"drawingHidden": "Crtež sakriven s karte",
|
||||
"alreadyShared": "{count, plural, =1{1 već podijeljeno} other{{count} već podijeljeno}}",
|
||||
"newDrawingsShared": "Podijeljeno {count} novi{plural} crtež{plural}",
|
||||
"drawingTools": "Alati za crtanje",
|
||||
"shareDrawing": "Podijeli Crtež",
|
||||
"shareDrawing": "Podijeli crtež",
|
||||
"shareWithAllNearbyDevices": "Podijeli sa svim obližnjim uređajima",
|
||||
"shareToRoom": "Podijeli u Sobu",
|
||||
"sendToPersistentStorage": "Pošalji u trajnu pohranu sobe",
|
||||
"deleteDrawingConfirm": "Jeste li sigurni da želite izbrisati ovaj crtež?",
|
||||
"drawingDeleted": "Crtež izbrisan",
|
||||
"yourDrawingsCount": "Vaši Crteži ({count})",
|
||||
"yourDrawingsCount": "Vaši crteži ({count})",
|
||||
"shared": "Podijeljeno",
|
||||
"line": "Linija",
|
||||
"rectangle": "Pravokutnik",
|
||||
|
||||
"updateAvailable": "Dostupno Ažuriranje",
|
||||
"currentVersion": "Trenutna",
|
||||
"latestVersion": "Najnovija",
|
||||
"downloadUpdate": "Preuzmi",
|
||||
"updateAvailable": "Dostupno ažuriranje",
|
||||
"currentVersion": "Trenutna verzija",
|
||||
"latestVersion": "Najnovija verzija",
|
||||
"downloadUpdate": "Preuzmi ažuriranje",
|
||||
"updateLater": "Kasnije",
|
||||
|
||||
"cadastralParcels": "Katastarske čestice",
|
||||
"forestRoads": "Šumske ceste",
|
||||
"showCadastralParcels": "Prikaži katastarske čestice",
|
||||
"showForestRoads": "Prikaži šumske ceste",
|
||||
"wmsOverlays": "WMS Prekrivanja",
|
||||
"wmsOverlays": "WMS prekrivanja",
|
||||
|
||||
"recentMessages": "Nedavne poruke",
|
||||
|
||||
@@ -1036,31 +1033,31 @@
|
||||
"wizardNext": "Dalje",
|
||||
"wizardGetStarted": "Započni",
|
||||
"wizardWelcomeTitle": "Dobrodošli u MeshCore SAR",
|
||||
"wizardWelcomeDescription": "Moćan alat za komunikaciju bez mreže za spasilačke operacije. Povežite se sa svojim timom koristeći mesh radio tehnologiju kada tradicionalne mreže nisu dostupne.",
|
||||
"wizardConnectingTitle": "Povezivanje s Radiom",
|
||||
"wizardConnectingDescription": "Povežite svoj pametni telefon s MeshCore radio uređajem putem Bluetootha kako biste započeli komunikaciju bez mreže.",
|
||||
"wizardConnectingFeature1": "Skeniraj obližnje MeshCore uređaje",
|
||||
"wizardConnectingFeature2": "Uparite se s vašim radiom putem Bluetootha",
|
||||
"wizardConnectingFeature3": "Radi potpuno offline - internet nije potreban",
|
||||
"wizardSimpleModeTitle": "Jednostavan Način",
|
||||
"wizardSimpleModeDescription": "Novi u mesh umrežavanju? Omogućite jednostavan način za pojednostavljeno sučelje samo s bitnim funkcijama.",
|
||||
"wizardSimpleModeFeature1": "Sučelje prijateljsko početnicima s osnovnim funkcijama",
|
||||
"wizardSimpleModeFeature2": "Prebacite na Napredni način bilo kada u Postavkama",
|
||||
"wizardWelcomeDescription": "Moćan alat za komunikaciju izvan mreže za spasilačke operacije. Povežite se s timom uz mesh radijsku tehnologiju kada tradicionalne mreže nisu dostupne.",
|
||||
"wizardConnectingTitle": "Povezivanje s radiom",
|
||||
"wizardConnectingDescription": "Povežite telefon s MeshCore radijskim uređajem putem Bluetootha i započnite komunikaciju izvan mreže.",
|
||||
"wizardConnectingFeature1": "Skenira obližnje MeshCore uređaje",
|
||||
"wizardConnectingFeature2": "Uparivanje s radijem putem Bluetootha",
|
||||
"wizardConnectingFeature3": "Radi potpuno izvan mreže — internet nije potreban",
|
||||
"wizardSimpleModeTitle": "Jednostavan način",
|
||||
"wizardSimpleModeDescription": "Prvi put koristite mesh mrežu? Uključite jednostavan način za pojednostavljeno sučelje s osnovnim funkcijama.",
|
||||
"wizardSimpleModeFeature1": "Sučelje prilagođeno početnicima s osnovnim funkcijama",
|
||||
"wizardSimpleModeFeature2": "U svakom trenutku prebacite na napredni način u Postavkama",
|
||||
"wizardChannelTitle": "Kanali",
|
||||
"wizardChannelDescription": "Emitirajte poruke svima na kanalu, savršeno za objave i koordinaciju cijele ekipe.",
|
||||
"wizardChannelDescription": "Šaljite poruke svima na kanalu — idealno za obavijesti i koordinaciju tima.",
|
||||
"wizardChannelFeature1": "Javni kanal za opću komunikaciju ekipe",
|
||||
"wizardChannelFeature2": "Stvorite prilagođene kanale za specifične grupe",
|
||||
"wizardChannelFeature3": "Poruke se automatski prosljeđuju putem mreže",
|
||||
"wizardContactsTitle": "Kontakti",
|
||||
"wizardContactsDescription": "Vaši članovi tima se automatski pojavljuju kada se pridruže mesh mreži. Pošaljite im direktne poruke ili pogledajte njihovu lokaciju.",
|
||||
"wizardContactsDescription": "Članovi tima se prikazuju automatski kada se pridruže mesh mreži. Šaljite im izravne poruke ili pogledajte njihovu lokaciju.",
|
||||
"wizardContactsFeature1": "Kontakti se automatski otkrivaju",
|
||||
"wizardContactsFeature2": "Šaljite privatne direktne poruke",
|
||||
"wizardContactsFeature3": "Pogledajte razinu baterije i vrijeme zadnjeg viđenja",
|
||||
"wizardMapTitle": "Karta & Lokacija",
|
||||
"wizardMapDescription": "Pratite svoj tim u stvarnom vremenu i označite važne lokacije za spasilačke operacije.",
|
||||
"wizardMapFeature1": "SAR oznake za pronađene osobe, požare i područja postavljanja",
|
||||
"wizardContactsFeature3": "Prikažite stanje baterije i vrijeme zadnje aktivnosti",
|
||||
"wizardMapTitle": "Karta i lokacija",
|
||||
"wizardMapDescription": "Pratite tim u stvarnom vremenu i označavajte ključne lokacije za spasilačke operacije.",
|
||||
"wizardMapFeature1": "SAR oznake za pronađene osobe, požare i točke okupljanja",
|
||||
"wizardMapFeature2": "GPS praćenje članova tima u stvarnom vremenu",
|
||||
"wizardMapFeature3": "Preuzmite offline karte za udaljena područja",
|
||||
"wizardMapFeature3": "Preuzmite karte za rad izvan mreže",
|
||||
"wizardMapFeature4": "Crtajte oblike i dijelite taktičke informacije",
|
||||
"viewWelcomeTutorial": "Pogledaj uputu dobrodošlice"
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ class AppLocalizationsHr extends AppLocalizations {
|
||||
String get autoSystem => 'Automatski (Sustav)';
|
||||
|
||||
@override
|
||||
String get followSystemTheme => 'Slijedi sistemsku temu';
|
||||
String get followSystemTheme => 'Slijedi temu sustava';
|
||||
|
||||
@override
|
||||
String get showRxTxIndicators => 'Prikaži RX/TX indikatore';
|
||||
@@ -240,13 +240,13 @@ class AppLocalizationsHr extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get technologiesList =>
|
||||
'• Flutter za višeplatformski razvoj\n• BLE (Bluetooth Low Energy) za mesh mrežu\n• OpenStreetMap za kartografiju\n• Provider za upravljanje stanjem\n• SharedPreferences za lokalno pohranu';
|
||||
'• Flutter za višeplatformski razvoj\n• BLE (Bluetooth Low Energy) za mesh mrežu\n• OpenStreetMap za kartografiju\n• Provider za upravljanje stanjem\n• SharedPreferences za lokalnu pohranu';
|
||||
|
||||
@override
|
||||
String get moreInfo => 'Više informacija';
|
||||
|
||||
@override
|
||||
String get learnMoreAbout => 'Saznajte više o MeshCore SAR';
|
||||
String get learnMoreAbout => 'Saznajte više o MeshCore SAR-u';
|
||||
|
||||
@override
|
||||
String get developer => 'Programer';
|
||||
@@ -306,7 +306,7 @@ class AppLocalizationsHr extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get defaultPinInfo =>
|
||||
'Zadani PIN za uređaje bez zaslona je 123456. Problemi s uparivanjem? Zaboravite bluetooth uređaj u postavkama sustava.';
|
||||
'Zadani PIN za uređaje bez zaslona je 123456. Problemi s uparivanjem? Zaboravite Bluetooth uređaj u postavkama sustava.';
|
||||
|
||||
@override
|
||||
String get noMessagesYet => 'Još nema poruka';
|
||||
@@ -318,7 +318,7 @@ class AppLocalizationsHr extends AppLocalizations {
|
||||
String get deleteContact => 'Izbriši kontakt';
|
||||
|
||||
@override
|
||||
String get delete => 'Obriši';
|
||||
String get delete => 'Izbriši';
|
||||
|
||||
@override
|
||||
String get viewOnMap => 'Prikaži na karti';
|
||||
@@ -815,7 +815,7 @@ class AppLocalizationsHr extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String latLonFormat(String latitude, String longitude) {
|
||||
return 'Ššir: $latitude Dužina: $longitude';
|
||||
return 'Šir: $latitude Duž: $longitude';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -847,7 +847,7 @@ class AppLocalizationsHr extends AppLocalizations {
|
||||
String get cacheSize => 'Veličina predmemorije';
|
||||
|
||||
@override
|
||||
String get storeName => 'Ime pohrane';
|
||||
String get storeName => 'Naziv spremišta';
|
||||
|
||||
@override
|
||||
String get noCacheStatistics => 'Statistika predmemorije nije dostupna';
|
||||
@@ -992,7 +992,8 @@ class AppLocalizationsHr extends AppLocalizations {
|
||||
String get resetMapRotationTooltip => 'Vrati kartu na sjever';
|
||||
|
||||
@override
|
||||
String get showMapDebugInfo => 'Prikaži debug informacije karte';
|
||||
String get showMapDebugInfo =>
|
||||
'Prikaži informacije za otklanjanje pogrešaka karte';
|
||||
|
||||
@override
|
||||
String get displayZoomLevelBounds => 'Prikaži razinu zumiranja i granice';
|
||||
@@ -1014,7 +1015,7 @@ class AppLocalizationsHr extends AppLocalizations {
|
||||
String get esriSatellite => 'ESRI satelit';
|
||||
|
||||
@override
|
||||
String get googleHybrid => 'Google hibridno';
|
||||
String get googleHybrid => 'Google hibridna karta';
|
||||
|
||||
@override
|
||||
String get googleRoadmap => 'Google cestovna karta';
|
||||
@@ -1310,12 +1311,12 @@ class AppLocalizationsHr extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String exportFailed(String error) {
|
||||
return 'Export failed: $error';
|
||||
return 'Izvoz nije uspio: $error';
|
||||
}
|
||||
|
||||
@override
|
||||
String importFailed(String error) {
|
||||
return 'Import failed: $error';
|
||||
return 'Uvoz nije uspio: $error';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -1495,7 +1496,7 @@ class AppLocalizationsHr extends AppLocalizations {
|
||||
String get lat => 'Šir';
|
||||
|
||||
@override
|
||||
String get lon => 'Dug';
|
||||
String get lon => 'Duž';
|
||||
|
||||
@override
|
||||
String get useCurrentLocation => 'Koristi trenutnu lokaciju';
|
||||
@@ -1563,16 +1564,16 @@ class AppLocalizationsHr extends AppLocalizations {
|
||||
String get nameRequired => 'Ime je obavezno';
|
||||
|
||||
@override
|
||||
String get templateDescription => 'Description (Optional)';
|
||||
String get templateDescription => 'Opis (neobavezno)';
|
||||
|
||||
@override
|
||||
String get templateDescriptionHint => 'Add additional context...';
|
||||
String get templateDescriptionHint => 'Dodajte dodatni kontekst...';
|
||||
|
||||
@override
|
||||
String get templateColor => 'Color';
|
||||
String get templateColor => 'Boja';
|
||||
|
||||
@override
|
||||
String get previewFormat => 'Preview (SAR Message Format)';
|
||||
String get previewFormat => 'Pregled (format SAR poruke)';
|
||||
|
||||
@override
|
||||
String get importFromClipboard => 'Uvezi';
|
||||
@@ -1582,26 +1583,26 @@ class AppLocalizationsHr extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String deleteTemplateConfirmation(String name) {
|
||||
return 'Delete template \'$name\'?';
|
||||
return 'Izbrisati predložak \'$name\'?';
|
||||
}
|
||||
|
||||
@override
|
||||
String get templateAdded => 'Template added';
|
||||
String get templateAdded => 'Predložak dodan';
|
||||
|
||||
@override
|
||||
String get templateUpdated => 'Template updated';
|
||||
String get templateUpdated => 'Predložak ažuriran';
|
||||
|
||||
@override
|
||||
String get templateDeleted => 'Template deleted';
|
||||
String get templateDeleted => 'Predložak izbrisan';
|
||||
|
||||
@override
|
||||
String templatesImported(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'Imported $count templates',
|
||||
one: 'Imported 1 template',
|
||||
zero: 'No templates imported',
|
||||
other: 'Uvezeno $count predložaka',
|
||||
one: 'Uvezen 1 predložak',
|
||||
zero: 'Nema uvezenih predložaka',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
@@ -1611,8 +1612,8 @@ class AppLocalizationsHr extends AppLocalizations {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'Exported $count templates to clipboard',
|
||||
one: 'Exported 1 template to clipboard',
|
||||
other: 'Izvezeno $count predložaka u međuspremnik',
|
||||
one: 'Izvezen 1 predložak u međuspremnik',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
@@ -1631,10 +1632,10 @@ class AppLocalizationsHr extends AppLocalizations {
|
||||
String get resetComplete => 'Predlošci vraćeni na zadane';
|
||||
|
||||
@override
|
||||
String get noTemplates => 'No templates available';
|
||||
String get noTemplates => 'Nema dostupnih predložaka';
|
||||
|
||||
@override
|
||||
String get tapAddToCreate => 'Tap + to create your first template';
|
||||
String get tapAddToCreate => 'Dodirnite + za izradu prvog predloška';
|
||||
|
||||
@override
|
||||
String get ok => 'OK';
|
||||
@@ -1691,7 +1692,7 @@ class AppLocalizationsHr extends AppLocalizations {
|
||||
String get selectRecipient => 'Odaberi primatelja';
|
||||
|
||||
@override
|
||||
String get broadcastToAllNearby => 'Emituj svima u blizini';
|
||||
String get broadcastToAllNearby => 'Emitiraj svima u blizini';
|
||||
|
||||
@override
|
||||
String get searchRecipients => 'Pretraži primatelje...';
|
||||
@@ -1773,14 +1774,14 @@ class AppLocalizationsHr extends AppLocalizations {
|
||||
String get sampleObjectTrailMarker => ' Oznaka staze pronađena izvan puta';
|
||||
|
||||
@override
|
||||
String get sampleMsgAllTeamsCheckIn => 'Svi timovi se jave';
|
||||
String get sampleMsgAllTeamsCheckIn => 'Svi timovi, javite se';
|
||||
|
||||
@override
|
||||
String get sampleMsgWeatherUpdate =>
|
||||
'Ažuriranje vremena: Vedro nebo, temp 18°C';
|
||||
|
||||
@override
|
||||
String get sampleMsgBaseCamp => 'Bazni kamp uspostavljen na okupljalište';
|
||||
String get sampleMsgBaseCamp => 'Bazni kamp uspostavljen na okupljalištu';
|
||||
|
||||
@override
|
||||
String get sampleMsgTeamAlpha => 'Tim se kreće prema sektoru 2';
|
||||
@@ -1893,7 +1894,7 @@ class AppLocalizationsHr extends AppLocalizations {
|
||||
'Koordinate kopirane u međuspremnik';
|
||||
|
||||
@override
|
||||
String get drawingShared => 'Crtež karte';
|
||||
String get drawingShared => 'Crtež podijeljen';
|
||||
|
||||
@override
|
||||
String get drawingHidden => 'Crtež sakriven s karte';
|
||||
@@ -1915,7 +1916,7 @@ class AppLocalizationsHr extends AppLocalizations {
|
||||
}
|
||||
|
||||
@override
|
||||
String get shareDrawing => 'Podijeli Crtež';
|
||||
String get shareDrawing => 'Podijeli crtež';
|
||||
|
||||
@override
|
||||
String get shareWithAllNearbyDevices =>
|
||||
@@ -1936,7 +1937,7 @@ class AppLocalizationsHr extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String yourDrawingsCount(int count) {
|
||||
return 'Vaši Crteži ($count)';
|
||||
return 'Vaši crteži ($count)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -1949,16 +1950,16 @@ class AppLocalizationsHr extends AppLocalizations {
|
||||
String get rectangle => 'Pravokutnik';
|
||||
|
||||
@override
|
||||
String get updateAvailable => 'Dostupno Ažuriranje';
|
||||
String get updateAvailable => 'Dostupno ažuriranje';
|
||||
|
||||
@override
|
||||
String get currentVersion => 'Trenutna';
|
||||
String get currentVersion => 'Trenutna verzija';
|
||||
|
||||
@override
|
||||
String get latestVersion => 'Najnovija';
|
||||
String get latestVersion => 'Najnovija verzija';
|
||||
|
||||
@override
|
||||
String get downloadUpdate => 'Preuzmi';
|
||||
String get downloadUpdate => 'Preuzmi ažuriranje';
|
||||
|
||||
@override
|
||||
String get updateLater => 'Kasnije';
|
||||
@@ -1976,7 +1977,7 @@ class AppLocalizationsHr extends AppLocalizations {
|
||||
String get showForestRoads => 'Prikaži šumske ceste';
|
||||
|
||||
@override
|
||||
String get wmsOverlays => 'WMS Prekrivanja';
|
||||
String get wmsOverlays => 'WMS prekrivanja';
|
||||
|
||||
@override
|
||||
String get recentMessages => 'Nedavne poruke';
|
||||
@@ -2048,47 +2049,47 @@ class AppLocalizationsHr extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get wizardWelcomeDescription =>
|
||||
'Moćan alat za komunikaciju bez mreže za spasilačke operacije. Povežite se sa svojim timom koristeći mesh radio tehnologiju kada tradicionalne mreže nisu dostupne.';
|
||||
'Moćan alat za komunikaciju izvan mreže za spasilačke operacije. Povežite se s timom uz mesh radijsku tehnologiju kada tradicionalne mreže nisu dostupne.';
|
||||
|
||||
@override
|
||||
String get wizardConnectingTitle => 'Povezivanje s Radiom';
|
||||
String get wizardConnectingTitle => 'Povezivanje s radiom';
|
||||
|
||||
@override
|
||||
String get wizardConnectingDescription =>
|
||||
'Povežite svoj pametni telefon s MeshCore radio uređajem putem Bluetootha kako biste započeli komunikaciju bez mreže.';
|
||||
'Povežite telefon s MeshCore radijskim uređajem putem Bluetootha i započnite komunikaciju izvan mreže.';
|
||||
|
||||
@override
|
||||
String get wizardConnectingFeature1 => 'Skeniraj obližnje MeshCore uređaje';
|
||||
String get wizardConnectingFeature1 => 'Skenira obližnje MeshCore uređaje';
|
||||
|
||||
@override
|
||||
String get wizardConnectingFeature2 =>
|
||||
'Uparite se s vašim radiom putem Bluetootha';
|
||||
'Uparivanje s radijem putem Bluetootha';
|
||||
|
||||
@override
|
||||
String get wizardConnectingFeature3 =>
|
||||
'Radi potpuno offline - internet nije potreban';
|
||||
'Radi potpuno izvan mreže — internet nije potreban';
|
||||
|
||||
@override
|
||||
String get wizardSimpleModeTitle => 'Jednostavan Način';
|
||||
String get wizardSimpleModeTitle => 'Jednostavan način';
|
||||
|
||||
@override
|
||||
String get wizardSimpleModeDescription =>
|
||||
'Novi u mesh umrežavanju? Omogućite jednostavan način za pojednostavljeno sučelje samo s bitnim funkcijama.';
|
||||
'Prvi put koristite mesh mrežu? Uključite jednostavan način za pojednostavljeno sučelje s osnovnim funkcijama.';
|
||||
|
||||
@override
|
||||
String get wizardSimpleModeFeature1 =>
|
||||
'Sučelje prijateljsko početnicima s osnovnim funkcijama';
|
||||
'Sučelje prilagođeno početnicima s osnovnim funkcijama';
|
||||
|
||||
@override
|
||||
String get wizardSimpleModeFeature2 =>
|
||||
'Prebacite na Napredni način bilo kada u Postavkama';
|
||||
'U svakom trenutku prebacite na napredni način u Postavkama';
|
||||
|
||||
@override
|
||||
String get wizardChannelTitle => 'Kanali';
|
||||
|
||||
@override
|
||||
String get wizardChannelDescription =>
|
||||
'Emitirajte poruke svima na kanalu, savršeno za objave i koordinaciju cijele ekipe.';
|
||||
'Šaljite poruke svima na kanalu — idealno za obavijesti i koordinaciju tima.';
|
||||
|
||||
@override
|
||||
String get wizardChannelFeature1 => 'Javni kanal za opću komunikaciju ekipe';
|
||||
@@ -2106,7 +2107,7 @@ class AppLocalizationsHr extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get wizardContactsDescription =>
|
||||
'Vaši članovi tima se automatski pojavljuju kada se pridruže mesh mreži. Pošaljite im direktne poruke ili pogledajte njihovu lokaciju.';
|
||||
'Članovi tima se prikazuju automatski kada se pridruže mesh mreži. Šaljite im izravne poruke ili pogledajte njihovu lokaciju.';
|
||||
|
||||
@override
|
||||
String get wizardContactsFeature1 => 'Kontakti se automatski otkrivaju';
|
||||
@@ -2116,26 +2117,25 @@ class AppLocalizationsHr extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get wizardContactsFeature3 =>
|
||||
'Pogledajte razinu baterije i vrijeme zadnjeg viđenja';
|
||||
'Prikažite stanje baterije i vrijeme zadnje aktivnosti';
|
||||
|
||||
@override
|
||||
String get wizardMapTitle => 'Karta & Lokacija';
|
||||
String get wizardMapTitle => 'Karta i lokacija';
|
||||
|
||||
@override
|
||||
String get wizardMapDescription =>
|
||||
'Pratite svoj tim u stvarnom vremenu i označite važne lokacije za spasilačke operacije.';
|
||||
'Pratite tim u stvarnom vremenu i označavajte ključne lokacije za spasilačke operacije.';
|
||||
|
||||
@override
|
||||
String get wizardMapFeature1 =>
|
||||
'SAR oznake za pronađene osobe, požare i područja postavljanja';
|
||||
'SAR oznake za pronađene osobe, požare i točke okupljanja';
|
||||
|
||||
@override
|
||||
String get wizardMapFeature2 =>
|
||||
'GPS praćenje članova tima u stvarnom vremenu';
|
||||
|
||||
@override
|
||||
String get wizardMapFeature3 =>
|
||||
'Preuzmite offline karte za udaljena područja';
|
||||
String get wizardMapFeature3 => 'Preuzmite karte za rad izvan mreže';
|
||||
|
||||
@override
|
||||
String get wizardMapFeature4 =>
|
||||
|
||||
@@ -306,7 +306,7 @@ class AppLocalizationsSl extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get defaultPinInfo =>
|
||||
'Privzeta PIN koda za naprave brez zaslona je 123456. Težave s seznanitvijo? Pozabite napravo bluetooth v sistemskih nastavitvah.';
|
||||
'Privzeta PIN koda za naprave brez zaslona je 123456. Težave s seznanitvijo? Pozabite napravo Bluetooth v sistemskih nastavitvah.';
|
||||
|
||||
@override
|
||||
String get noMessagesYet => 'Še ni sporočil';
|
||||
@@ -829,7 +829,7 @@ class AppLocalizationsSl extends AppLocalizations {
|
||||
String get teamMembers => 'Člani ekipe';
|
||||
|
||||
@override
|
||||
String get repeaters => 'Repetitorji';
|
||||
String get repeaters => 'Ponavljalniki';
|
||||
|
||||
@override
|
||||
String get rooms => 'Sobe';
|
||||
@@ -992,7 +992,8 @@ class AppLocalizationsSl extends AppLocalizations {
|
||||
String get resetMapRotationTooltip => 'Ponastavi zemljevid na sever';
|
||||
|
||||
@override
|
||||
String get showMapDebugInfo => 'Prikaži debug informacije zemljevida';
|
||||
String get showMapDebugInfo =>
|
||||
'Prikaži informacije za razhroščevanje zemljevida';
|
||||
|
||||
@override
|
||||
String get displayZoomLevelBounds => 'Prikaži nivo povečave in meje';
|
||||
@@ -1014,7 +1015,7 @@ class AppLocalizationsSl extends AppLocalizations {
|
||||
String get esriSatellite => 'ESRI satelit';
|
||||
|
||||
@override
|
||||
String get googleHybrid => 'Google hibridno';
|
||||
String get googleHybrid => 'Google hibridni zemljevid';
|
||||
|
||||
@override
|
||||
String get googleRoadmap => 'Google cestni zemljevid';
|
||||
@@ -1313,12 +1314,12 @@ class AppLocalizationsSl extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String exportFailed(String error) {
|
||||
return 'Export failed: $error';
|
||||
return 'Izvoz ni uspel: $error';
|
||||
}
|
||||
|
||||
@override
|
||||
String importFailed(String error) {
|
||||
return 'Import failed: $error';
|
||||
return 'Uvoz ni uspel: $error';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -1497,7 +1498,7 @@ class AppLocalizationsSl extends AppLocalizations {
|
||||
String get lat => 'Šir';
|
||||
|
||||
@override
|
||||
String get lon => 'Dol';
|
||||
String get lon => 'Dolž';
|
||||
|
||||
@override
|
||||
String get useCurrentLocation => 'Uporabi trenutno lokacijo';
|
||||
@@ -1509,7 +1510,7 @@ class AppLocalizationsSl extends AppLocalizations {
|
||||
String get chatNode => 'Vozlišče za klepet';
|
||||
|
||||
@override
|
||||
String get repeater => 'Ojačevalnik';
|
||||
String get repeater => 'Ponavljalnik';
|
||||
|
||||
@override
|
||||
String get roomChannel => 'Soba/Kanal';
|
||||
@@ -1565,16 +1566,16 @@ class AppLocalizationsSl extends AppLocalizations {
|
||||
String get nameRequired => 'Ime je obvezno';
|
||||
|
||||
@override
|
||||
String get templateDescription => 'Description (Optional)';
|
||||
String get templateDescription => 'Opis (neobvezno)';
|
||||
|
||||
@override
|
||||
String get templateDescriptionHint => 'Add additional context...';
|
||||
String get templateDescriptionHint => 'Dodajte dodatni kontekst...';
|
||||
|
||||
@override
|
||||
String get templateColor => 'Color';
|
||||
String get templateColor => 'Barva';
|
||||
|
||||
@override
|
||||
String get previewFormat => 'Preview (SAR Message Format)';
|
||||
String get previewFormat => 'Predogled (oblika SAR sporočila)';
|
||||
|
||||
@override
|
||||
String get importFromClipboard => 'Uvozi';
|
||||
@@ -1584,26 +1585,26 @@ class AppLocalizationsSl extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String deleteTemplateConfirmation(String name) {
|
||||
return 'Delete template \'$name\'?';
|
||||
return 'Izbrišem predlogo \'$name\'?';
|
||||
}
|
||||
|
||||
@override
|
||||
String get templateAdded => 'Template added';
|
||||
String get templateAdded => 'Predloga dodana';
|
||||
|
||||
@override
|
||||
String get templateUpdated => 'Template updated';
|
||||
String get templateUpdated => 'Predloga posodobljena';
|
||||
|
||||
@override
|
||||
String get templateDeleted => 'Template deleted';
|
||||
String get templateDeleted => 'Predloga izbrisana';
|
||||
|
||||
@override
|
||||
String templatesImported(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'Imported $count templates',
|
||||
one: 'Imported 1 template',
|
||||
zero: 'No templates imported',
|
||||
other: 'Uvoženih $count predlog',
|
||||
one: 'Uvožena 1 predloga',
|
||||
zero: 'Ni uvoženih predlog',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
@@ -1613,8 +1614,8 @@ class AppLocalizationsSl extends AppLocalizations {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'Exported $count templates to clipboard',
|
||||
one: 'Exported 1 template to clipboard',
|
||||
other: 'Izvoženih $count predlog v odložišče',
|
||||
one: 'Izvožena 1 predloga v odložišče',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
@@ -1633,10 +1634,10 @@ class AppLocalizationsSl extends AppLocalizations {
|
||||
String get resetComplete => 'Predloge ponastavljene na privzeto';
|
||||
|
||||
@override
|
||||
String get noTemplates => 'No templates available';
|
||||
String get noTemplates => 'Ni razpoložljivih predlog';
|
||||
|
||||
@override
|
||||
String get tapAddToCreate => 'Tap + to create your first template';
|
||||
String get tapAddToCreate => 'Tapnite + za ustvarjanje prve predloge';
|
||||
|
||||
@override
|
||||
String get ok => 'OK';
|
||||
@@ -1839,7 +1840,7 @@ class AppLocalizationsSl extends AppLocalizations {
|
||||
'Helikopter za evakuacijo prihod 10 minut';
|
||||
|
||||
@override
|
||||
String get sampleMsgEmergencyResolved => 'Nujnost rešena - vse čisto';
|
||||
String get sampleMsgEmergencyResolved => 'Nujna situacija rešena – vse čisto';
|
||||
|
||||
@override
|
||||
String get sampleMsgEmergencyStagingArea => ' Nujno zbirališče';
|
||||
@@ -1894,7 +1895,7 @@ class AppLocalizationsSl extends AppLocalizations {
|
||||
String get coordinatesCopiedToClipboard => 'Koordinate kopirane v odložišče';
|
||||
|
||||
@override
|
||||
String get drawingShared => 'Risba zemljevida';
|
||||
String get drawingShared => 'Risba deljena';
|
||||
|
||||
@override
|
||||
String get drawingHidden => 'Risba skrita z zemljevida';
|
||||
@@ -1916,7 +1917,7 @@ class AppLocalizationsSl extends AppLocalizations {
|
||||
}
|
||||
|
||||
@override
|
||||
String get shareDrawing => 'Deli Risbo';
|
||||
String get shareDrawing => 'Deli risbo';
|
||||
|
||||
@override
|
||||
String get shareWithAllNearbyDevices => 'Deli z vsemi bližnjimi napravami';
|
||||
@@ -1936,7 +1937,7 @@ class AppLocalizationsSl extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String yourDrawingsCount(int count) {
|
||||
return 'Vaše Risbe ($count)';
|
||||
return 'Vaše risbe ($count)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -1949,16 +1950,16 @@ class AppLocalizationsSl extends AppLocalizations {
|
||||
String get rectangle => 'Pravokotnik';
|
||||
|
||||
@override
|
||||
String get updateAvailable => 'Na Voljo Posodobitev';
|
||||
String get updateAvailable => 'Na voljo je posodobitev';
|
||||
|
||||
@override
|
||||
String get currentVersion => 'Trenutna';
|
||||
String get currentVersion => 'Trenutna različica';
|
||||
|
||||
@override
|
||||
String get latestVersion => 'Najnovejša';
|
||||
String get latestVersion => 'Najnovejša različica';
|
||||
|
||||
@override
|
||||
String get downloadUpdate => 'Prenesi';
|
||||
String get downloadUpdate => 'Prenesi posodobitev';
|
||||
|
||||
@override
|
||||
String get updateLater => 'Kasneje';
|
||||
@@ -1976,7 +1977,7 @@ class AppLocalizationsSl extends AppLocalizations {
|
||||
String get showForestRoads => 'Prikaži gozdne ceste';
|
||||
|
||||
@override
|
||||
String get wmsOverlays => 'WMS Prekrivanja';
|
||||
String get wmsOverlays => 'WMS prekrivanja';
|
||||
|
||||
@override
|
||||
String get recentMessages => 'Nedavna sporočila';
|
||||
@@ -2047,47 +2048,47 @@ class AppLocalizationsSl extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get wizardWelcomeDescription =>
|
||||
'Močno orodje za komunikacijo brez omrežja za reševalne operacije. Povežite se s svojo ekipo z uporabo mrežne radijske tehnologije, ko tradicionalna omrežja niso na voljo.';
|
||||
'Zmogljivo orodje za komunikacijo brez omrežja za reševalne operacije. S svojo ekipo se povežite z mesh radijsko tehnologijo, ko tradicionalna omrežja niso na voljo.';
|
||||
|
||||
@override
|
||||
String get wizardConnectingTitle => 'Povezava z Radijem';
|
||||
String get wizardConnectingTitle => 'Povezava z radijem';
|
||||
|
||||
@override
|
||||
String get wizardConnectingDescription =>
|
||||
'Povežite svoj pametni telefon z napravo MeshCore radio preko Bluetootha za začetek komunikacije brez omrežja.';
|
||||
'Svoj telefon povežite z radijsko napravo MeshCore prek Bluetootha in začnite komunicirati brez omrežja.';
|
||||
|
||||
@override
|
||||
String get wizardConnectingFeature1 => 'Iskanje bližnjih naprav MeshCore';
|
||||
String get wizardConnectingFeature1 => 'Poišče bližnje naprave MeshCore';
|
||||
|
||||
@override
|
||||
String get wizardConnectingFeature2 =>
|
||||
'Združite s svojim radijem preko Bluetootha';
|
||||
'Seznanitev z radijsko napravo prek Bluetootha';
|
||||
|
||||
@override
|
||||
String get wizardConnectingFeature3 =>
|
||||
'Deluje popolnoma brez povezave - internet ni potreben';
|
||||
'Deluje povsem brez povezave — internet ni potreben';
|
||||
|
||||
@override
|
||||
String get wizardSimpleModeTitle => 'Preprost Način';
|
||||
String get wizardSimpleModeTitle => 'Preprost način';
|
||||
|
||||
@override
|
||||
String get wizardSimpleModeDescription =>
|
||||
'Novi v mrežnem omrežju? Omogočite preprost način za poenostavljen vmesnik z le bistvenimi funkcijami.';
|
||||
'Ste novi v mesh omrežju? Vključite preprost način za poenostavljen vmesnik z osnovnimi funkcijami.';
|
||||
|
||||
@override
|
||||
String get wizardSimpleModeFeature1 =>
|
||||
'Vmesnik prijazen za začetnike z osnovnimi funkcijami';
|
||||
'Vmesnik, prilagojen začetnikom, z osnovnimi funkcijami';
|
||||
|
||||
@override
|
||||
String get wizardSimpleModeFeature2 =>
|
||||
'Kadarkoli preklopite na napredni način v nastavitvah';
|
||||
'Na napredni način lahko kadarkoli preklopite v nastavitvah';
|
||||
|
||||
@override
|
||||
String get wizardChannelTitle => 'Kanali';
|
||||
|
||||
@override
|
||||
String get wizardChannelDescription =>
|
||||
'Oddajajte sporočila vsem na kanalu, popolno za objave in usklajevanje cele ekipe.';
|
||||
'Pošiljajte sporočila vsem na kanalu — idealno za obvestila in koordinacijo ekipe.';
|
||||
|
||||
@override
|
||||
String get wizardChannelFeature1 =>
|
||||
@@ -2099,47 +2100,47 @@ class AppLocalizationsSl extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get wizardChannelFeature3 =>
|
||||
'Sporočila so samodejno posredovana preko mreže';
|
||||
'Sporočila se samodejno posredujejo prek mreže';
|
||||
|
||||
@override
|
||||
String get wizardContactsTitle => 'Stiki';
|
||||
|
||||
@override
|
||||
String get wizardContactsDescription =>
|
||||
'Vaši člani ekipe se samodejno prikažejo, ko se pridružijo mrežnemu omrežju. Pošljite jim neposredna sporočila ali si oglejte njihovo lokacijo.';
|
||||
'Člani ekipe se prikažejo samodejno, ko se pridružijo mesh omrežju. Pošiljajte jim neposredna sporočila ali si oglejte njihovo lokacijo.';
|
||||
|
||||
@override
|
||||
String get wizardContactsFeature1 => 'Stiki se odkrijejo samodejno';
|
||||
String get wizardContactsFeature1 => 'Samodejno odkrivanje stikov';
|
||||
|
||||
@override
|
||||
String get wizardContactsFeature2 =>
|
||||
'Pošiljajte zasebna neposredna sporočila';
|
||||
'Pošiljanje zasebnih neposrednih sporočil';
|
||||
|
||||
@override
|
||||
String get wizardContactsFeature3 =>
|
||||
'Oglejte si raven baterije in čas zadnjega ogleda';
|
||||
'Prikaz stanja baterije in časa zadnje aktivnosti';
|
||||
|
||||
@override
|
||||
String get wizardMapTitle => 'Zemljevid & Lokacija';
|
||||
String get wizardMapTitle => 'Zemljevid in lokacija';
|
||||
|
||||
@override
|
||||
String get wizardMapDescription =>
|
||||
'Sledite svoji ekipi v realnem času in označite pomembne lokacije za reševalne operacije.';
|
||||
'Spremljajte svojo ekipo v realnem času in označujte ključne lokacije za reševalne operacije.';
|
||||
|
||||
@override
|
||||
String get wizardMapFeature1 =>
|
||||
'SAR označevalci za najdene osebe, požare in območja postavitve';
|
||||
'SAR označevalci za najdene osebe, požare in zbirna mesta';
|
||||
|
||||
@override
|
||||
String get wizardMapFeature2 => 'Sledenje GPS članov ekipe v realnem času';
|
||||
String get wizardMapFeature2 => 'Sledenje članom ekipe z GPS v realnem času';
|
||||
|
||||
@override
|
||||
String get wizardMapFeature3 =>
|
||||
'Prenesite zemljevide brez povezave za oddaljene kraje';
|
||||
'Prenesite zemljevide za uporabo brez povezave';
|
||||
|
||||
@override
|
||||
String get wizardMapFeature4 =>
|
||||
'Narišite oblike in delite taktične informacije';
|
||||
'Rišite oblike in delite taktične informacije';
|
||||
|
||||
@override
|
||||
String get viewWelcomeTutorial => 'Ogled vadnice dobrodošlice';
|
||||
|
||||
@@ -175,7 +175,7 @@
|
||||
|
||||
"locationBroadcast": "Oddajanje lokacije: {latitude}, {longitude}",
|
||||
|
||||
"defaultPinInfo": "Privzeta PIN koda za naprave brez zaslona je 123456. Težave s seznanitvijo? Pozabite napravo bluetooth v sistemskih nastavitvah.",
|
||||
"defaultPinInfo": "Privzeta PIN koda za naprave brez zaslona je 123456. Težave s seznanitvijo? Pozabite napravo Bluetooth v sistemskih nastavitvah.",
|
||||
|
||||
"noMessagesYet": "Še ni sporočil",
|
||||
|
||||
@@ -185,6 +185,8 @@
|
||||
|
||||
"delete": "Izbriši",
|
||||
|
||||
|
||||
|
||||
"viewOnMap": "Poglej na zemljevidu",
|
||||
|
||||
"refresh": "Osveži",
|
||||
@@ -359,7 +361,7 @@
|
||||
|
||||
"noGpsData": "Ni GPS podatkov",
|
||||
|
||||
"distance": "Razdalja",
|
||||
|
||||
|
||||
"pingingDirect": "Pinganje {name} (neposredno preko poti)...",
|
||||
|
||||
@@ -435,6 +437,8 @@
|
||||
|
||||
"distance": "Razdalja",
|
||||
|
||||
|
||||
|
||||
"bearing": "Azimut",
|
||||
|
||||
"direction": "Smer",
|
||||
@@ -475,7 +479,7 @@
|
||||
|
||||
"teamMembers": "Člani ekipe",
|
||||
|
||||
"repeaters": "Repetitorji",
|
||||
"repeaters": "Ponavljalniki",
|
||||
|
||||
"rooms": "Sobe",
|
||||
|
||||
@@ -571,7 +575,7 @@
|
||||
|
||||
"resetMapRotationTooltip": "Ponastavi zemljevid na sever",
|
||||
|
||||
"showMapDebugInfo": "Prikaži debug informacije zemljevida",
|
||||
"showMapDebugInfo": "Prikaži informacije za razhroščevanje zemljevida",
|
||||
|
||||
"displayZoomLevelBounds": "Prikaži nivo povečave in meje",
|
||||
|
||||
@@ -585,7 +589,7 @@
|
||||
|
||||
"esriSatellite": "ESRI satelit",
|
||||
|
||||
"googleHybrid": "Google hibridno",
|
||||
"googleHybrid": "Google hibridni zemljevid",
|
||||
|
||||
"googleRoadmap": "Google cestni zemljevid",
|
||||
|
||||
@@ -769,8 +773,6 @@
|
||||
|
||||
"trailHiddenRecording": "Pot je skrita (še se snema)",
|
||||
|
||||
"distance": "Razdalja",
|
||||
|
||||
"duration": "Trajanje",
|
||||
|
||||
"points": "Točke",
|
||||
@@ -849,7 +851,7 @@
|
||||
|
||||
"lat": "Šir",
|
||||
|
||||
"lon": "Dol",
|
||||
"lon": "Dolž",
|
||||
|
||||
"useCurrentLocation": "Uporabi trenutno lokacijo",
|
||||
|
||||
@@ -857,7 +859,7 @@
|
||||
|
||||
"chatNode": "Vozlišče za klepet",
|
||||
|
||||
"repeater": "Ojačevalnik",
|
||||
"repeater": "Ponavljalnik",
|
||||
|
||||
"roomChannel": "Soba/Kanal",
|
||||
|
||||
@@ -879,28 +881,27 @@
|
||||
"templateEmoji": "Emoji",
|
||||
"emojiRequired": "Emoji je obvezen",
|
||||
"nameRequired": "Ime je obvezno",
|
||||
"templateDescription": "Description (Optional)",
|
||||
"templateDescriptionHint": "Add additional context...",
|
||||
"templateColor": "Color",
|
||||
"previewFormat": "Preview (SAR Message Format)",
|
||||
"templateDescription": "Opis (neobvezno)",
|
||||
"templateDescriptionHint": "Dodajte dodatni kontekst...",
|
||||
"templateColor": "Barva",
|
||||
"previewFormat": "Predogled (oblika SAR sporočila)",
|
||||
"importFromClipboard": "Uvozi",
|
||||
"exportToClipboard": "Izvozi",
|
||||
"deleteTemplateConfirmation": "Delete template '{name}'?",
|
||||
"templateAdded": "Template added",
|
||||
"templateUpdated": "Template updated",
|
||||
"templateDeleted": "Template deleted",
|
||||
"templatesImported": "{count, plural, =0{No templates imported} =1{Imported 1 template} other{Imported {count} templates}}",
|
||||
"templatesExported": "{count, plural, =1{Exported 1 template to clipboard} other{Exported {count} templates to clipboard}}",
|
||||
"importFailed": "Import failed: {error}",
|
||||
"exportFailed": "Export failed: {error}",
|
||||
"deleteTemplateConfirmation": "Izbrišem predlogo '{name}'?",
|
||||
"templateAdded": "Predloga dodana",
|
||||
"templateUpdated": "Predloga posodobljena",
|
||||
"templateDeleted": "Predloga izbrisana",
|
||||
"templatesImported": "{count, plural, =0{Ni uvoženih predlog} =1{Uvožena 1 predloga} other{Uvoženih {count} predlog}}",
|
||||
"templatesExported": "{count, plural, =1{Izvožena 1 predloga v odložišče} other{Izvoženih {count} predlog v odložišče}}",
|
||||
"importFailed": "Uvoz ni uspel: {error}",
|
||||
"exportFailed": "Izvoz ni uspel: {error}",
|
||||
"resetToDefaults": "Ponastavi na privzeto",
|
||||
"resetToDefaultsConfirmation": "To bo izbrisalo vse prilagojene predloge in obnovilo 4 privzete predloge. Nadaljevati?",
|
||||
"reset": "Ponastavi",
|
||||
"resetComplete": "Predloge ponastavljene na privzeto",
|
||||
"noTemplates": "No templates available",
|
||||
"tapAddToCreate": "Tap + to create your first template",
|
||||
"noTemplates": "Ni razpoložljivih predlog",
|
||||
"tapAddToCreate": "Tapnite + za ustvarjanje prve predloge",
|
||||
"ok": "OK",
|
||||
"delete": "Izbriši",
|
||||
|
||||
"permissionsSection": "Dovoljenja",
|
||||
"locationPermission": "Dovoljenje za lokacijo",
|
||||
@@ -965,7 +966,7 @@
|
||||
"sampleMsgPriorityHelicopter": "PRIORITETA: Potrebna podpora helikopterja",
|
||||
"sampleMsgMedicalTeamEnRoute": "Medicinska ekipa na poti do vaše lokacije",
|
||||
"sampleMsgEvacHelicopter": "Helikopter za evakuacijo prihod 10 minut",
|
||||
"sampleMsgEmergencyResolved": "Nujnost rešena - vse čisto",
|
||||
"sampleMsgEmergencyResolved": "Nujna situacija rešena – vse čisto",
|
||||
"sampleMsgEmergencyStagingArea": " Nujno zbirališče",
|
||||
"sampleMsgEmergencyServices": "Nujne službe obveščene in se odzivajo",
|
||||
"sampleAlphaTeamLead": "Vodja Ekipe",
|
||||
@@ -985,33 +986,33 @@
|
||||
"rectangleDrawing": "Pravokotna risba",
|
||||
"coordinatesCopiedToClipboard": "Koordinate kopirane v odložišče",
|
||||
|
||||
"drawingShared": "Risba zemljevida",
|
||||
"drawingShared": "Risba deljena",
|
||||
"drawingHidden": "Risba skrita z zemljevida",
|
||||
"alreadyShared": "{count, plural, =1{1 že deljeno} other{{count} že deljeno}}",
|
||||
"newDrawingsShared": "Deljeno {count} nov{plural} risb{plural}",
|
||||
"drawingTools": "Orodja za risanje",
|
||||
"shareDrawing": "Deli Risbo",
|
||||
|
||||
"shareDrawing": "Deli risbo",
|
||||
"shareWithAllNearbyDevices": "Deli z vsemi bližnjimi napravami",
|
||||
"shareToRoom": "Deli v Sobo",
|
||||
"sendToPersistentStorage": "Pošlji v trajno shrambo sobe",
|
||||
"deleteDrawingConfirm": "Ali ste prepričani, da želite izbrisati to risbo?",
|
||||
"drawingDeleted": "Risba izbrisana",
|
||||
"yourDrawingsCount": "Vaše Risbe ({count})",
|
||||
"yourDrawingsCount": "Vaše risbe ({count})",
|
||||
"shared": "Deljeno",
|
||||
"line": "Črta",
|
||||
"rectangle": "Pravokotnik",
|
||||
|
||||
"updateAvailable": "Na Voljo Posodobitev",
|
||||
"currentVersion": "Trenutna",
|
||||
"latestVersion": "Najnovejša",
|
||||
"downloadUpdate": "Prenesi",
|
||||
"updateAvailable": "Na voljo je posodobitev",
|
||||
"currentVersion": "Trenutna različica",
|
||||
"latestVersion": "Najnovejša različica",
|
||||
"downloadUpdate": "Prenesi posodobitev",
|
||||
"updateLater": "Kasneje",
|
||||
|
||||
"cadastralParcels": "Katastrske parcele",
|
||||
"forestRoads": "Gozdne ceste",
|
||||
"showCadastralParcels": "Prikaži katastrske parcele",
|
||||
"showForestRoads": "Prikaži gozdne ceste",
|
||||
"wmsOverlays": "WMS Prekrivanja",
|
||||
"wmsOverlays": "WMS prekrivanja",
|
||||
|
||||
"recentMessages": "Nedavna sporočila",
|
||||
|
||||
@@ -1036,31 +1037,31 @@
|
||||
"wizardNext": "Naprej",
|
||||
"wizardGetStarted": "Začni",
|
||||
"wizardWelcomeTitle": "Dobrodošli v MeshCore SAR",
|
||||
"wizardWelcomeDescription": "Močno orodje za komunikacijo brez omrežja za reševalne operacije. Povežite se s svojo ekipo z uporabo mrežne radijske tehnologije, ko tradicionalna omrežja niso na voljo.",
|
||||
"wizardConnectingTitle": "Povezava z Radijem",
|
||||
"wizardConnectingDescription": "Povežite svoj pametni telefon z napravo MeshCore radio preko Bluetootha za začetek komunikacije brez omrežja.",
|
||||
"wizardConnectingFeature1": "Iskanje bližnjih naprav MeshCore",
|
||||
"wizardConnectingFeature2": "Združite s svojim radijem preko Bluetootha",
|
||||
"wizardConnectingFeature3": "Deluje popolnoma brez povezave - internet ni potreben",
|
||||
"wizardSimpleModeTitle": "Preprost Način",
|
||||
"wizardSimpleModeDescription": "Novi v mrežnem omrežju? Omogočite preprost način za poenostavljen vmesnik z le bistvenimi funkcijami.",
|
||||
"wizardSimpleModeFeature1": "Vmesnik prijazen za začetnike z osnovnimi funkcijami",
|
||||
"wizardSimpleModeFeature2": "Kadarkoli preklopite na napredni način v nastavitvah",
|
||||
"wizardWelcomeDescription": "Zmogljivo orodje za komunikacijo brez omrežja za reševalne operacije. S svojo ekipo se povežite z mesh radijsko tehnologijo, ko tradicionalna omrežja niso na voljo.",
|
||||
"wizardConnectingTitle": "Povezava z radijem",
|
||||
"wizardConnectingDescription": "Svoj telefon povežite z radijsko napravo MeshCore prek Bluetootha in začnite komunicirati brez omrežja.",
|
||||
"wizardConnectingFeature1": "Poišče bližnje naprave MeshCore",
|
||||
"wizardConnectingFeature2": "Seznanitev z radijsko napravo prek Bluetootha",
|
||||
"wizardConnectingFeature3": "Deluje povsem brez povezave — internet ni potreben",
|
||||
"wizardSimpleModeTitle": "Preprost način",
|
||||
"wizardSimpleModeDescription": "Ste novi v mesh omrežju? Vključite preprost način za poenostavljen vmesnik z osnovnimi funkcijami.",
|
||||
"wizardSimpleModeFeature1": "Vmesnik, prilagojen začetnikom, z osnovnimi funkcijami",
|
||||
"wizardSimpleModeFeature2": "Na napredni način lahko kadarkoli preklopite v nastavitvah",
|
||||
"wizardChannelTitle": "Kanali",
|
||||
"wizardChannelDescription": "Oddajajte sporočila vsem na kanalu, popolno za objave in usklajevanje cele ekipe.",
|
||||
"wizardChannelDescription": "Pošiljajte sporočila vsem na kanalu — idealno za obvestila in koordinacijo ekipe.",
|
||||
"wizardChannelFeature1": "Javni kanal za splošno komunikacijo ekipe",
|
||||
"wizardChannelFeature2": "Ustvarite kanale po meri za določene skupine",
|
||||
"wizardChannelFeature3": "Sporočila so samodejno posredovana preko mreže",
|
||||
"wizardChannelFeature3": "Sporočila se samodejno posredujejo prek mreže",
|
||||
"wizardContactsTitle": "Stiki",
|
||||
"wizardContactsDescription": "Vaši člani ekipe se samodejno prikažejo, ko se pridružijo mrežnemu omrežju. Pošljite jim neposredna sporočila ali si oglejte njihovo lokacijo.",
|
||||
"wizardContactsFeature1": "Stiki se odkrijejo samodejno",
|
||||
"wizardContactsFeature2": "Pošiljajte zasebna neposredna sporočila",
|
||||
"wizardContactsFeature3": "Oglejte si raven baterije in čas zadnjega ogleda",
|
||||
"wizardMapTitle": "Zemljevid & Lokacija",
|
||||
"wizardMapDescription": "Sledite svoji ekipi v realnem času in označite pomembne lokacije za reševalne operacije.",
|
||||
"wizardMapFeature1": "SAR označevalci za najdene osebe, požare in območja postavitve",
|
||||
"wizardMapFeature2": "Sledenje GPS članov ekipe v realnem času",
|
||||
"wizardMapFeature3": "Prenesite zemljevide brez povezave za oddaljene kraje",
|
||||
"wizardMapFeature4": "Narišite oblike in delite taktične informacije",
|
||||
"wizardContactsDescription": "Člani ekipe se prikažejo samodejno, ko se pridružijo mesh omrežju. Pošiljajte jim neposredna sporočila ali si oglejte njihovo lokacijo.",
|
||||
"wizardContactsFeature1": "Samodejno odkrivanje stikov",
|
||||
"wizardContactsFeature2": "Pošiljanje zasebnih neposrednih sporočil",
|
||||
"wizardContactsFeature3": "Prikaz stanja baterije in časa zadnje aktivnosti",
|
||||
"wizardMapTitle": "Zemljevid in lokacija",
|
||||
"wizardMapDescription": "Spremljajte svojo ekipo v realnem času in označujte ključne lokacije za reševalne operacije.",
|
||||
"wizardMapFeature1": "SAR označevalci za najdene osebe, požare in zbirna mesta",
|
||||
"wizardMapFeature2": "Sledenje članom ekipe z GPS v realnem času",
|
||||
"wizardMapFeature3": "Prenesite zemljevide za uporabo brez povezave",
|
||||
"wizardMapFeature4": "Rišite oblike in delite taktične informacije",
|
||||
"viewWelcomeTutorial": "Ogled vadnice dobrodošlice"
|
||||
}
|
||||
|
||||
1
lib/l10n/untranslated.json
Normal file
1
lib/l10n/untranslated.json
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_map/flutter_map.dart';
|
||||
import 'package:latlong2/latlong.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import '../models/location_trail.dart';
|
||||
@@ -27,6 +28,10 @@ class MapProvider with ChangeNotifier {
|
||||
// Imported trail (from GPX)
|
||||
LocationTrail? _importedTrail;
|
||||
|
||||
// Download area selection
|
||||
bool _isSelectingDownloadArea = false;
|
||||
LatLngBounds? _downloadAreaBounds;
|
||||
|
||||
LatLng? get targetLocation => _targetLocation;
|
||||
double? get targetZoom => _targetZoom;
|
||||
bool get shouldAnimate => _shouldAnimate;
|
||||
@@ -48,6 +53,10 @@ class MapProvider with ChangeNotifier {
|
||||
// Imported trail getters
|
||||
LocationTrail? get importedTrail => _importedTrail;
|
||||
|
||||
// Download area getters
|
||||
bool get isSelectingDownloadArea => _isSelectingDownloadArea;
|
||||
LatLngBounds? get downloadAreaBounds => _downloadAreaBounds;
|
||||
|
||||
void navigateToLocation({
|
||||
required LatLng location,
|
||||
double zoom = 15.0,
|
||||
@@ -302,4 +311,24 @@ class MapProvider with ChangeNotifier {
|
||||
_isTrailVisible = true;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// Enter download area selection mode with initial bounds
|
||||
void enterDownloadAreaMode(LatLngBounds initialBounds) {
|
||||
_isSelectingDownloadArea = true;
|
||||
_downloadAreaBounds = initialBounds;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// Exit download area selection mode
|
||||
void exitDownloadAreaMode() {
|
||||
_isSelectingDownloadArea = false;
|
||||
_downloadAreaBounds = null;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// Update the download area bounds (while dragging/resizing)
|
||||
void updateDownloadAreaBounds(LatLngBounds bounds) {
|
||||
_downloadAreaBounds = bounds;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -887,7 +887,44 @@ class _MapManagementScreenState extends State<MapManagementScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
/// Convert zoom level to user-friendly description
|
||||
String _getZoomDescription(int zoom) {
|
||||
if (zoom <= 5) {
|
||||
return 'Continental view (very low detail)';
|
||||
} else if (zoom <= 8) {
|
||||
return 'Country view (low detail)';
|
||||
} else if (zoom <= 10) {
|
||||
return 'Regional view (basic detail)';
|
||||
} else if (zoom <= 12) {
|
||||
return 'City view (moderate detail)';
|
||||
} else if (zoom <= 15) {
|
||||
return 'Neighborhood view (good detail)';
|
||||
} else if (zoom <= 17) {
|
||||
return 'Street view (high detail)';
|
||||
} else {
|
||||
return 'Building view (very high detail)';
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildDownloadCard() {
|
||||
// Calculate current bounds for preview
|
||||
LatLngBounds? previewBounds;
|
||||
try {
|
||||
final north = double.tryParse(_northController.text);
|
||||
final south = double.tryParse(_southController.text);
|
||||
final east = double.tryParse(_eastController.text);
|
||||
final west = double.tryParse(_westController.text);
|
||||
|
||||
if (north != null && south != null && east != null && west != null) {
|
||||
previewBounds = LatLngBounds(
|
||||
LatLng(south, west),
|
||||
LatLng(north, east),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
// Invalid bounds, preview will be null
|
||||
}
|
||||
|
||||
return Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
@@ -900,6 +937,80 @@ class _MapManagementScreenState extends State<MapManagementScreen> {
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Preview map (if bounds are valid)
|
||||
if (previewBounds != null) ...[
|
||||
Container(
|
||||
height: 200,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.grey[300]!),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: Stack(
|
||||
children: [
|
||||
FlutterMap(
|
||||
options: MapOptions(
|
||||
initialCenter: previewBounds.center,
|
||||
initialZoom: 12.0,
|
||||
minZoom: 1,
|
||||
maxZoom: 19,
|
||||
interactionOptions: const InteractionOptions(
|
||||
flags: InteractiveFlag.none, // Static preview
|
||||
),
|
||||
onMapReady: () {
|
||||
// Fit bounds after map is ready would require MapController
|
||||
// For now, center on bounds center
|
||||
},
|
||||
),
|
||||
children: [
|
||||
TileLayer(
|
||||
urlTemplate: _selectedLayer.urlTemplate,
|
||||
userAgentPackageName: 'com.meshcore.sar',
|
||||
),
|
||||
// Blue rectangle showing download area
|
||||
PolygonLayer(
|
||||
polygons: [
|
||||
Polygon(
|
||||
points: [
|
||||
LatLng(previewBounds.north, previewBounds.west),
|
||||
LatLng(previewBounds.north, previewBounds.east),
|
||||
LatLng(previewBounds.south, previewBounds.east),
|
||||
LatLng(previewBounds.south, previewBounds.west),
|
||||
],
|
||||
color: Colors.blue.withValues(alpha: 0.2),
|
||||
borderColor: Colors.blue,
|
||||
borderStrokeWidth: 3.0,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
// Label overlay
|
||||
Positioned(
|
||||
top: 8,
|
||||
left: 8,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black.withValues(alpha: 0.6),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
child: Text(
|
||||
'Download Area Preview',
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
|
||||
// Map Layer Selection
|
||||
DropdownButtonFormField<MapLayer>(
|
||||
initialValue: _selectedLayer,
|
||||
@@ -1010,13 +1121,22 @@ class _MapManagementScreenState extends State<MapManagementScreen> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(AppLocalizations.of(context)!.minZoom(_minZoom)),
|
||||
Text(
|
||||
AppLocalizations.of(context)!.minZoom(_minZoom),
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
Text(
|
||||
_getZoomDescription(_minZoom),
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: Colors.grey[600],
|
||||
),
|
||||
),
|
||||
Slider(
|
||||
value: _minZoom.toDouble(),
|
||||
min: 1,
|
||||
max: 19,
|
||||
divisions: 18,
|
||||
label: '$_minZoom',
|
||||
label: '$_minZoom - ${_getZoomDescription(_minZoom)}',
|
||||
onChanged: _isDownloading
|
||||
? null
|
||||
: (value) {
|
||||
@@ -1036,13 +1156,22 @@ class _MapManagementScreenState extends State<MapManagementScreen> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(AppLocalizations.of(context)!.maxZoom(_maxZoom)),
|
||||
Text(
|
||||
AppLocalizations.of(context)!.maxZoom(_maxZoom),
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
Text(
|
||||
_getZoomDescription(_maxZoom),
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: Colors.grey[600],
|
||||
),
|
||||
),
|
||||
Slider(
|
||||
value: _maxZoom.toDouble(),
|
||||
min: 1,
|
||||
max: 19,
|
||||
divisions: 18,
|
||||
label: '$_maxZoom',
|
||||
label: '$_maxZoom - ${_getZoomDescription(_maxZoom)}',
|
||||
onChanged: _isDownloading
|
||||
? null
|
||||
: (value) {
|
||||
|
||||
@@ -38,6 +38,7 @@ import '../widgets/map/drawing_toolbar.dart';
|
||||
import '../widgets/map/location_trail_layer.dart';
|
||||
import '../widgets/map/trail_controls.dart';
|
||||
import '../widgets/map/map_message_overlay.dart';
|
||||
import '../widgets/map/download_area_overlay.dart';
|
||||
import '../widgets/messages/sar_update_sheet.dart';
|
||||
import '../l10n/app_localizations.dart';
|
||||
import 'map_management_screen.dart';
|
||||
@@ -708,21 +709,10 @@ class _MapTabState extends State<MapTab> with AutomaticKeepAliveClientMixin {
|
||||
try {
|
||||
// Get current map bounds
|
||||
final bounds = _mapController.camera.visibleBounds;
|
||||
final currentZoom = _mapController.camera.zoom.round();
|
||||
|
||||
// Navigate to Map Management screen with pre-populated data
|
||||
final appProvider = context.read<AppProvider>();
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => MapManagementScreen(
|
||||
tileCacheService: appProvider.tileCacheService,
|
||||
initialLayer: _currentLayer,
|
||||
initialBounds: bounds,
|
||||
initialZoom: currentZoom,
|
||||
),
|
||||
),
|
||||
);
|
||||
// Enter download area selection mode (show preview overlay)
|
||||
final mapProvider = context.read<MapProvider>();
|
||||
mapProvider.enterDownloadAreaMode(bounds);
|
||||
} catch (e) {
|
||||
debugPrint('Error accessing map camera: $e');
|
||||
}
|
||||
@@ -1550,13 +1540,15 @@ class _MapTabState extends State<MapTab> with AutomaticKeepAliveClientMixin {
|
||||
widget.onNavigateToMessages?.call();
|
||||
},
|
||||
),
|
||||
// User location marker
|
||||
// User location marker with directional pointer
|
||||
if (_markerService.generateUserLocationMarker(
|
||||
position: _locationService.currentPosition,
|
||||
heading: _currentHeading,
|
||||
context: context,
|
||||
) != null)
|
||||
_markerService.generateUserLocationMarker(
|
||||
position: _locationService.currentPosition,
|
||||
heading: _currentHeading,
|
||||
context: context,
|
||||
)!,
|
||||
// Measurement point 1 marker
|
||||
@@ -1745,6 +1737,36 @@ class _MapTabState extends State<MapTab> with AutomaticKeepAliveClientMixin {
|
||||
}
|
||||
},
|
||||
),
|
||||
// Download area selection polygon (rendered on top when in selection mode)
|
||||
Consumer<MapProvider>(
|
||||
builder: (context, mapProvider, _) {
|
||||
if (!mapProvider.isSelectingDownloadArea || mapProvider.downloadAreaBounds == null) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
final bounds = mapProvider.downloadAreaBounds!;
|
||||
|
||||
// Add padding to the bounds so the rectangle is visible within the screen
|
||||
// Calculate 5% padding on each side
|
||||
final latPadding = (bounds.north - bounds.south) * 0.05;
|
||||
final lonPadding = (bounds.east - bounds.west) * 0.05;
|
||||
|
||||
return PolygonLayer(
|
||||
polygons: [
|
||||
Polygon(
|
||||
points: [
|
||||
LatLng(bounds.north - latPadding, bounds.west + lonPadding), // Top-left
|
||||
LatLng(bounds.north - latPadding, bounds.east - lonPadding), // Top-right
|
||||
LatLng(bounds.south + latPadding, bounds.east - lonPadding), // Bottom-right
|
||||
LatLng(bounds.south + latPadding, bounds.west + lonPadding), // Bottom-left
|
||||
],
|
||||
color: Colors.blue.withValues(alpha: 0.2),
|
||||
borderColor: Colors.blue,
|
||||
borderStrokeWidth: 3.0,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
@@ -1761,6 +1783,52 @@ class _MapTabState extends State<MapTab> with AutomaticKeepAliveClientMixin {
|
||||
],
|
||||
),
|
||||
),
|
||||
// Download area overlay (shown when in download area selection mode)
|
||||
Consumer<MapProvider>(
|
||||
builder: (context, mapProvider, _) {
|
||||
if (!mapProvider.isSelectingDownloadArea || mapProvider.downloadAreaBounds == null) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return DownloadAreaOverlay(
|
||||
bounds: mapProvider.downloadAreaBounds!,
|
||||
onConfirm: () {
|
||||
// Navigate to map management screen with selected bounds
|
||||
final bounds = mapProvider.downloadAreaBounds!;
|
||||
final zoom = _mapController.camera.zoom.round();
|
||||
|
||||
// Find matching layer from MapLayer.allLayers to avoid instance mismatch
|
||||
// Only pass initialLayer if it's in allLayers (standard layers only)
|
||||
MapLayer? initialLayer;
|
||||
try {
|
||||
initialLayer = MapLayer.allLayers.firstWhere(
|
||||
(layer) => layer.type == _currentLayer.type,
|
||||
);
|
||||
} catch (e) {
|
||||
// Current layer not in allLayers (WMS/vector), don't pass it
|
||||
initialLayer = null;
|
||||
}
|
||||
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => MapManagementScreen(
|
||||
tileCacheService: _tileCache,
|
||||
initialBounds: bounds,
|
||||
initialLayer: initialLayer,
|
||||
initialZoom: zoom,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Exit download area selection mode
|
||||
mapProvider.exitDownloadAreaMode();
|
||||
},
|
||||
onCancel: () {
|
||||
// Exit download area selection mode
|
||||
mapProvider.exitDownloadAreaMode();
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
// Exit fullscreen button - top left (only shown in fullscreen mode)
|
||||
if (_isFullscreen)
|
||||
Positioned(
|
||||
|
||||
@@ -412,9 +412,10 @@ class LocationTrackingService {
|
||||
debugPrint('✅ [LocationTracking] Initial position set without broadcast');
|
||||
debugPrint(' Next broadcast allowed in ${minTimeIntervalSeconds}s');
|
||||
} catch (e) {
|
||||
debugPrint('❌ [LocationTracking] Failed to set initial position: $e');
|
||||
onError?.call('Failed to set initial position: $e');
|
||||
debugPrint('⚠️ [LocationTracking] Failed to set initial position: $e');
|
||||
debugPrint(' Will retry on next GPS update');
|
||||
// Don't mark as set on failure, so it will retry on next update
|
||||
// Don't call onError - this is not critical since it will retry automatically
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:latlong2/latlong.dart';
|
||||
import 'package:geolocator/geolocator.dart';
|
||||
import '../models/contact.dart';
|
||||
import '../models/sar_marker.dart';
|
||||
import '../widgets/map/location_pointer.dart';
|
||||
|
||||
/// Centralized service for map marker management.
|
||||
///
|
||||
@@ -222,47 +223,31 @@ class MapMarkerService {
|
||||
}).toList();
|
||||
}
|
||||
|
||||
/// Generate user location marker.
|
||||
/// Generate user location marker with directional pointer.
|
||||
///
|
||||
/// Parameters:
|
||||
/// - [position]: Current GPS position
|
||||
/// - [heading]: Current heading in degrees (0-360, where 0 = North)
|
||||
/// Pass null or -1 if heading unavailable
|
||||
/// - [context]: Build context for theme access
|
||||
///
|
||||
/// Returns null if position is unavailable.
|
||||
Marker? generateUserLocationMarker({
|
||||
required Position? position,
|
||||
double? heading,
|
||||
required BuildContext context,
|
||||
}) {
|
||||
if (position == null) return null;
|
||||
|
||||
return Marker(
|
||||
point: LatLng(position.latitude, position.longitude),
|
||||
width: 40,
|
||||
height: 40,
|
||||
rotate: false, // Don't rotate with map
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.primary.withValues(alpha: 0.3),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Container(
|
||||
margin: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
shape: BoxShape.circle,
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: Colors.black26,
|
||||
blurRadius: 4,
|
||||
),
|
||||
],
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.my_location,
|
||||
color: Colors.white,
|
||||
size: 16,
|
||||
),
|
||||
),
|
||||
width: 60,
|
||||
height: 60,
|
||||
rotate: false, // Don't rotate with map - we handle rotation internally
|
||||
child: LocationPointer(
|
||||
heading: heading,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
size: 60,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -468,8 +468,9 @@ class MeshCoreBleService {
|
||||
required double latitude,
|
||||
required double longitude,
|
||||
}) async {
|
||||
// This command returns OK (0x00) response, so wait for acknowledgment
|
||||
await _commandSender.writeDataAndWaitForAck(
|
||||
// This command updates device's advertised location
|
||||
// Fire-and-forget - no ACK needed since actual broadcast happens via sendSelfAdvert
|
||||
await _commandSender.writeData(
|
||||
FrameBuilder.buildSetAdvertLatLon(
|
||||
latitude: latitude,
|
||||
longitude: longitude,
|
||||
|
||||
108
lib/widgets/map/download_area_overlay.dart
Normal file
108
lib/widgets/map/download_area_overlay.dart
Normal file
@@ -0,0 +1,108 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_map/flutter_map.dart';
|
||||
|
||||
/// Overlay widget that displays controls for download area selection.
|
||||
/// The actual polygon should be rendered inside FlutterMap's children.
|
||||
class DownloadAreaOverlay extends StatelessWidget {
|
||||
final LatLngBounds bounds;
|
||||
final VoidCallback onConfirm;
|
||||
final VoidCallback onCancel;
|
||||
|
||||
const DownloadAreaOverlay({
|
||||
super.key,
|
||||
required this.bounds,
|
||||
required this.onConfirm,
|
||||
required this.onCancel,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
children: [
|
||||
// Control buttons at the top
|
||||
Positioned(
|
||||
top: 16,
|
||||
left: 16,
|
||||
right: 16,
|
||||
child: Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Text(
|
||||
'Download Area Selection',
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'The blue rectangle shows the area to be downloaded. '
|
||||
'To change the area, tap Cancel and select download again.',
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
Expanded(
|
||||
child: OutlinedButton.icon(
|
||||
onPressed: onCancel,
|
||||
icon: const Icon(Icons.close),
|
||||
label: const Text('Cancel'),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: FilledButton.icon(
|
||||
onPressed: onConfirm,
|
||||
icon: const Icon(Icons.check),
|
||||
label: const Text('Confirm'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// Area info at the bottom
|
||||
Positioned(
|
||||
bottom: 16,
|
||||
left: 16,
|
||||
right: 16,
|
||||
child: Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Area Bounds',
|
||||
style: Theme.of(context).textTheme.titleSmall,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'N: ${bounds.north.toStringAsFixed(4)}° '
|
||||
'S: ${bounds.south.toStringAsFixed(4)}°',
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
Text(
|
||||
'E: ${bounds.east.toStringAsFixed(4)}° '
|
||||
'W: ${bounds.west.toStringAsFixed(4)}°',
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
175
lib/widgets/map/location_pointer.dart
Normal file
175
lib/widgets/map/location_pointer.dart
Normal file
@@ -0,0 +1,175 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// A navigation arrow pointer that indicates the user's location and direction of travel.
|
||||
///
|
||||
/// The pointer consists of:
|
||||
/// - An outer semi-transparent circle representing GPS accuracy
|
||||
/// - An inner triangular arrow pointing in the direction of travel/heading
|
||||
/// - Optional rotation based on compass or GPS heading
|
||||
class LocationPointer extends StatelessWidget {
|
||||
/// The heading in degrees (0-360, where 0 = North, 90 = East)
|
||||
/// If null or -1, the pointer will not rotate
|
||||
final double? heading;
|
||||
|
||||
/// The primary color for the pointer
|
||||
final Color color;
|
||||
|
||||
/// The size of the entire widget
|
||||
final double size;
|
||||
|
||||
const LocationPointer({
|
||||
super.key,
|
||||
this.heading,
|
||||
required this.color,
|
||||
this.size = 40.0,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Determine if we have valid heading data
|
||||
final hasValidHeading = heading != null && heading! >= 0;
|
||||
|
||||
// Calculate rotation angle (convert heading to radians)
|
||||
final rotationAngle = hasValidHeading ? (heading! * 3.14159 / 180.0) : 0.0;
|
||||
|
||||
return SizedBox(
|
||||
width: size,
|
||||
height: size,
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
// Outer accuracy circle (very subtle, light blue)
|
||||
Container(
|
||||
width: size * 0.6,
|
||||
height: size * 0.6,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF4285F4).withValues(alpha: 0.2),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
// Inner rotatable arrow pointer (much larger - 90% of size)
|
||||
Transform.rotate(
|
||||
angle: rotationAngle,
|
||||
child: CustomPaint(
|
||||
size: Size(size * 0.9, size * 0.9),
|
||||
painter: _NavigationPointerPainter(
|
||||
color: color,
|
||||
hasValidHeading: hasValidHeading,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Custom painter that draws a navigation arrow pointer
|
||||
class _NavigationPointerPainter extends CustomPainter {
|
||||
final Color color;
|
||||
final bool hasValidHeading;
|
||||
|
||||
_NavigationPointerPainter({
|
||||
required this.color,
|
||||
required this.hasValidHeading,
|
||||
});
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final center = Offset(size.width / 2, size.height / 2);
|
||||
final width = size.width;
|
||||
final height = size.height;
|
||||
|
||||
if (hasValidHeading) {
|
||||
// Create navigation arrow with V-shaped cutout at bottom
|
||||
final arrowPath = Path();
|
||||
|
||||
// Top point (sharp tip)
|
||||
arrowPath.moveTo(center.dx, height * 0.08);
|
||||
|
||||
// Right side down to bottom right
|
||||
arrowPath.lineTo(center.dx + width * 0.42, height * 0.92);
|
||||
|
||||
// V-cutout at bottom - right side to center
|
||||
arrowPath.lineTo(center.dx, height * 0.70);
|
||||
|
||||
// V-cutout - center to left side
|
||||
arrowPath.lineTo(center.dx - width * 0.42, height * 0.92);
|
||||
|
||||
// Left side back up to top
|
||||
arrowPath.lineTo(center.dx, height * 0.08);
|
||||
|
||||
arrowPath.close();
|
||||
|
||||
// Draw shadow for depth
|
||||
final shadowPaint = Paint()
|
||||
..color = Colors.black.withValues(alpha: 0.25)
|
||||
..style = PaintingStyle.fill
|
||||
..maskFilter = const MaskFilter.blur(BlurStyle.normal, 4);
|
||||
|
||||
canvas.save();
|
||||
canvas.translate(2, 2);
|
||||
canvas.drawPath(arrowPath, shadowPaint);
|
||||
canvas.restore();
|
||||
|
||||
// Left side (lighter cyan) - #75D5F0
|
||||
final leftSidePath = Path();
|
||||
leftSidePath.moveTo(center.dx, height * 0.08);
|
||||
leftSidePath.lineTo(center.dx - width * 0.42, height * 0.92);
|
||||
leftSidePath.lineTo(center.dx, height * 0.70);
|
||||
leftSidePath.close();
|
||||
|
||||
final leftPaint = Paint()
|
||||
..color = const Color(0xFF75D5F0)
|
||||
..style = PaintingStyle.fill;
|
||||
canvas.drawPath(leftSidePath, leftPaint);
|
||||
|
||||
// Right side (darker cyan) - #00B8E6
|
||||
final rightSidePath = Path();
|
||||
rightSidePath.moveTo(center.dx, height * 0.08);
|
||||
rightSidePath.lineTo(center.dx, height * 0.70);
|
||||
rightSidePath.lineTo(center.dx + width * 0.42, height * 0.92);
|
||||
rightSidePath.close();
|
||||
|
||||
final rightPaint = Paint()
|
||||
..color = const Color(0xFF00B8E6)
|
||||
..style = PaintingStyle.fill;
|
||||
canvas.drawPath(rightSidePath, rightPaint);
|
||||
|
||||
// Optional: Draw white border for contrast
|
||||
final borderPaint = Paint()
|
||||
..color = Colors.white
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = 2.0
|
||||
..strokeJoin = StrokeJoin.round;
|
||||
canvas.drawPath(arrowPath, borderPaint);
|
||||
|
||||
} else {
|
||||
// No heading available - draw a blue circle with white border
|
||||
final circlePaint = Paint()
|
||||
..color = const Color(0xFF4285F4)
|
||||
..style = PaintingStyle.fill;
|
||||
|
||||
canvas.drawCircle(center, width * 0.4, circlePaint);
|
||||
|
||||
// White border
|
||||
final borderPaint = Paint()
|
||||
..color = Colors.white
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = 2.5;
|
||||
canvas.drawCircle(center, width * 0.4, borderPaint);
|
||||
|
||||
// Center white dot
|
||||
final centerDot = Paint()
|
||||
..color = Colors.white
|
||||
..style = PaintingStyle.fill;
|
||||
canvas.drawCircle(center, width * 0.15, centerDot);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(_NavigationPointerPainter oldDelegate) {
|
||||
return oldDelegate.color != color ||
|
||||
oldDelegate.hasValidHeading != hasValidHeading;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user