mirror of
https://github.com/zjs81/meshcore-open.git
synced 2026-07-06 08:46:40 +10:00
Merge pull request #461 from ericszimmermann/ez_alternative_maps
Alternative Maps
This commit is contained in:
@@ -48,7 +48,7 @@ MeshCore Open is a cross-platform mobile application for communicating with Mesh
|
||||
- **Live Map View**: Real-time visualization of mesh network nodes on an interactive map
|
||||
- **Node Filtering**: Filter by node type (chat, repeater, sensor) and time range
|
||||
- **Location Sharing**: Share GPS coordinates and custom markers with contacts
|
||||
- **Offline Maps**: Download map tiles for offline use in remote areas
|
||||
- **Offline Maps**: Download map tiles for offline use in remote areas (with [StadiaMaps](https://stadiamaps.com/pricing/) Free Subscription API-Key)
|
||||
- **MGRS Coordinates**: Support for Military Grid Reference System coordinate format
|
||||
|
||||
### Device Management
|
||||
|
||||
@@ -129,6 +129,10 @@ A dedicated sub-screen for app-level preferences (nothing here is sent to the de
|
||||
- **Show Other Nodes**: Toggle room/sensor markers
|
||||
- **Time Filter**: All time / Last 1h / Last 6h / Last 24h / Last week
|
||||
- **Units**: Metric / Imperial
|
||||
- **Raster Tile Source**: Sets the MAP theme and with that from where to get the map tile data:
|
||||
- OpenStreetMap (Auto/Standard/Dark) is provided by the free OpenStreetMap tile server. (Can only be used for live view or already cached.)
|
||||
- Stamen Terrain / AlidadeSmooth Dark / Outdoors / OSM Bright are [StadiaMaps.com raster tile maps](https://stadiamaps.com/products/maps/interactive-basemaps/) for which you can choose an Hosted Endpoint (Worldwide / Europe hosted) and have to provide the API key to your subscription. (You can cache these maps for offline Map usage.)
|
||||
There will be no account provided by meshcore-open. You will have to get you own subscription. StadiaMaps offers a [free](https://stadiamaps.com/pricing/) subscription to download up to 200'000 Standart Raster Basemap tiles.
|
||||
- **Offline Map Cache**: Navigate to tile download screen
|
||||
|
||||
### Translation
|
||||
|
||||
@@ -242,6 +242,19 @@
|
||||
"appSettings_last6Hours": "Последни 6 часа",
|
||||
"appSettings_last24Hours": "Последно 24 часа",
|
||||
"appSettings_lastWeek": "Миналата седмица",
|
||||
"appSettings_rasterTileSource": "Източник на растерни плочки",
|
||||
"appSettings_stadiaEndpoint": "Крайна точка на Stadia",
|
||||
"appSettings_stadiaApiKey": "API ключ за Stadia",
|
||||
"appSettings_stadiaApiKeyRequired": "Задължително за използване на Stadia Maps",
|
||||
"appSettings_stadiaApiKeyConfigured": "Конфигурирано: {maskedKey}",
|
||||
"@appSettings_stadiaApiKeyConfigured": {
|
||||
"placeholders": {
|
||||
"maskedKey": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"appSettings_stadiaApiKeyDialogDescription": "Въведете своя API ключ за Stadia Maps. Приложението го използва за заявки за растерни плочки.",
|
||||
"appSettings_offlineMapCache": "Кеш на офлайн карти",
|
||||
"appSettings_noAreaSelected": "Няма избрана област",
|
||||
"appSettings_areaSelectedZoom": "Избрана е област (мащаб {minZoom}-{maxZoom})",
|
||||
@@ -771,6 +784,56 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_cachedTilesLabel": "Cached tiles",
|
||||
"mapCache_cachedTileSummaryLabel": "Cached tile summary",
|
||||
"mapCache_bulkDownloadDisabledForSource": "Offline bulk downloads are disabled for {source}.",
|
||||
"@mapCache_bulkDownloadDisabledForSource": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_bulkDownloadDisabledInConfig": "Offline bulk downloads are disabled for {source} in this app configuration.",
|
||||
"@mapCache_bulkDownloadDisabledInConfig": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summarySource": "Source: {source}",
|
||||
"@mapCache_summarySource": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryCachedTilesForSource": "Cached tiles for source: {count}",
|
||||
"@mapCache_summaryCachedTilesForSource": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryCachedInSelection": "Cached in selected area/zoom: {count}",
|
||||
"@mapCache_summaryCachedInSelection": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryApproxCacheSize": "Approx cache size: {size}",
|
||||
"@mapCache_summaryApproxCacheSize": {
|
||||
"placeholders": {
|
||||
"size": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_boundsLabel": "Север {north}, Юг {south}, Изток {east}, Запад {west}",
|
||||
"@mapCache_boundsLabel": {
|
||||
"placeholders": {
|
||||
|
||||
@@ -242,6 +242,19 @@
|
||||
"appSettings_last6Hours": "Letzte 6 Stunden",
|
||||
"appSettings_last24Hours": "Letzte 24 Stunden",
|
||||
"appSettings_lastWeek": "Letzte Woche",
|
||||
"appSettings_rasterTileSource": "Quelle für Rasterkacheln",
|
||||
"appSettings_stadiaEndpoint": "Stadia-Endpunkt",
|
||||
"appSettings_stadiaApiKey": "Stadia-API-Schlüssel",
|
||||
"appSettings_stadiaApiKeyRequired": "Für die Nutzung von Stadia Maps erforderlich",
|
||||
"appSettings_stadiaApiKeyConfigured": "Konfiguriert: {maskedKey}",
|
||||
"@appSettings_stadiaApiKeyConfigured": {
|
||||
"placeholders": {
|
||||
"maskedKey": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"appSettings_stadiaApiKeyDialogDescription": "Geben Sie Ihren Stadia-Maps-API-Schlüssel ein. Die App verwendet ihn für Rasterkachel-Anfragen.",
|
||||
"appSettings_offlineMapCache": "Offline-Karten-Cache",
|
||||
"appSettings_noAreaSelected": "Kein Bereich ausgewählt",
|
||||
"appSettings_areaSelectedZoom": "Bereich ausgewählt (Zoom {minZoom}-{maxZoom})",
|
||||
@@ -793,6 +806,56 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_cachedTilesLabel": "Cached tiles",
|
||||
"mapCache_cachedTileSummaryLabel": "Cached tile summary",
|
||||
"mapCache_bulkDownloadDisabledForSource": "Offline bulk downloads are disabled for {source}.",
|
||||
"@mapCache_bulkDownloadDisabledForSource": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_bulkDownloadDisabledInConfig": "Offline bulk downloads are disabled for {source} in this app configuration.",
|
||||
"@mapCache_bulkDownloadDisabledInConfig": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summarySource": "Source: {source}",
|
||||
"@mapCache_summarySource": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryCachedTilesForSource": "Cached tiles for source: {count}",
|
||||
"@mapCache_summaryCachedTilesForSource": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryCachedInSelection": "Cached in selected area/zoom: {count}",
|
||||
"@mapCache_summaryCachedInSelection": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryApproxCacheSize": "Approx cache size: {size}",
|
||||
"@mapCache_summaryApproxCacheSize": {
|
||||
"placeholders": {
|
||||
"size": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_boundsLabel": "N {north}, S {south}, E {east}, W {west}",
|
||||
"@mapCache_boundsLabel": {
|
||||
"placeholders": {
|
||||
|
||||
@@ -363,6 +363,19 @@
|
||||
"appSettings_last6Hours": "Last 6 hours",
|
||||
"appSettings_last24Hours": "Last 24 hours",
|
||||
"appSettings_lastWeek": "Last week",
|
||||
"appSettings_rasterTileSource": "Raster Tile Source",
|
||||
"appSettings_stadiaEndpoint": "Stadia Endpoint",
|
||||
"appSettings_stadiaApiKey": "Stadia API Key",
|
||||
"appSettings_stadiaApiKeyRequired": "Required for Stadia Maps usage",
|
||||
"appSettings_stadiaApiKeyConfigured": "Configured: {maskedKey}",
|
||||
"@appSettings_stadiaApiKeyConfigured": {
|
||||
"placeholders": {
|
||||
"maskedKey": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"appSettings_stadiaApiKeyDialogDescription": "Enter your Stadia Maps API key. This app uses it for raster tile requests.",
|
||||
"appSettings_offlineMapCache": "Offline Map Cache",
|
||||
"appSettings_unitsTitle": "Units",
|
||||
"appSettings_unitsMetric": "Metric (m / km)",
|
||||
@@ -1093,6 +1106,56 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_cachedTilesLabel": "Cached tiles",
|
||||
"mapCache_cachedTileSummaryLabel": "Cached tile summary",
|
||||
"mapCache_bulkDownloadDisabledForSource": "Offline bulk downloads are disabled for {source}.",
|
||||
"@mapCache_bulkDownloadDisabledForSource": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_bulkDownloadDisabledInConfig": "Offline bulk downloads are disabled for {source} in this app configuration.",
|
||||
"@mapCache_bulkDownloadDisabledInConfig": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summarySource": "Source: {source}",
|
||||
"@mapCache_summarySource": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryCachedTilesForSource": "Cached tiles for source: {count}",
|
||||
"@mapCache_summaryCachedTilesForSource": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryCachedInSelection": "Cached in selected area/zoom: {count}",
|
||||
"@mapCache_summaryCachedInSelection": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryApproxCacheSize": "Approx cache size: {size}",
|
||||
"@mapCache_summaryApproxCacheSize": {
|
||||
"placeholders": {
|
||||
"size": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_boundsLabel": "N {north}, S {south}, E {east}, W {west}",
|
||||
"@mapCache_boundsLabel": {
|
||||
"placeholders": {
|
||||
|
||||
@@ -242,6 +242,19 @@
|
||||
"appSettings_last6Hours": "Últimas 6 horas",
|
||||
"appSettings_last24Hours": "Últimas 24 horas",
|
||||
"appSettings_lastWeek": "La semana pasada",
|
||||
"appSettings_rasterTileSource": "Fuente de teselas ráster",
|
||||
"appSettings_stadiaEndpoint": "Punto de acceso de Stadia",
|
||||
"appSettings_stadiaApiKey": "Clave API de Stadia",
|
||||
"appSettings_stadiaApiKeyRequired": "Obligatorio para usar Stadia Maps",
|
||||
"appSettings_stadiaApiKeyConfigured": "Configurado: {maskedKey}",
|
||||
"@appSettings_stadiaApiKeyConfigured": {
|
||||
"placeholders": {
|
||||
"maskedKey": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"appSettings_stadiaApiKeyDialogDescription": "Introduce tu clave API de Stadia Maps. La aplicación la usa para solicitar teselas ráster.",
|
||||
"appSettings_offlineMapCache": "Caché de mapa sin conexión",
|
||||
"appSettings_noAreaSelected": "No se ha seleccionado ningún área",
|
||||
"appSettings_areaSelectedZoom": "Área seleccionada (zoom {minZoom}-{maxZoom})",
|
||||
@@ -793,6 +806,56 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_cachedTilesLabel": "Cached tiles",
|
||||
"mapCache_cachedTileSummaryLabel": "Cached tile summary",
|
||||
"mapCache_bulkDownloadDisabledForSource": "Offline bulk downloads are disabled for {source}.",
|
||||
"@mapCache_bulkDownloadDisabledForSource": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_bulkDownloadDisabledInConfig": "Offline bulk downloads are disabled for {source} in this app configuration.",
|
||||
"@mapCache_bulkDownloadDisabledInConfig": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summarySource": "Source: {source}",
|
||||
"@mapCache_summarySource": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryCachedTilesForSource": "Cached tiles for source: {count}",
|
||||
"@mapCache_summaryCachedTilesForSource": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryCachedInSelection": "Cached in selected area/zoom: {count}",
|
||||
"@mapCache_summaryCachedInSelection": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryApproxCacheSize": "Approx cache size: {size}",
|
||||
"@mapCache_summaryApproxCacheSize": {
|
||||
"placeholders": {
|
||||
"size": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_boundsLabel": "N {north}, S {south}, E {east}, W {west}",
|
||||
"@mapCache_boundsLabel": {
|
||||
"placeholders": {
|
||||
|
||||
@@ -242,6 +242,19 @@
|
||||
"appSettings_last6Hours": "Dernières 6 heures",
|
||||
"appSettings_last24Hours": "Dernières 24 heures",
|
||||
"appSettings_lastWeek": "La semaine dernière",
|
||||
"appSettings_rasterTileSource": "Source de tuiles raster",
|
||||
"appSettings_stadiaEndpoint": "Point de terminaison Stadia",
|
||||
"appSettings_stadiaApiKey": "Clé API Stadia",
|
||||
"appSettings_stadiaApiKeyRequired": "Requis pour l’utilisation de Stadia Maps",
|
||||
"appSettings_stadiaApiKeyConfigured": "Configuré : {maskedKey}",
|
||||
"@appSettings_stadiaApiKeyConfigured": {
|
||||
"placeholders": {
|
||||
"maskedKey": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"appSettings_stadiaApiKeyDialogDescription": "Entrez votre clé API Stadia Maps. Cette application l’utilise pour les requêtes de tuiles raster.",
|
||||
"appSettings_offlineMapCache": "Cache de carte hors ligne",
|
||||
"appSettings_noAreaSelected": "Aucune zone sélectionnée",
|
||||
"appSettings_areaSelectedZoom": "Zone sélectionnée (zoom {minZoom}-{maxZoom})",
|
||||
@@ -793,6 +806,56 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_cachedTilesLabel": "Cached tiles",
|
||||
"mapCache_cachedTileSummaryLabel": "Cached tile summary",
|
||||
"mapCache_bulkDownloadDisabledForSource": "Offline bulk downloads are disabled for {source}.",
|
||||
"@mapCache_bulkDownloadDisabledForSource": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_bulkDownloadDisabledInConfig": "Offline bulk downloads are disabled for {source} in this app configuration.",
|
||||
"@mapCache_bulkDownloadDisabledInConfig": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summarySource": "Source: {source}",
|
||||
"@mapCache_summarySource": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryCachedTilesForSource": "Cached tiles for source: {count}",
|
||||
"@mapCache_summaryCachedTilesForSource": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryCachedInSelection": "Cached in selected area/zoom: {count}",
|
||||
"@mapCache_summaryCachedInSelection": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryApproxCacheSize": "Approx cache size: {size}",
|
||||
"@mapCache_summaryApproxCacheSize": {
|
||||
"placeholders": {
|
||||
"size": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_boundsLabel": "N {north}, S {south}, E {east}, O {west}",
|
||||
"@mapCache_boundsLabel": {
|
||||
"placeholders": {
|
||||
|
||||
@@ -334,6 +334,19 @@
|
||||
"appSettings_last6Hours": "Utolsó 6 óra",
|
||||
"appSettings_last24Hours": "Az elmúlt 24 óra",
|
||||
"appSettings_lastWeek": "Múlt héten",
|
||||
"appSettings_rasterTileSource": "Raszteres csempeforrás",
|
||||
"appSettings_stadiaEndpoint": "Stadia végpont",
|
||||
"appSettings_stadiaApiKey": "Stadia API-kulcs",
|
||||
"appSettings_stadiaApiKeyRequired": "A Stadia Maps használatához kötelező",
|
||||
"appSettings_stadiaApiKeyConfigured": "Konfigurálva: {maskedKey}",
|
||||
"@appSettings_stadiaApiKeyConfigured": {
|
||||
"placeholders": {
|
||||
"maskedKey": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"appSettings_stadiaApiKeyDialogDescription": "Adja meg a Stadia Maps API-kulcsát. Az alkalmazás ezt használja raszteres csempekérésekhez.",
|
||||
"appSettings_offlineMapCache": "Offline térképgyorsítótár",
|
||||
"appSettings_unitsTitle": "Egységek",
|
||||
"appSettings_unitsMetric": "Metrikus (m/km)",
|
||||
@@ -954,6 +967,56 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_cachedTilesLabel": "Cached tiles",
|
||||
"mapCache_cachedTileSummaryLabel": "Cached tile summary",
|
||||
"mapCache_bulkDownloadDisabledForSource": "Offline bulk downloads are disabled for {source}.",
|
||||
"@mapCache_bulkDownloadDisabledForSource": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_bulkDownloadDisabledInConfig": "Offline bulk downloads are disabled for {source} in this app configuration.",
|
||||
"@mapCache_bulkDownloadDisabledInConfig": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summarySource": "Source: {source}",
|
||||
"@mapCache_summarySource": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryCachedTilesForSource": "Cached tiles for source: {count}",
|
||||
"@mapCache_summaryCachedTilesForSource": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryCachedInSelection": "Cached in selected area/zoom: {count}",
|
||||
"@mapCache_summaryCachedInSelection": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryApproxCacheSize": "Approx cache size: {size}",
|
||||
"@mapCache_summaryApproxCacheSize": {
|
||||
"placeholders": {
|
||||
"size": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_boundsLabel": "É {north}, D {south}, K {east}, Ny {west}",
|
||||
"@mapCache_boundsLabel": {
|
||||
"placeholders": {
|
||||
|
||||
@@ -240,6 +240,19 @@
|
||||
"appSettings_last6Hours": "Ultimi 6 ore",
|
||||
"appSettings_last24Hours": "Ultime 24 ore",
|
||||
"appSettings_lastWeek": "La settimana scorsa",
|
||||
"appSettings_rasterTileSource": "Sorgente tile raster",
|
||||
"appSettings_stadiaEndpoint": "Endpoint Stadia",
|
||||
"appSettings_stadiaApiKey": "Chiave API Stadia",
|
||||
"appSettings_stadiaApiKeyRequired": "Obbligatoria per usare Stadia Maps",
|
||||
"appSettings_stadiaApiKeyConfigured": "Configurata: {maskedKey}",
|
||||
"@appSettings_stadiaApiKeyConfigured": {
|
||||
"placeholders": {
|
||||
"maskedKey": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"appSettings_stadiaApiKeyDialogDescription": "Inserisci la chiave API di Stadia Maps. Questa app la usa per le richieste di tile raster.",
|
||||
"appSettings_offlineMapCache": "Cache Mappa Offline",
|
||||
"appSettings_noAreaSelected": "Nessun'area selezionata",
|
||||
"appSettings_areaSelectedZoom": "Area selezionata (zoom {minZoom}-{maxZoom})",
|
||||
@@ -837,6 +850,56 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_cachedTilesLabel": "Cached tiles",
|
||||
"mapCache_cachedTileSummaryLabel": "Cached tile summary",
|
||||
"mapCache_bulkDownloadDisabledForSource": "Offline bulk downloads are disabled for {source}.",
|
||||
"@mapCache_bulkDownloadDisabledForSource": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_bulkDownloadDisabledInConfig": "Offline bulk downloads are disabled for {source} in this app configuration.",
|
||||
"@mapCache_bulkDownloadDisabledInConfig": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summarySource": "Source: {source}",
|
||||
"@mapCache_summarySource": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryCachedTilesForSource": "Cached tiles for source: {count}",
|
||||
"@mapCache_summaryCachedTilesForSource": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryCachedInSelection": "Cached in selected area/zoom: {count}",
|
||||
"@mapCache_summaryCachedInSelection": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryApproxCacheSize": "Approx cache size: {size}",
|
||||
"@mapCache_summaryApproxCacheSize": {
|
||||
"placeholders": {
|
||||
"size": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_boundsLabel": "N {north}, S {south}, E {east}, W {west}",
|
||||
"@mapCache_boundsLabel": {
|
||||
"placeholders": {
|
||||
|
||||
@@ -340,6 +340,19 @@
|
||||
"appSettings_last6Hours": "過去6時間",
|
||||
"appSettings_last24Hours": "過去24時間",
|
||||
"appSettings_lastWeek": "過去1週間",
|
||||
"appSettings_rasterTileSource": "ラスタタイルのソース",
|
||||
"appSettings_stadiaEndpoint": "Stadia エンドポイント",
|
||||
"appSettings_stadiaApiKey": "Stadia API キー",
|
||||
"appSettings_stadiaApiKeyRequired": "Stadia Maps の利用に必要です",
|
||||
"appSettings_stadiaApiKeyConfigured": "設定済み: {maskedKey}",
|
||||
"@appSettings_stadiaApiKeyConfigured": {
|
||||
"placeholders": {
|
||||
"maskedKey": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"appSettings_stadiaApiKeyDialogDescription": "Stadia Maps の API キーを入力してください。このアプリはラスタタイルの取得に使用します。",
|
||||
"appSettings_offlineMapCache": "オフライン地図キャッシュ",
|
||||
"appSettings_unitsTitle": "単位",
|
||||
"appSettings_unitsMetric": "メートル法 (m / km)",
|
||||
@@ -972,6 +985,56 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_cachedTilesLabel": "Cached tiles",
|
||||
"mapCache_cachedTileSummaryLabel": "Cached tile summary",
|
||||
"mapCache_bulkDownloadDisabledForSource": "Offline bulk downloads are disabled for {source}.",
|
||||
"@mapCache_bulkDownloadDisabledForSource": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_bulkDownloadDisabledInConfig": "Offline bulk downloads are disabled for {source} in this app configuration.",
|
||||
"@mapCache_bulkDownloadDisabledInConfig": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summarySource": "Source: {source}",
|
||||
"@mapCache_summarySource": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryCachedTilesForSource": "Cached tiles for source: {count}",
|
||||
"@mapCache_summaryCachedTilesForSource": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryCachedInSelection": "Cached in selected area/zoom: {count}",
|
||||
"@mapCache_summaryCachedInSelection": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryApproxCacheSize": "Approx cache size: {size}",
|
||||
"@mapCache_summaryApproxCacheSize": {
|
||||
"placeholders": {
|
||||
"size": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_boundsLabel": "N {north}, S {south}, E {east}, W {west}",
|
||||
"@mapCache_boundsLabel": {
|
||||
"placeholders": {
|
||||
|
||||
@@ -334,6 +334,19 @@
|
||||
"appSettings_last6Hours": "지난 6시간",
|
||||
"appSettings_last24Hours": "지난 24시간",
|
||||
"appSettings_lastWeek": "지난 주",
|
||||
"appSettings_rasterTileSource": "래스터 타일 소스",
|
||||
"appSettings_stadiaEndpoint": "Stadia 엔드포인트",
|
||||
"appSettings_stadiaApiKey": "Stadia API 키",
|
||||
"appSettings_stadiaApiKeyRequired": "Stadia Maps를 사용하려면 필요합니다",
|
||||
"appSettings_stadiaApiKeyConfigured": "설정됨: {maskedKey}",
|
||||
"@appSettings_stadiaApiKeyConfigured": {
|
||||
"placeholders": {
|
||||
"maskedKey": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"appSettings_stadiaApiKeyDialogDescription": "Stadia Maps API 키를 입력하세요. 이 앱은 래스터 타일 요청에 이 키를 사용합니다.",
|
||||
"appSettings_offlineMapCache": "오프라인 지도 캐시",
|
||||
"appSettings_unitsTitle": "단위",
|
||||
"appSettings_unitsMetric": "미터법 (m / km)",
|
||||
@@ -954,6 +967,56 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_cachedTilesLabel": "Cached tiles",
|
||||
"mapCache_cachedTileSummaryLabel": "Cached tile summary",
|
||||
"mapCache_bulkDownloadDisabledForSource": "Offline bulk downloads are disabled for {source}.",
|
||||
"@mapCache_bulkDownloadDisabledForSource": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_bulkDownloadDisabledInConfig": "Offline bulk downloads are disabled for {source} in this app configuration.",
|
||||
"@mapCache_bulkDownloadDisabledInConfig": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summarySource": "Source: {source}",
|
||||
"@mapCache_summarySource": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryCachedTilesForSource": "Cached tiles for source: {count}",
|
||||
"@mapCache_summaryCachedTilesForSource": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryCachedInSelection": "Cached in selected area/zoom: {count}",
|
||||
"@mapCache_summaryCachedInSelection": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryApproxCacheSize": "Approx cache size: {size}",
|
||||
"@mapCache_summaryApproxCacheSize": {
|
||||
"placeholders": {
|
||||
"size": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_boundsLabel": "N {north}, S {south}, E {east}, W {west}",
|
||||
"@mapCache_boundsLabel": {
|
||||
"placeholders": {
|
||||
|
||||
@@ -1804,6 +1804,42 @@ abstract class AppLocalizations {
|
||||
/// **'Last week'**
|
||||
String get appSettings_lastWeek;
|
||||
|
||||
/// No description provided for @appSettings_rasterTileSource.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Raster Tile Source'**
|
||||
String get appSettings_rasterTileSource;
|
||||
|
||||
/// No description provided for @appSettings_stadiaEndpoint.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Stadia Endpoint'**
|
||||
String get appSettings_stadiaEndpoint;
|
||||
|
||||
/// No description provided for @appSettings_stadiaApiKey.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Stadia API Key'**
|
||||
String get appSettings_stadiaApiKey;
|
||||
|
||||
/// No description provided for @appSettings_stadiaApiKeyRequired.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Required for Stadia Maps usage'**
|
||||
String get appSettings_stadiaApiKeyRequired;
|
||||
|
||||
/// No description provided for @appSettings_stadiaApiKeyConfigured.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Configured: {maskedKey}'**
|
||||
String appSettings_stadiaApiKeyConfigured(String maskedKey);
|
||||
|
||||
/// No description provided for @appSettings_stadiaApiKeyDialogDescription.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Enter your Stadia Maps API key. This app uses it for raster tile requests.'**
|
||||
String get appSettings_stadiaApiKeyDialogDescription;
|
||||
|
||||
/// No description provided for @appSettings_offlineMapCache.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
@@ -3808,6 +3844,54 @@ abstract class AppLocalizations {
|
||||
/// **'Failed downloads: {count}'**
|
||||
String mapCache_failedDownloads(int count);
|
||||
|
||||
/// No description provided for @mapCache_cachedTilesLabel.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Cached tiles'**
|
||||
String get mapCache_cachedTilesLabel;
|
||||
|
||||
/// No description provided for @mapCache_cachedTileSummaryLabel.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Cached tile summary'**
|
||||
String get mapCache_cachedTileSummaryLabel;
|
||||
|
||||
/// No description provided for @mapCache_bulkDownloadDisabledForSource.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Offline bulk downloads are disabled for {source}.'**
|
||||
String mapCache_bulkDownloadDisabledForSource(String source);
|
||||
|
||||
/// No description provided for @mapCache_bulkDownloadDisabledInConfig.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Offline bulk downloads are disabled for {source} in this app configuration.'**
|
||||
String mapCache_bulkDownloadDisabledInConfig(String source);
|
||||
|
||||
/// No description provided for @mapCache_summarySource.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Source: {source}'**
|
||||
String mapCache_summarySource(String source);
|
||||
|
||||
/// No description provided for @mapCache_summaryCachedTilesForSource.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Cached tiles for source: {count}'**
|
||||
String mapCache_summaryCachedTilesForSource(int count);
|
||||
|
||||
/// No description provided for @mapCache_summaryCachedInSelection.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Cached in selected area/zoom: {count}'**
|
||||
String mapCache_summaryCachedInSelection(int count);
|
||||
|
||||
/// No description provided for @mapCache_summaryApproxCacheSize.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Approx cache size: {size}'**
|
||||
String mapCache_summaryApproxCacheSize(String size);
|
||||
|
||||
/// No description provided for @mapCache_boundsLabel.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
||||
@@ -947,6 +947,28 @@ class AppLocalizationsBg extends AppLocalizations {
|
||||
@override
|
||||
String get appSettings_lastWeek => 'Миналата седмица';
|
||||
|
||||
@override
|
||||
String get appSettings_rasterTileSource => 'Източник на растерни плочки';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaEndpoint => 'Крайна точка на Stadia';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKey => 'API ключ за Stadia';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKeyRequired =>
|
||||
'Задължително за използване на Stadia Maps';
|
||||
|
||||
@override
|
||||
String appSettings_stadiaApiKeyConfigured(String maskedKey) {
|
||||
return 'Конфигурирано: $maskedKey';
|
||||
}
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKeyDialogDescription =>
|
||||
'Въведете своя API ключ за Stadia Maps. Приложението го използва за заявки за растерни плочки.';
|
||||
|
||||
@override
|
||||
String get appSettings_offlineMapCache => 'Кеш на офлайн карти';
|
||||
|
||||
@@ -2098,6 +2120,42 @@ class AppLocalizationsBg extends AppLocalizations {
|
||||
return 'Неуспешни изтегляния: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mapCache_cachedTilesLabel => 'Cached tiles';
|
||||
|
||||
@override
|
||||
String get mapCache_cachedTileSummaryLabel => 'Cached tile summary';
|
||||
|
||||
@override
|
||||
String mapCache_bulkDownloadDisabledForSource(String source) {
|
||||
return 'Offline bulk downloads are disabled for $source.';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_bulkDownloadDisabledInConfig(String source) {
|
||||
return 'Offline bulk downloads are disabled for $source in this app configuration.';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summarySource(String source) {
|
||||
return 'Source: $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryCachedTilesForSource(int count) {
|
||||
return 'Cached tiles for source: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryCachedInSelection(int count) {
|
||||
return 'Cached in selected area/zoom: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryApproxCacheSize(String size) {
|
||||
return 'Approx cache size: $size';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_boundsLabel(
|
||||
String north,
|
||||
|
||||
@@ -944,6 +944,28 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||
@override
|
||||
String get appSettings_lastWeek => 'Letzte Woche';
|
||||
|
||||
@override
|
||||
String get appSettings_rasterTileSource => 'Quelle für Rasterkacheln';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaEndpoint => 'Stadia-Endpunkt';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKey => 'Stadia-API-Schlüssel';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKeyRequired =>
|
||||
'Für die Nutzung von Stadia Maps erforderlich';
|
||||
|
||||
@override
|
||||
String appSettings_stadiaApiKeyConfigured(String maskedKey) {
|
||||
return 'Konfiguriert: $maskedKey';
|
||||
}
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKeyDialogDescription =>
|
||||
'Geben Sie Ihren Stadia-Maps-API-Schlüssel ein. Die App verwendet ihn für Rasterkachel-Anfragen.';
|
||||
|
||||
@override
|
||||
String get appSettings_offlineMapCache => 'Offline-Karten-Cache';
|
||||
|
||||
@@ -2098,6 +2120,42 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||
return 'Fehlgeschlagene Downloads: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mapCache_cachedTilesLabel => 'Cached tiles';
|
||||
|
||||
@override
|
||||
String get mapCache_cachedTileSummaryLabel => 'Cached tile summary';
|
||||
|
||||
@override
|
||||
String mapCache_bulkDownloadDisabledForSource(String source) {
|
||||
return 'Offline bulk downloads are disabled for $source.';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_bulkDownloadDisabledInConfig(String source) {
|
||||
return 'Offline bulk downloads are disabled for $source in this app configuration.';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summarySource(String source) {
|
||||
return 'Source: $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryCachedTilesForSource(int count) {
|
||||
return 'Cached tiles for source: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryCachedInSelection(int count) {
|
||||
return 'Cached in selected area/zoom: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryApproxCacheSize(String size) {
|
||||
return 'Approx cache size: $size';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_boundsLabel(
|
||||
String north,
|
||||
|
||||
@@ -927,6 +927,28 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
@override
|
||||
String get appSettings_lastWeek => 'Last week';
|
||||
|
||||
@override
|
||||
String get appSettings_rasterTileSource => 'Raster Tile Source';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaEndpoint => 'Stadia Endpoint';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKey => 'Stadia API Key';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKeyRequired =>
|
||||
'Required for Stadia Maps usage';
|
||||
|
||||
@override
|
||||
String appSettings_stadiaApiKeyConfigured(String maskedKey) {
|
||||
return 'Configured: $maskedKey';
|
||||
}
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKeyDialogDescription =>
|
||||
'Enter your Stadia Maps API key. This app uses it for raster tile requests.';
|
||||
|
||||
@override
|
||||
String get appSettings_offlineMapCache => 'Offline Map Cache';
|
||||
|
||||
@@ -2059,6 +2081,42 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
return 'Failed downloads: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mapCache_cachedTilesLabel => 'Cached tiles';
|
||||
|
||||
@override
|
||||
String get mapCache_cachedTileSummaryLabel => 'Cached tile summary';
|
||||
|
||||
@override
|
||||
String mapCache_bulkDownloadDisabledForSource(String source) {
|
||||
return 'Offline bulk downloads are disabled for $source.';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_bulkDownloadDisabledInConfig(String source) {
|
||||
return 'Offline bulk downloads are disabled for $source in this app configuration.';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summarySource(String source) {
|
||||
return 'Source: $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryCachedTilesForSource(int count) {
|
||||
return 'Cached tiles for source: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryCachedInSelection(int count) {
|
||||
return 'Cached in selected area/zoom: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryApproxCacheSize(String size) {
|
||||
return 'Approx cache size: $size';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_boundsLabel(
|
||||
String north,
|
||||
|
||||
@@ -941,6 +941,28 @@ class AppLocalizationsEs extends AppLocalizations {
|
||||
@override
|
||||
String get appSettings_lastWeek => 'La semana pasada';
|
||||
|
||||
@override
|
||||
String get appSettings_rasterTileSource => 'Fuente de teselas ráster';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaEndpoint => 'Punto de acceso de Stadia';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKey => 'Clave API de Stadia';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKeyRequired =>
|
||||
'Obligatorio para usar Stadia Maps';
|
||||
|
||||
@override
|
||||
String appSettings_stadiaApiKeyConfigured(String maskedKey) {
|
||||
return 'Configurado: $maskedKey';
|
||||
}
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKeyDialogDescription =>
|
||||
'Introduce tu clave API de Stadia Maps. La aplicación la usa para solicitar teselas ráster.';
|
||||
|
||||
@override
|
||||
String get appSettings_offlineMapCache => 'Caché de mapa sin conexión';
|
||||
|
||||
@@ -2092,6 +2114,42 @@ class AppLocalizationsEs extends AppLocalizations {
|
||||
return 'Descargas fallidas: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mapCache_cachedTilesLabel => 'Cached tiles';
|
||||
|
||||
@override
|
||||
String get mapCache_cachedTileSummaryLabel => 'Cached tile summary';
|
||||
|
||||
@override
|
||||
String mapCache_bulkDownloadDisabledForSource(String source) {
|
||||
return 'Offline bulk downloads are disabled for $source.';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_bulkDownloadDisabledInConfig(String source) {
|
||||
return 'Offline bulk downloads are disabled for $source in this app configuration.';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summarySource(String source) {
|
||||
return 'Source: $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryCachedTilesForSource(int count) {
|
||||
return 'Cached tiles for source: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryCachedInSelection(int count) {
|
||||
return 'Cached in selected area/zoom: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryApproxCacheSize(String size) {
|
||||
return 'Approx cache size: $size';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_boundsLabel(
|
||||
String north,
|
||||
|
||||
@@ -945,6 +945,28 @@ class AppLocalizationsFr extends AppLocalizations {
|
||||
@override
|
||||
String get appSettings_lastWeek => 'La semaine dernière';
|
||||
|
||||
@override
|
||||
String get appSettings_rasterTileSource => 'Source de tuiles raster';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaEndpoint => 'Point de terminaison Stadia';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKey => 'Clé API Stadia';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKeyRequired =>
|
||||
'Requis pour l’utilisation de Stadia Maps';
|
||||
|
||||
@override
|
||||
String appSettings_stadiaApiKeyConfigured(String maskedKey) {
|
||||
return 'Configuré : $maskedKey';
|
||||
}
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKeyDialogDescription =>
|
||||
'Entrez votre clé API Stadia Maps. Cette application l’utilise pour les requêtes de tuiles raster.';
|
||||
|
||||
@override
|
||||
String get appSettings_offlineMapCache => 'Cache de carte hors ligne';
|
||||
|
||||
@@ -2099,6 +2121,42 @@ class AppLocalizationsFr extends AppLocalizations {
|
||||
return 'Téléchargements échoués : $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mapCache_cachedTilesLabel => 'Cached tiles';
|
||||
|
||||
@override
|
||||
String get mapCache_cachedTileSummaryLabel => 'Cached tile summary';
|
||||
|
||||
@override
|
||||
String mapCache_bulkDownloadDisabledForSource(String source) {
|
||||
return 'Offline bulk downloads are disabled for $source.';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_bulkDownloadDisabledInConfig(String source) {
|
||||
return 'Offline bulk downloads are disabled for $source in this app configuration.';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summarySource(String source) {
|
||||
return 'Source: $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryCachedTilesForSource(int count) {
|
||||
return 'Cached tiles for source: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryCachedInSelection(int count) {
|
||||
return 'Cached in selected area/zoom: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryApproxCacheSize(String size) {
|
||||
return 'Approx cache size: $size';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_boundsLabel(
|
||||
String north,
|
||||
|
||||
@@ -937,6 +937,28 @@ class AppLocalizationsHu extends AppLocalizations {
|
||||
@override
|
||||
String get appSettings_lastWeek => 'Múlt héten';
|
||||
|
||||
@override
|
||||
String get appSettings_rasterTileSource => 'Raszteres csempeforrás';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaEndpoint => 'Stadia végpont';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKey => 'Stadia API-kulcs';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKeyRequired =>
|
||||
'A Stadia Maps használatához kötelező';
|
||||
|
||||
@override
|
||||
String appSettings_stadiaApiKeyConfigured(String maskedKey) {
|
||||
return 'Konfigurálva: $maskedKey';
|
||||
}
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKeyDialogDescription =>
|
||||
'Adja meg a Stadia Maps API-kulcsát. Az alkalmazás ezt használja raszteres csempekérésekhez.';
|
||||
|
||||
@override
|
||||
String get appSettings_offlineMapCache => 'Offline térképgyorsítótár';
|
||||
|
||||
@@ -2090,6 +2112,42 @@ class AppLocalizationsHu extends AppLocalizations {
|
||||
return 'Sikertelen letöltések: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mapCache_cachedTilesLabel => 'Cached tiles';
|
||||
|
||||
@override
|
||||
String get mapCache_cachedTileSummaryLabel => 'Cached tile summary';
|
||||
|
||||
@override
|
||||
String mapCache_bulkDownloadDisabledForSource(String source) {
|
||||
return 'Offline bulk downloads are disabled for $source.';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_bulkDownloadDisabledInConfig(String source) {
|
||||
return 'Offline bulk downloads are disabled for $source in this app configuration.';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summarySource(String source) {
|
||||
return 'Source: $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryCachedTilesForSource(int count) {
|
||||
return 'Cached tiles for source: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryCachedInSelection(int count) {
|
||||
return 'Cached in selected area/zoom: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryApproxCacheSize(String size) {
|
||||
return 'Approx cache size: $size';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_boundsLabel(
|
||||
String north,
|
||||
|
||||
@@ -944,6 +944,28 @@ class AppLocalizationsIt extends AppLocalizations {
|
||||
@override
|
||||
String get appSettings_lastWeek => 'La settimana scorsa';
|
||||
|
||||
@override
|
||||
String get appSettings_rasterTileSource => 'Sorgente tile raster';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaEndpoint => 'Endpoint Stadia';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKey => 'Chiave API Stadia';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKeyRequired =>
|
||||
'Obbligatoria per usare Stadia Maps';
|
||||
|
||||
@override
|
||||
String appSettings_stadiaApiKeyConfigured(String maskedKey) {
|
||||
return 'Configurata: $maskedKey';
|
||||
}
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKeyDialogDescription =>
|
||||
'Inserisci la chiave API di Stadia Maps. Questa app la usa per le richieste di tile raster.';
|
||||
|
||||
@override
|
||||
String get appSettings_offlineMapCache => 'Cache Mappa Offline';
|
||||
|
||||
@@ -2094,6 +2116,42 @@ class AppLocalizationsIt extends AppLocalizations {
|
||||
return 'Download falliti: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mapCache_cachedTilesLabel => 'Cached tiles';
|
||||
|
||||
@override
|
||||
String get mapCache_cachedTileSummaryLabel => 'Cached tile summary';
|
||||
|
||||
@override
|
||||
String mapCache_bulkDownloadDisabledForSource(String source) {
|
||||
return 'Offline bulk downloads are disabled for $source.';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_bulkDownloadDisabledInConfig(String source) {
|
||||
return 'Offline bulk downloads are disabled for $source in this app configuration.';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summarySource(String source) {
|
||||
return 'Source: $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryCachedTilesForSource(int count) {
|
||||
return 'Cached tiles for source: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryCachedInSelection(int count) {
|
||||
return 'Cached in selected area/zoom: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryApproxCacheSize(String size) {
|
||||
return 'Approx cache size: $size';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_boundsLabel(
|
||||
String north,
|
||||
|
||||
@@ -891,6 +891,27 @@ class AppLocalizationsJa extends AppLocalizations {
|
||||
@override
|
||||
String get appSettings_lastWeek => '過去1週間';
|
||||
|
||||
@override
|
||||
String get appSettings_rasterTileSource => 'ラスタタイルのソース';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaEndpoint => 'Stadia エンドポイント';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKey => 'Stadia API キー';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKeyRequired => 'Stadia Maps の利用に必要です';
|
||||
|
||||
@override
|
||||
String appSettings_stadiaApiKeyConfigured(String maskedKey) {
|
||||
return '設定済み: $maskedKey';
|
||||
}
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKeyDialogDescription =>
|
||||
'Stadia Maps の API キーを入力してください。このアプリはラスタタイルの取得に使用します。';
|
||||
|
||||
@override
|
||||
String get appSettings_offlineMapCache => 'オフライン地図キャッシュ';
|
||||
|
||||
@@ -2006,6 +2027,42 @@ class AppLocalizationsJa extends AppLocalizations {
|
||||
return '失敗したダウンロード: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mapCache_cachedTilesLabel => 'Cached tiles';
|
||||
|
||||
@override
|
||||
String get mapCache_cachedTileSummaryLabel => 'Cached tile summary';
|
||||
|
||||
@override
|
||||
String mapCache_bulkDownloadDisabledForSource(String source) {
|
||||
return 'Offline bulk downloads are disabled for $source.';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_bulkDownloadDisabledInConfig(String source) {
|
||||
return 'Offline bulk downloads are disabled for $source in this app configuration.';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summarySource(String source) {
|
||||
return 'Source: $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryCachedTilesForSource(int count) {
|
||||
return 'Cached tiles for source: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryCachedInSelection(int count) {
|
||||
return 'Cached in selected area/zoom: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryApproxCacheSize(String size) {
|
||||
return 'Approx cache size: $size';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_boundsLabel(
|
||||
String north,
|
||||
|
||||
@@ -894,6 +894,27 @@ class AppLocalizationsKo extends AppLocalizations {
|
||||
@override
|
||||
String get appSettings_lastWeek => '지난 주';
|
||||
|
||||
@override
|
||||
String get appSettings_rasterTileSource => '래스터 타일 소스';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaEndpoint => 'Stadia 엔드포인트';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKey => 'Stadia API 키';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKeyRequired => 'Stadia Maps를 사용하려면 필요합니다';
|
||||
|
||||
@override
|
||||
String appSettings_stadiaApiKeyConfigured(String maskedKey) {
|
||||
return '설정됨: $maskedKey';
|
||||
}
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKeyDialogDescription =>
|
||||
'Stadia Maps API 키를 입력하세요. 이 앱은 래스터 타일 요청에 이 키를 사용합니다.';
|
||||
|
||||
@override
|
||||
String get appSettings_offlineMapCache => '오프라인 지도 캐시';
|
||||
|
||||
@@ -2010,6 +2031,42 @@ class AppLocalizationsKo extends AppLocalizations {
|
||||
return '실패한 다운로드: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mapCache_cachedTilesLabel => 'Cached tiles';
|
||||
|
||||
@override
|
||||
String get mapCache_cachedTileSummaryLabel => 'Cached tile summary';
|
||||
|
||||
@override
|
||||
String mapCache_bulkDownloadDisabledForSource(String source) {
|
||||
return 'Offline bulk downloads are disabled for $source.';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_bulkDownloadDisabledInConfig(String source) {
|
||||
return 'Offline bulk downloads are disabled for $source in this app configuration.';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summarySource(String source) {
|
||||
return 'Source: $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryCachedTilesForSource(int count) {
|
||||
return 'Cached tiles for source: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryCachedInSelection(int count) {
|
||||
return 'Cached in selected area/zoom: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryApproxCacheSize(String size) {
|
||||
return 'Approx cache size: $size';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_boundsLabel(
|
||||
String north,
|
||||
|
||||
@@ -936,6 +936,28 @@ class AppLocalizationsNl extends AppLocalizations {
|
||||
@override
|
||||
String get appSettings_lastWeek => 'Afgelopen week';
|
||||
|
||||
@override
|
||||
String get appSettings_rasterTileSource => 'Rastertegelbron';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaEndpoint => 'Stadia-eindpunt';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKey => 'Stadia API-sleutel';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKeyRequired =>
|
||||
'Vereist voor gebruik van Stadia Maps';
|
||||
|
||||
@override
|
||||
String appSettings_stadiaApiKeyConfigured(String maskedKey) {
|
||||
return 'Geconfigureerd: $maskedKey';
|
||||
}
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKeyDialogDescription =>
|
||||
'Voer je Stadia Maps API-sleutel in. De app gebruikt die voor rastertegelverzoeken.';
|
||||
|
||||
@override
|
||||
String get appSettings_offlineMapCache => 'Offline Kaartcache';
|
||||
|
||||
@@ -2082,6 +2104,42 @@ class AppLocalizationsNl extends AppLocalizations {
|
||||
return 'Mislukte downloads: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mapCache_cachedTilesLabel => 'Cached tiles';
|
||||
|
||||
@override
|
||||
String get mapCache_cachedTileSummaryLabel => 'Cached tile summary';
|
||||
|
||||
@override
|
||||
String mapCache_bulkDownloadDisabledForSource(String source) {
|
||||
return 'Offline bulk downloads are disabled for $source.';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_bulkDownloadDisabledInConfig(String source) {
|
||||
return 'Offline bulk downloads are disabled for $source in this app configuration.';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summarySource(String source) {
|
||||
return 'Source: $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryCachedTilesForSource(int count) {
|
||||
return 'Cached tiles for source: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryCachedInSelection(int count) {
|
||||
return 'Cached in selected area/zoom: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryApproxCacheSize(String size) {
|
||||
return 'Approx cache size: $size';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_boundsLabel(
|
||||
String north,
|
||||
|
||||
@@ -945,6 +945,28 @@ class AppLocalizationsPl extends AppLocalizations {
|
||||
@override
|
||||
String get appSettings_lastWeek => 'Ostatni tydzień';
|
||||
|
||||
@override
|
||||
String get appSettings_rasterTileSource => 'Źródło kafelków rastrowych';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaEndpoint => 'Punkt końcowy Stadia';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKey => 'Klucz API Stadia';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKeyRequired =>
|
||||
'Wymagane do korzystania ze Stadia Maps';
|
||||
|
||||
@override
|
||||
String appSettings_stadiaApiKeyConfigured(String maskedKey) {
|
||||
return 'Skonfigurowano: $maskedKey';
|
||||
}
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKeyDialogDescription =>
|
||||
'Wprowadź swój klucz API Stadia Maps. Aplikacja używa go do żądań kafelków rastrowych.';
|
||||
|
||||
@override
|
||||
String get appSettings_offlineMapCache => 'Pamięć podręczna map offline';
|
||||
|
||||
@@ -2112,6 +2134,42 @@ class AppLocalizationsPl extends AppLocalizations {
|
||||
return 'Nieudane pobrania: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mapCache_cachedTilesLabel => 'Cached tiles';
|
||||
|
||||
@override
|
||||
String get mapCache_cachedTileSummaryLabel => 'Cached tile summary';
|
||||
|
||||
@override
|
||||
String mapCache_bulkDownloadDisabledForSource(String source) {
|
||||
return 'Offline bulk downloads are disabled for $source.';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_bulkDownloadDisabledInConfig(String source) {
|
||||
return 'Offline bulk downloads are disabled for $source in this app configuration.';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summarySource(String source) {
|
||||
return 'Source: $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryCachedTilesForSource(int count) {
|
||||
return 'Cached tiles for source: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryCachedInSelection(int count) {
|
||||
return 'Cached in selected area/zoom: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryApproxCacheSize(String size) {
|
||||
return 'Approx cache size: $size';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_boundsLabel(
|
||||
String north,
|
||||
|
||||
@@ -942,6 +942,28 @@ class AppLocalizationsPt extends AppLocalizations {
|
||||
@override
|
||||
String get appSettings_lastWeek => 'Da última semana';
|
||||
|
||||
@override
|
||||
String get appSettings_rasterTileSource => 'Fonte de blocos raster';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaEndpoint => 'Endpoint da Stadia';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKey => 'Chave da API Stadia';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKeyRequired =>
|
||||
'Obrigatório para usar o Stadia Maps';
|
||||
|
||||
@override
|
||||
String appSettings_stadiaApiKeyConfigured(String maskedKey) {
|
||||
return 'Configurado: $maskedKey';
|
||||
}
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKeyDialogDescription =>
|
||||
'Insira sua chave da API Stadia Maps. O aplicativo a usa para solicitações de blocos raster.';
|
||||
|
||||
@override
|
||||
String get appSettings_offlineMapCache => 'Cache de Mapa Offline';
|
||||
|
||||
@@ -2095,6 +2117,42 @@ class AppLocalizationsPt extends AppLocalizations {
|
||||
return 'Downloads falhas: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mapCache_cachedTilesLabel => 'Cached tiles';
|
||||
|
||||
@override
|
||||
String get mapCache_cachedTileSummaryLabel => 'Cached tile summary';
|
||||
|
||||
@override
|
||||
String mapCache_bulkDownloadDisabledForSource(String source) {
|
||||
return 'Offline bulk downloads are disabled for $source.';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_bulkDownloadDisabledInConfig(String source) {
|
||||
return 'Offline bulk downloads are disabled for $source in this app configuration.';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summarySource(String source) {
|
||||
return 'Source: $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryCachedTilesForSource(int count) {
|
||||
return 'Cached tiles for source: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryCachedInSelection(int count) {
|
||||
return 'Cached in selected area/zoom: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryApproxCacheSize(String size) {
|
||||
return 'Approx cache size: $size';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_boundsLabel(
|
||||
String north,
|
||||
|
||||
@@ -945,6 +945,28 @@ class AppLocalizationsRu extends AppLocalizations {
|
||||
@override
|
||||
String get appSettings_lastWeek => 'Последнюю неделю';
|
||||
|
||||
@override
|
||||
String get appSettings_rasterTileSource => 'Источник растровых тайлов';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaEndpoint => 'Конечная точка Stadia';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKey => 'Ключ API Stadia';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKeyRequired =>
|
||||
'Требуется для использования Stadia Maps';
|
||||
|
||||
@override
|
||||
String appSettings_stadiaApiKeyConfigured(String maskedKey) {
|
||||
return 'Настроено: $maskedKey';
|
||||
}
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKeyDialogDescription =>
|
||||
'Введите свой ключ API Stadia Maps. Приложение использует его для запросов растровых тайлов.';
|
||||
|
||||
@override
|
||||
String get appSettings_offlineMapCache => 'Кэш офлайн-карты';
|
||||
|
||||
@@ -2099,6 +2121,42 @@ class AppLocalizationsRu extends AppLocalizations {
|
||||
return 'Неудачных загрузок: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mapCache_cachedTilesLabel => 'Cached tiles';
|
||||
|
||||
@override
|
||||
String get mapCache_cachedTileSummaryLabel => 'Cached tile summary';
|
||||
|
||||
@override
|
||||
String mapCache_bulkDownloadDisabledForSource(String source) {
|
||||
return 'Offline bulk downloads are disabled for $source.';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_bulkDownloadDisabledInConfig(String source) {
|
||||
return 'Offline bulk downloads are disabled for $source in this app configuration.';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summarySource(String source) {
|
||||
return 'Source: $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryCachedTilesForSource(int count) {
|
||||
return 'Cached tiles for source: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryCachedInSelection(int count) {
|
||||
return 'Cached in selected area/zoom: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryApproxCacheSize(String size) {
|
||||
return 'Approx cache size: $size';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_boundsLabel(
|
||||
String north,
|
||||
|
||||
@@ -932,6 +932,28 @@ class AppLocalizationsSk extends AppLocalizations {
|
||||
@override
|
||||
String get appSettings_lastWeek => 'Minul týždeň';
|
||||
|
||||
@override
|
||||
String get appSettings_rasterTileSource => 'Zdroj rastrových dlaždíc';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaEndpoint => 'Koncový bod Stadia';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKey => 'Kľúč API Stadia';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKeyRequired =>
|
||||
'Vyžaduje sa na používanie Stadia Maps';
|
||||
|
||||
@override
|
||||
String appSettings_stadiaApiKeyConfigured(String maskedKey) {
|
||||
return 'Nakonfigurované: $maskedKey';
|
||||
}
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKeyDialogDescription =>
|
||||
'Zadajte svoj kľúč API pre Stadia Maps. Aplikácia ho používa na požiadavky na rastrové dlaždice.';
|
||||
|
||||
@override
|
||||
String get appSettings_offlineMapCache => 'Offline Mapa Pamäť';
|
||||
|
||||
@@ -2085,6 +2107,42 @@ class AppLocalizationsSk extends AppLocalizations {
|
||||
return 'Neúspešné stiahnutia: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mapCache_cachedTilesLabel => 'Cached tiles';
|
||||
|
||||
@override
|
||||
String get mapCache_cachedTileSummaryLabel => 'Cached tile summary';
|
||||
|
||||
@override
|
||||
String mapCache_bulkDownloadDisabledForSource(String source) {
|
||||
return 'Offline bulk downloads are disabled for $source.';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_bulkDownloadDisabledInConfig(String source) {
|
||||
return 'Offline bulk downloads are disabled for $source in this app configuration.';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summarySource(String source) {
|
||||
return 'Source: $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryCachedTilesForSource(int count) {
|
||||
return 'Cached tiles for source: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryCachedInSelection(int count) {
|
||||
return 'Cached in selected area/zoom: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryApproxCacheSize(String size) {
|
||||
return 'Approx cache size: $size';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_boundsLabel(
|
||||
String north,
|
||||
|
||||
@@ -933,6 +933,28 @@ class AppLocalizationsSl extends AppLocalizations {
|
||||
@override
|
||||
String get appSettings_lastWeek => 'Prejšnji teden';
|
||||
|
||||
@override
|
||||
String get appSettings_rasterTileSource => 'Vir rastrskih ploščic';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaEndpoint => 'Končna točka Stadia';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKey => 'Ključ API Stadia';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKeyRequired =>
|
||||
'Obvezno za uporabo Stadia Maps';
|
||||
|
||||
@override
|
||||
String appSettings_stadiaApiKeyConfigured(String maskedKey) {
|
||||
return 'Nastavljeno: $maskedKey';
|
||||
}
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKeyDialogDescription =>
|
||||
'Vnesite svoj ključ API za Stadia Maps. Aplikacija ga uporablja za zahteve rastrskih ploščic.';
|
||||
|
||||
@override
|
||||
String get appSettings_offlineMapCache => 'Shramba zemljevidov brez povezave';
|
||||
|
||||
@@ -2082,6 +2104,42 @@ class AppLocalizationsSl extends AppLocalizations {
|
||||
return 'Poslovniški izniki: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mapCache_cachedTilesLabel => 'Cached tiles';
|
||||
|
||||
@override
|
||||
String get mapCache_cachedTileSummaryLabel => 'Cached tile summary';
|
||||
|
||||
@override
|
||||
String mapCache_bulkDownloadDisabledForSource(String source) {
|
||||
return 'Offline bulk downloads are disabled for $source.';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_bulkDownloadDisabledInConfig(String source) {
|
||||
return 'Offline bulk downloads are disabled for $source in this app configuration.';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summarySource(String source) {
|
||||
return 'Source: $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryCachedTilesForSource(int count) {
|
||||
return 'Cached tiles for source: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryCachedInSelection(int count) {
|
||||
return 'Cached in selected area/zoom: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryApproxCacheSize(String size) {
|
||||
return 'Approx cache size: $size';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_boundsLabel(
|
||||
String north,
|
||||
|
||||
@@ -926,6 +926,28 @@ class AppLocalizationsSv extends AppLocalizations {
|
||||
@override
|
||||
String get appSettings_lastWeek => 'Förra veckan';
|
||||
|
||||
@override
|
||||
String get appSettings_rasterTileSource => 'Källa för rasterplattor';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaEndpoint => 'Stadia-slutpunkt';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKey => 'Stadia API-nyckel';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKeyRequired =>
|
||||
'Krävs för att använda Stadia Maps';
|
||||
|
||||
@override
|
||||
String appSettings_stadiaApiKeyConfigured(String maskedKey) {
|
||||
return 'Konfigurerad: $maskedKey';
|
||||
}
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKeyDialogDescription =>
|
||||
'Ange din Stadia Maps API-nyckel. Appen använder den för förfrågningar om rasterplattor.';
|
||||
|
||||
@override
|
||||
String get appSettings_offlineMapCache => 'Offline Kartcache';
|
||||
|
||||
@@ -2070,6 +2092,42 @@ class AppLocalizationsSv extends AppLocalizations {
|
||||
return 'Misslyckade nedladdningar: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mapCache_cachedTilesLabel => 'Cached tiles';
|
||||
|
||||
@override
|
||||
String get mapCache_cachedTileSummaryLabel => 'Cached tile summary';
|
||||
|
||||
@override
|
||||
String mapCache_bulkDownloadDisabledForSource(String source) {
|
||||
return 'Offline bulk downloads are disabled for $source.';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_bulkDownloadDisabledInConfig(String source) {
|
||||
return 'Offline bulk downloads are disabled for $source in this app configuration.';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summarySource(String source) {
|
||||
return 'Source: $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryCachedTilesForSource(int count) {
|
||||
return 'Cached tiles for source: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryCachedInSelection(int count) {
|
||||
return 'Cached in selected area/zoom: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryApproxCacheSize(String size) {
|
||||
return 'Approx cache size: $size';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_boundsLabel(
|
||||
String north,
|
||||
|
||||
@@ -939,6 +939,28 @@ class AppLocalizationsUk extends AppLocalizations {
|
||||
@override
|
||||
String get appSettings_lastWeek => 'Минулий тиждень';
|
||||
|
||||
@override
|
||||
String get appSettings_rasterTileSource => 'Джерело растрових тайлів';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaEndpoint => 'Кінцева точка Stadia';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKey => 'Ключ API Stadia';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKeyRequired =>
|
||||
'Потрібно для використання Stadia Maps';
|
||||
|
||||
@override
|
||||
String appSettings_stadiaApiKeyConfigured(String maskedKey) {
|
||||
return 'Налаштовано: $maskedKey';
|
||||
}
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKeyDialogDescription =>
|
||||
'Введіть свій ключ API Stadia Maps. Програма використовує його для запитів растрових тайлів.';
|
||||
|
||||
@override
|
||||
String get appSettings_offlineMapCache => 'Офлайн-кеш карти';
|
||||
|
||||
@@ -2092,6 +2114,42 @@ class AppLocalizationsUk extends AppLocalizations {
|
||||
return 'Невдалі завантаження: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mapCache_cachedTilesLabel => 'Cached tiles';
|
||||
|
||||
@override
|
||||
String get mapCache_cachedTileSummaryLabel => 'Cached tile summary';
|
||||
|
||||
@override
|
||||
String mapCache_bulkDownloadDisabledForSource(String source) {
|
||||
return 'Offline bulk downloads are disabled for $source.';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_bulkDownloadDisabledInConfig(String source) {
|
||||
return 'Offline bulk downloads are disabled for $source in this app configuration.';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summarySource(String source) {
|
||||
return 'Source: $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryCachedTilesForSource(int count) {
|
||||
return 'Cached tiles for source: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryCachedInSelection(int count) {
|
||||
return 'Cached in selected area/zoom: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryApproxCacheSize(String size) {
|
||||
return 'Approx cache size: $size';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_boundsLabel(
|
||||
String north,
|
||||
|
||||
@@ -878,6 +878,27 @@ class AppLocalizationsZh extends AppLocalizations {
|
||||
@override
|
||||
String get appSettings_lastWeek => '上周';
|
||||
|
||||
@override
|
||||
String get appSettings_rasterTileSource => '栅格瓦片源';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaEndpoint => 'Stadia 端点';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKey => 'Stadia API 密钥';
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKeyRequired => '使用 Stadia Maps 时必需';
|
||||
|
||||
@override
|
||||
String appSettings_stadiaApiKeyConfigured(String maskedKey) {
|
||||
return '已配置:$maskedKey';
|
||||
}
|
||||
|
||||
@override
|
||||
String get appSettings_stadiaApiKeyDialogDescription =>
|
||||
'请输入你的 Stadia Maps API 密钥。该应用会使用它来请求栅格瓦片。';
|
||||
|
||||
@override
|
||||
String get appSettings_offlineMapCache => '离线地图缓存';
|
||||
|
||||
@@ -1985,6 +2006,42 @@ class AppLocalizationsZh extends AppLocalizations {
|
||||
return '下载失败:$count';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mapCache_cachedTilesLabel => 'Cached tiles';
|
||||
|
||||
@override
|
||||
String get mapCache_cachedTileSummaryLabel => 'Cached tile summary';
|
||||
|
||||
@override
|
||||
String mapCache_bulkDownloadDisabledForSource(String source) {
|
||||
return 'Offline bulk downloads are disabled for $source.';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_bulkDownloadDisabledInConfig(String source) {
|
||||
return 'Offline bulk downloads are disabled for $source in this app configuration.';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summarySource(String source) {
|
||||
return 'Source: $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryCachedTilesForSource(int count) {
|
||||
return 'Cached tiles for source: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryCachedInSelection(int count) {
|
||||
return 'Cached in selected area/zoom: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_summaryApproxCacheSize(String size) {
|
||||
return 'Approx cache size: $size';
|
||||
}
|
||||
|
||||
@override
|
||||
String mapCache_boundsLabel(
|
||||
String north,
|
||||
|
||||
@@ -242,6 +242,19 @@
|
||||
"appSettings_last6Hours": "Afgelopen 6 uur",
|
||||
"appSettings_last24Hours": "Afgelopen 24 uur",
|
||||
"appSettings_lastWeek": "Afgelopen week",
|
||||
"appSettings_rasterTileSource": "Rastertegelbron",
|
||||
"appSettings_stadiaEndpoint": "Stadia-eindpunt",
|
||||
"appSettings_stadiaApiKey": "Stadia API-sleutel",
|
||||
"appSettings_stadiaApiKeyRequired": "Vereist voor gebruik van Stadia Maps",
|
||||
"appSettings_stadiaApiKeyConfigured": "Geconfigureerd: {maskedKey}",
|
||||
"@appSettings_stadiaApiKeyConfigured": {
|
||||
"placeholders": {
|
||||
"maskedKey": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"appSettings_stadiaApiKeyDialogDescription": "Voer je Stadia Maps API-sleutel in. De app gebruikt die voor rastertegelverzoeken.",
|
||||
"appSettings_offlineMapCache": "Offline Kaartcache",
|
||||
"appSettings_noAreaSelected": "Geen gebied geselecteerd",
|
||||
"appSettings_areaSelectedZoom": "Geselecteerd gebied (zoom {minZoom}-{maxZoom})",
|
||||
@@ -771,6 +784,56 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_cachedTilesLabel": "Cached tiles",
|
||||
"mapCache_cachedTileSummaryLabel": "Cached tile summary",
|
||||
"mapCache_bulkDownloadDisabledForSource": "Offline bulk downloads are disabled for {source}.",
|
||||
"@mapCache_bulkDownloadDisabledForSource": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_bulkDownloadDisabledInConfig": "Offline bulk downloads are disabled for {source} in this app configuration.",
|
||||
"@mapCache_bulkDownloadDisabledInConfig": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summarySource": "Source: {source}",
|
||||
"@mapCache_summarySource": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryCachedTilesForSource": "Cached tiles for source: {count}",
|
||||
"@mapCache_summaryCachedTilesForSource": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryCachedInSelection": "Cached in selected area/zoom: {count}",
|
||||
"@mapCache_summaryCachedInSelection": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryApproxCacheSize": "Approx cache size: {size}",
|
||||
"@mapCache_summaryApproxCacheSize": {
|
||||
"placeholders": {
|
||||
"size": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_boundsLabel": "N {north}, S {south}, E {east}, W {west}",
|
||||
"@mapCache_boundsLabel": {
|
||||
"placeholders": {
|
||||
|
||||
@@ -242,6 +242,19 @@
|
||||
"appSettings_last6Hours": "Ostatnie 6 godzin",
|
||||
"appSettings_last24Hours": "Ostatnie 24 godziny",
|
||||
"appSettings_lastWeek": "Ostatni tydzień",
|
||||
"appSettings_rasterTileSource": "Źródło kafelków rastrowych",
|
||||
"appSettings_stadiaEndpoint": "Punkt końcowy Stadia",
|
||||
"appSettings_stadiaApiKey": "Klucz API Stadia",
|
||||
"appSettings_stadiaApiKeyRequired": "Wymagane do korzystania ze Stadia Maps",
|
||||
"appSettings_stadiaApiKeyConfigured": "Skonfigurowano: {maskedKey}",
|
||||
"@appSettings_stadiaApiKeyConfigured": {
|
||||
"placeholders": {
|
||||
"maskedKey": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"appSettings_stadiaApiKeyDialogDescription": "Wprowadź swój klucz API Stadia Maps. Aplikacja używa go do żądań kafelków rastrowych.",
|
||||
"appSettings_offlineMapCache": "Pamięć podręczna map offline",
|
||||
"appSettings_noAreaSelected": "Nie wybrano żadnego obszaru.",
|
||||
"appSettings_areaSelectedZoom": "Wybrany obszar (skala {minZoom}-{maxZoom})",
|
||||
@@ -781,6 +794,56 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_cachedTilesLabel": "Cached tiles",
|
||||
"mapCache_cachedTileSummaryLabel": "Cached tile summary",
|
||||
"mapCache_bulkDownloadDisabledForSource": "Offline bulk downloads are disabled for {source}.",
|
||||
"@mapCache_bulkDownloadDisabledForSource": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_bulkDownloadDisabledInConfig": "Offline bulk downloads are disabled for {source} in this app configuration.",
|
||||
"@mapCache_bulkDownloadDisabledInConfig": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summarySource": "Source: {source}",
|
||||
"@mapCache_summarySource": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryCachedTilesForSource": "Cached tiles for source: {count}",
|
||||
"@mapCache_summaryCachedTilesForSource": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryCachedInSelection": "Cached in selected area/zoom: {count}",
|
||||
"@mapCache_summaryCachedInSelection": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryApproxCacheSize": "Approx cache size: {size}",
|
||||
"@mapCache_summaryApproxCacheSize": {
|
||||
"placeholders": {
|
||||
"size": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_boundsLabel": "N {north}, S {south}, E {east}, W {west}",
|
||||
"@mapCache_boundsLabel": {
|
||||
"placeholders": {
|
||||
|
||||
@@ -242,6 +242,19 @@
|
||||
"appSettings_last6Hours": "Últimos 6 horas",
|
||||
"appSettings_last24Hours": "Últimas 24 horas",
|
||||
"appSettings_lastWeek": "Da última semana",
|
||||
"appSettings_rasterTileSource": "Fonte de blocos raster",
|
||||
"appSettings_stadiaEndpoint": "Endpoint da Stadia",
|
||||
"appSettings_stadiaApiKey": "Chave da API Stadia",
|
||||
"appSettings_stadiaApiKeyRequired": "Obrigatório para usar o Stadia Maps",
|
||||
"appSettings_stadiaApiKeyConfigured": "Configurado: {maskedKey}",
|
||||
"@appSettings_stadiaApiKeyConfigured": {
|
||||
"placeholders": {
|
||||
"maskedKey": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"appSettings_stadiaApiKeyDialogDescription": "Insira sua chave da API Stadia Maps. O aplicativo a usa para solicitações de blocos raster.",
|
||||
"appSettings_offlineMapCache": "Cache de Mapa Offline",
|
||||
"appSettings_noAreaSelected": "Nenhuma área selecionada",
|
||||
"appSettings_areaSelectedZoom": "Área selecionada (zoom {minZoom}-{maxZoom})",
|
||||
@@ -771,6 +784,56 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_cachedTilesLabel": "Cached tiles",
|
||||
"mapCache_cachedTileSummaryLabel": "Cached tile summary",
|
||||
"mapCache_bulkDownloadDisabledForSource": "Offline bulk downloads are disabled for {source}.",
|
||||
"@mapCache_bulkDownloadDisabledForSource": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_bulkDownloadDisabledInConfig": "Offline bulk downloads are disabled for {source} in this app configuration.",
|
||||
"@mapCache_bulkDownloadDisabledInConfig": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summarySource": "Source: {source}",
|
||||
"@mapCache_summarySource": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryCachedTilesForSource": "Cached tiles for source: {count}",
|
||||
"@mapCache_summaryCachedTilesForSource": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryCachedInSelection": "Cached in selected area/zoom: {count}",
|
||||
"@mapCache_summaryCachedInSelection": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryApproxCacheSize": "Approx cache size: {size}",
|
||||
"@mapCache_summaryApproxCacheSize": {
|
||||
"placeholders": {
|
||||
"size": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_boundsLabel": "N {north}, S {south}, E {east}, W {west}",
|
||||
"@mapCache_boundsLabel": {
|
||||
"placeholders": {
|
||||
|
||||
@@ -192,6 +192,19 @@
|
||||
"appSettings_last6Hours": "Последние 6 часов",
|
||||
"appSettings_last24Hours": "Последние 24 часа",
|
||||
"appSettings_lastWeek": "Последнюю неделю",
|
||||
"appSettings_rasterTileSource": "Источник растровых тайлов",
|
||||
"appSettings_stadiaEndpoint": "Конечная точка Stadia",
|
||||
"appSettings_stadiaApiKey": "Ключ API Stadia",
|
||||
"appSettings_stadiaApiKeyRequired": "Требуется для использования Stadia Maps",
|
||||
"appSettings_stadiaApiKeyConfigured": "Настроено: {maskedKey}",
|
||||
"@appSettings_stadiaApiKeyConfigured": {
|
||||
"placeholders": {
|
||||
"maskedKey": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"appSettings_stadiaApiKeyDialogDescription": "Введите свой ключ API Stadia Maps. Приложение использует его для запросов растровых тайлов.",
|
||||
"appSettings_offlineMapCache": "Кэш офлайн-карты",
|
||||
"appSettings_noAreaSelected": "Область не выбрана",
|
||||
"appSettings_areaSelectedZoom": "Область выбрана (масштаб {minZoom}–{maxZoom})",
|
||||
@@ -440,6 +453,56 @@
|
||||
"mapCache_downloadTilesButton": "Загрузить плитки",
|
||||
"mapCache_clearCacheButton": "Очистить кэш",
|
||||
"mapCache_failedDownloads": "Неудачных загрузок: {count}",
|
||||
"mapCache_cachedTilesLabel": "Cached tiles",
|
||||
"mapCache_cachedTileSummaryLabel": "Cached tile summary",
|
||||
"mapCache_bulkDownloadDisabledForSource": "Offline bulk downloads are disabled for {source}.",
|
||||
"@mapCache_bulkDownloadDisabledForSource": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_bulkDownloadDisabledInConfig": "Offline bulk downloads are disabled for {source} in this app configuration.",
|
||||
"@mapCache_bulkDownloadDisabledInConfig": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summarySource": "Source: {source}",
|
||||
"@mapCache_summarySource": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryCachedTilesForSource": "Cached tiles for source: {count}",
|
||||
"@mapCache_summaryCachedTilesForSource": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryCachedInSelection": "Cached in selected area/zoom: {count}",
|
||||
"@mapCache_summaryCachedInSelection": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryApproxCacheSize": "Approx cache size: {size}",
|
||||
"@mapCache_summaryApproxCacheSize": {
|
||||
"placeholders": {
|
||||
"size": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_boundsLabel": "С {north}, Ю {south}, В {east}, З {west}",
|
||||
"time_justNow": "Только что",
|
||||
"time_minutesAgo": "{minutes} мин назад",
|
||||
|
||||
@@ -242,6 +242,19 @@
|
||||
"appSettings_last6Hours": "Posledné 6 hodín",
|
||||
"appSettings_last24Hours": "Posledných 24 hodín",
|
||||
"appSettings_lastWeek": "Minul týždeň",
|
||||
"appSettings_rasterTileSource": "Zdroj rastrových dlaždíc",
|
||||
"appSettings_stadiaEndpoint": "Koncový bod Stadia",
|
||||
"appSettings_stadiaApiKey": "Kľúč API Stadia",
|
||||
"appSettings_stadiaApiKeyRequired": "Vyžaduje sa na používanie Stadia Maps",
|
||||
"appSettings_stadiaApiKeyConfigured": "Nakonfigurované: {maskedKey}",
|
||||
"@appSettings_stadiaApiKeyConfigured": {
|
||||
"placeholders": {
|
||||
"maskedKey": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"appSettings_stadiaApiKeyDialogDescription": "Zadajte svoj kľúč API pre Stadia Maps. Aplikácia ho používa na požiadavky na rastrové dlaždice.",
|
||||
"appSettings_offlineMapCache": "Offline Mapa Pamäť",
|
||||
"appSettings_noAreaSelected": "Neoznačila sa žiadna oblasť",
|
||||
"appSettings_areaSelectedZoom": "Vyberená oblasť (zoom {minZoom}-{maxZoom})",
|
||||
@@ -771,6 +784,56 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_cachedTilesLabel": "Cached tiles",
|
||||
"mapCache_cachedTileSummaryLabel": "Cached tile summary",
|
||||
"mapCache_bulkDownloadDisabledForSource": "Offline bulk downloads are disabled for {source}.",
|
||||
"@mapCache_bulkDownloadDisabledForSource": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_bulkDownloadDisabledInConfig": "Offline bulk downloads are disabled for {source} in this app configuration.",
|
||||
"@mapCache_bulkDownloadDisabledInConfig": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summarySource": "Source: {source}",
|
||||
"@mapCache_summarySource": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryCachedTilesForSource": "Cached tiles for source: {count}",
|
||||
"@mapCache_summaryCachedTilesForSource": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryCachedInSelection": "Cached in selected area/zoom: {count}",
|
||||
"@mapCache_summaryCachedInSelection": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryApproxCacheSize": "Approx cache size: {size}",
|
||||
"@mapCache_summaryApproxCacheSize": {
|
||||
"placeholders": {
|
||||
"size": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_boundsLabel": "N {north}, S {south}, E {east}, W {west}",
|
||||
"@mapCache_boundsLabel": {
|
||||
"placeholders": {
|
||||
|
||||
@@ -242,6 +242,19 @@
|
||||
"appSettings_last6Hours": "Zadnjih 6 ur",
|
||||
"appSettings_last24Hours": "Zadnjih 24 ur",
|
||||
"appSettings_lastWeek": "Prejšnji teden",
|
||||
"appSettings_rasterTileSource": "Vir rastrskih ploščic",
|
||||
"appSettings_stadiaEndpoint": "Končna točka Stadia",
|
||||
"appSettings_stadiaApiKey": "Ključ API Stadia",
|
||||
"appSettings_stadiaApiKeyRequired": "Obvezno za uporabo Stadia Maps",
|
||||
"appSettings_stadiaApiKeyConfigured": "Nastavljeno: {maskedKey}",
|
||||
"@appSettings_stadiaApiKeyConfigured": {
|
||||
"placeholders": {
|
||||
"maskedKey": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"appSettings_stadiaApiKeyDialogDescription": "Vnesite svoj ključ API za Stadia Maps. Aplikacija ga uporablja za zahteve rastrskih ploščic.",
|
||||
"appSettings_offlineMapCache": "Shramba zemljevidov brez povezave",
|
||||
"appSettings_noAreaSelected": "Območje ni izbrano",
|
||||
"appSettings_areaSelectedZoom": "Izbrano območje (povečava {minZoom}-{maxZoom})",
|
||||
@@ -771,6 +784,56 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_cachedTilesLabel": "Cached tiles",
|
||||
"mapCache_cachedTileSummaryLabel": "Cached tile summary",
|
||||
"mapCache_bulkDownloadDisabledForSource": "Offline bulk downloads are disabled for {source}.",
|
||||
"@mapCache_bulkDownloadDisabledForSource": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_bulkDownloadDisabledInConfig": "Offline bulk downloads are disabled for {source} in this app configuration.",
|
||||
"@mapCache_bulkDownloadDisabledInConfig": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summarySource": "Source: {source}",
|
||||
"@mapCache_summarySource": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryCachedTilesForSource": "Cached tiles for source: {count}",
|
||||
"@mapCache_summaryCachedTilesForSource": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryCachedInSelection": "Cached in selected area/zoom: {count}",
|
||||
"@mapCache_summaryCachedInSelection": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryApproxCacheSize": "Approx cache size: {size}",
|
||||
"@mapCache_summaryApproxCacheSize": {
|
||||
"placeholders": {
|
||||
"size": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_boundsLabel": "N {north}, S {south}, E {east}, W {west}",
|
||||
"@mapCache_boundsLabel": {
|
||||
"placeholders": {
|
||||
|
||||
@@ -242,6 +242,19 @@
|
||||
"appSettings_last6Hours": "De senaste 6 timmarna",
|
||||
"appSettings_last24Hours": "De senaste 24 timmarna",
|
||||
"appSettings_lastWeek": "Förra veckan",
|
||||
"appSettings_rasterTileSource": "Källa för rasterplattor",
|
||||
"appSettings_stadiaEndpoint": "Stadia-slutpunkt",
|
||||
"appSettings_stadiaApiKey": "Stadia API-nyckel",
|
||||
"appSettings_stadiaApiKeyRequired": "Krävs för att använda Stadia Maps",
|
||||
"appSettings_stadiaApiKeyConfigured": "Konfigurerad: {maskedKey}",
|
||||
"@appSettings_stadiaApiKeyConfigured": {
|
||||
"placeholders": {
|
||||
"maskedKey": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"appSettings_stadiaApiKeyDialogDescription": "Ange din Stadia Maps API-nyckel. Appen använder den för förfrågningar om rasterplattor.",
|
||||
"appSettings_offlineMapCache": "Offline Kartcache",
|
||||
"appSettings_noAreaSelected": "Ingen area markerad",
|
||||
"appSettings_areaSelectedZoom": "Område markerat (zoom {minZoom}-{maxZoom})",
|
||||
@@ -771,6 +784,56 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_cachedTilesLabel": "Cached tiles",
|
||||
"mapCache_cachedTileSummaryLabel": "Cached tile summary",
|
||||
"mapCache_bulkDownloadDisabledForSource": "Offline bulk downloads are disabled for {source}.",
|
||||
"@mapCache_bulkDownloadDisabledForSource": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_bulkDownloadDisabledInConfig": "Offline bulk downloads are disabled for {source} in this app configuration.",
|
||||
"@mapCache_bulkDownloadDisabledInConfig": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summarySource": "Source: {source}",
|
||||
"@mapCache_summarySource": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryCachedTilesForSource": "Cached tiles for source: {count}",
|
||||
"@mapCache_summaryCachedTilesForSource": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryCachedInSelection": "Cached in selected area/zoom: {count}",
|
||||
"@mapCache_summaryCachedInSelection": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryApproxCacheSize": "Approx cache size: {size}",
|
||||
"@mapCache_summaryApproxCacheSize": {
|
||||
"placeholders": {
|
||||
"size": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_boundsLabel": "N {north}, S {south}, E {east}, W {west}",
|
||||
"@mapCache_boundsLabel": {
|
||||
"placeholders": {
|
||||
|
||||
@@ -244,6 +244,19 @@
|
||||
"appSettings_last6Hours": "Останні 6 годин",
|
||||
"appSettings_last24Hours": "Останні 24 години",
|
||||
"appSettings_lastWeek": "Минулий тиждень",
|
||||
"appSettings_rasterTileSource": "Джерело растрових тайлів",
|
||||
"appSettings_stadiaEndpoint": "Кінцева точка Stadia",
|
||||
"appSettings_stadiaApiKey": "Ключ API Stadia",
|
||||
"appSettings_stadiaApiKeyRequired": "Потрібно для використання Stadia Maps",
|
||||
"appSettings_stadiaApiKeyConfigured": "Налаштовано: {maskedKey}",
|
||||
"@appSettings_stadiaApiKeyConfigured": {
|
||||
"placeholders": {
|
||||
"maskedKey": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"appSettings_stadiaApiKeyDialogDescription": "Введіть свій ключ API Stadia Maps. Програма використовує його для запитів растрових тайлів.",
|
||||
"appSettings_offlineMapCache": "Офлайн-кеш карти",
|
||||
"appSettings_noAreaSelected": "Область не вибрано",
|
||||
"appSettings_areaSelectedZoom": "Вибрана область (зум {minZoom}-{maxZoom})",
|
||||
@@ -781,6 +794,56 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_cachedTilesLabel": "Cached tiles",
|
||||
"mapCache_cachedTileSummaryLabel": "Cached tile summary",
|
||||
"mapCache_bulkDownloadDisabledForSource": "Offline bulk downloads are disabled for {source}.",
|
||||
"@mapCache_bulkDownloadDisabledForSource": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_bulkDownloadDisabledInConfig": "Offline bulk downloads are disabled for {source} in this app configuration.",
|
||||
"@mapCache_bulkDownloadDisabledInConfig": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summarySource": "Source: {source}",
|
||||
"@mapCache_summarySource": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryCachedTilesForSource": "Cached tiles for source: {count}",
|
||||
"@mapCache_summaryCachedTilesForSource": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryCachedInSelection": "Cached in selected area/zoom: {count}",
|
||||
"@mapCache_summaryCachedInSelection": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryApproxCacheSize": "Approx cache size: {size}",
|
||||
"@mapCache_summaryApproxCacheSize": {
|
||||
"placeholders": {
|
||||
"size": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_boundsLabel": "Пн {north}, Пд {south}, Сх {east}, Зх {west}",
|
||||
"@mapCache_boundsLabel": {
|
||||
"placeholders": {
|
||||
|
||||
@@ -256,6 +256,19 @@
|
||||
"appSettings_last6Hours": "过去6小时",
|
||||
"appSettings_last24Hours": "过去24小时",
|
||||
"appSettings_lastWeek": "上周",
|
||||
"appSettings_rasterTileSource": "栅格瓦片源",
|
||||
"appSettings_stadiaEndpoint": "Stadia 端点",
|
||||
"appSettings_stadiaApiKey": "Stadia API 密钥",
|
||||
"appSettings_stadiaApiKeyRequired": "使用 Stadia Maps 时必需",
|
||||
"appSettings_stadiaApiKeyConfigured": "已配置:{maskedKey}",
|
||||
"@appSettings_stadiaApiKeyConfigured": {
|
||||
"placeholders": {
|
||||
"maskedKey": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"appSettings_stadiaApiKeyDialogDescription": "请输入你的 Stadia Maps API 密钥。该应用会使用它来请求栅格瓦片。",
|
||||
"appSettings_offlineMapCache": "离线地图缓存",
|
||||
"appSettings_noAreaSelected": "未选择任何区域",
|
||||
"appSettings_areaSelectedZoom": "已选择区域(缩放 {minZoom} - {maxZoom})",
|
||||
@@ -798,6 +811,56 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_cachedTilesLabel": "Cached tiles",
|
||||
"mapCache_cachedTileSummaryLabel": "Cached tile summary",
|
||||
"mapCache_bulkDownloadDisabledForSource": "Offline bulk downloads are disabled for {source}.",
|
||||
"@mapCache_bulkDownloadDisabledForSource": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_bulkDownloadDisabledInConfig": "Offline bulk downloads are disabled for {source} in this app configuration.",
|
||||
"@mapCache_bulkDownloadDisabledInConfig": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summarySource": "Source: {source}",
|
||||
"@mapCache_summarySource": {
|
||||
"placeholders": {
|
||||
"source": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryCachedTilesForSource": "Cached tiles for source: {count}",
|
||||
"@mapCache_summaryCachedTilesForSource": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryCachedInSelection": "Cached in selected area/zoom: {count}",
|
||||
"@mapCache_summaryCachedInSelection": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_summaryApproxCacheSize": "Approx cache size: {size}",
|
||||
"@mapCache_summaryApproxCacheSize": {
|
||||
"placeholders": {
|
||||
"size": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapCache_boundsLabel": "北 {north}, 南 {south}, 东 {east}, 西 {west}",
|
||||
"@mapCache_boundsLabel": {
|
||||
"placeholders": {
|
||||
|
||||
+4
-2
@@ -51,7 +51,9 @@ void main() async {
|
||||
final bleDebugLogService = BleDebugLogService();
|
||||
final appDebugLogService = AppDebugLogService();
|
||||
final backgroundService = BackgroundService();
|
||||
final mapTileCacheService = MapTileCacheService();
|
||||
final mapTileCacheService = MapTileCacheService(
|
||||
appSettingsService: appSettingsService,
|
||||
);
|
||||
final chatTextScaleService = ChatTextScaleService();
|
||||
final translationService = TranslationService(appSettingsService);
|
||||
final uiViewStateService = UiViewStateService();
|
||||
@@ -183,7 +185,7 @@ class MeshCoreApp extends StatelessWidget {
|
||||
ChangeNotifierProvider.value(value: translationService),
|
||||
ChangeNotifierProvider.value(value: uiViewStateService),
|
||||
Provider.value(value: storage),
|
||||
Provider.value(value: mapTileCacheService),
|
||||
ChangeNotifierProvider.value(value: mapTileCacheService),
|
||||
ChangeNotifierProvider.value(value: timeoutPredictionService),
|
||||
],
|
||||
child: Consumer<AppSettingsService>(
|
||||
|
||||
@@ -76,6 +76,8 @@ class Cyr2LatProfile {
|
||||
|
||||
class AppSettings {
|
||||
static const Object _unset = Object();
|
||||
static const String stadiaDemo =
|
||||
'51bd0381-4685-4666-bae8-48940f6d77c0';
|
||||
|
||||
final bool clearPathOnMaxRetry;
|
||||
final bool mapShowRepeaters;
|
||||
@@ -91,6 +93,9 @@ class AppSettings {
|
||||
final Map<String, double>? mapCacheBounds;
|
||||
final int mapCacheMinZoom;
|
||||
final int mapCacheMaxZoom;
|
||||
final String mapRasterSourceId;
|
||||
final String mapTileEndpointId;
|
||||
final String? mapTileApiKey;
|
||||
final bool notificationsEnabled;
|
||||
final bool notifyOnNewMessage;
|
||||
final bool notifyOnNewChannelMessage;
|
||||
@@ -124,6 +129,17 @@ class AppSettings {
|
||||
final List<Cyr2LatProfile> cyr2latProfiles;
|
||||
final String selectedCyr2latProfileId;
|
||||
|
||||
String get effectiveMapTileApiKey {
|
||||
final apiKey = mapTileApiKey?.trim();
|
||||
if (apiKey == null || apiKey.isEmpty) {
|
||||
return stadiaDemo;
|
||||
}
|
||||
return apiKey;
|
||||
}
|
||||
|
||||
bool get usesstadiaDemo =>
|
||||
effectiveMapTileApiKey == stadiaDemo;
|
||||
|
||||
Map<String, String> get cyr2latCharMap {
|
||||
final profile = cyr2latProfiles.firstWhere(
|
||||
(p) => p.id == selectedCyr2latProfileId,
|
||||
@@ -147,6 +163,9 @@ class AppSettings {
|
||||
this.mapCacheBounds,
|
||||
this.mapCacheMinZoom = 10,
|
||||
this.mapCacheMaxZoom = 15,
|
||||
this.mapRasterSourceId = 'osm_auto',
|
||||
this.mapTileEndpointId = 'standard_2x',
|
||||
this.mapTileApiKey,
|
||||
this.notificationsEnabled = true,
|
||||
this.notifyOnNewMessage = true,
|
||||
this.notifyOnNewChannelMessage = true,
|
||||
@@ -210,6 +229,9 @@ class AppSettings {
|
||||
'map_cache_bounds': mapCacheBounds,
|
||||
'map_cache_min_zoom': mapCacheMinZoom,
|
||||
'map_cache_max_zoom': mapCacheMaxZoom,
|
||||
'map_raster_source_id': mapRasterSourceId,
|
||||
'map_tile_endpoint_id': mapTileEndpointId,
|
||||
'map_tile_api_key': mapTileApiKey,
|
||||
'notifications_enabled': notificationsEnabled,
|
||||
'notify_on_new_message': notifyOnNewMessage,
|
||||
'notify_on_new_channel_message': notifyOnNewChannelMessage,
|
||||
@@ -277,6 +299,9 @@ class AppSettings {
|
||||
),
|
||||
mapCacheMinZoom: json['map_cache_min_zoom'] as int? ?? 10,
|
||||
mapCacheMaxZoom: json['map_cache_max_zoom'] as int? ?? 15,
|
||||
mapRasterSourceId: json['map_raster_source_id'] as String? ?? 'osm_auto',
|
||||
mapTileEndpointId: json['map_tile_endpoint_id'] as String? ?? 'standard',
|
||||
mapTileApiKey: json['map_tile_api_key'] as String?,
|
||||
notificationsEnabled: json['notifications_enabled'] as bool? ?? true,
|
||||
notifyOnNewMessage: json['notify_on_new_message'] as bool? ?? true,
|
||||
notifyOnNewChannelMessage:
|
||||
@@ -390,6 +415,9 @@ class AppSettings {
|
||||
Object? mapCacheBounds = _unset,
|
||||
int? mapCacheMinZoom,
|
||||
int? mapCacheMaxZoom,
|
||||
String? mapRasterSourceId,
|
||||
String? mapTileEndpointId,
|
||||
Object? mapTileApiKey = _unset,
|
||||
bool? notificationsEnabled,
|
||||
bool? notifyOnNewMessage,
|
||||
bool? notifyOnNewChannelMessage,
|
||||
@@ -441,6 +469,11 @@ class AppSettings {
|
||||
: mapCacheBounds as Map<String, double>?,
|
||||
mapCacheMinZoom: mapCacheMinZoom ?? this.mapCacheMinZoom,
|
||||
mapCacheMaxZoom: mapCacheMaxZoom ?? this.mapCacheMaxZoom,
|
||||
mapRasterSourceId: mapRasterSourceId ?? this.mapRasterSourceId,
|
||||
mapTileEndpointId: mapTileEndpointId ?? this.mapTileEndpointId,
|
||||
mapTileApiKey: mapTileApiKey == _unset
|
||||
? this.mapTileApiKey
|
||||
: mapTileApiKey as String?,
|
||||
notificationsEnabled: notificationsEnabled ?? this.notificationsEnabled,
|
||||
notifyOnNewMessage: notifyOnNewMessage ?? this.notifyOnNewMessage,
|
||||
notifyOnNewChannelMessage:
|
||||
|
||||
@@ -8,6 +8,7 @@ import '../l10n/l10n.dart';
|
||||
import '../models/app_settings.dart';
|
||||
import '../models/translation_support.dart';
|
||||
import '../services/app_settings_service.dart';
|
||||
import '../services/map_tile_cache_service.dart';
|
||||
import '../services/notification_service.dart';
|
||||
import '../services/translation_service.dart';
|
||||
import '../theme/mesh_theme.dart';
|
||||
@@ -699,52 +700,229 @@ class AppSettingsScreen extends StatelessWidget {
|
||||
) {
|
||||
final scheme = Theme.of(context).colorScheme;
|
||||
final textTheme = Theme.of(context).textTheme;
|
||||
return Column(
|
||||
children: [
|
||||
SwitchListTile(
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 4,
|
||||
final children = <Widget>[
|
||||
SwitchListTile(
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
||||
secondary: const Icon(Icons.router_outlined, size: 20),
|
||||
title: Text(context.l10n.appSettings_showRepeaters),
|
||||
subtitle: Text(context.l10n.appSettings_showRepeatersSubtitle),
|
||||
value: settingsService.settings.mapShowRepeaters,
|
||||
onChanged: (value) => settingsService.setMapShowRepeaters(value),
|
||||
),
|
||||
const Divider(height: 1, indent: 16),
|
||||
SwitchListTile(
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
||||
secondary: const Icon(Icons.chat_outlined, size: 20),
|
||||
title: Text(context.l10n.appSettings_showChatNodes),
|
||||
subtitle: Text(context.l10n.appSettings_showChatNodesSubtitle),
|
||||
value: settingsService.settings.mapShowChatNodes,
|
||||
onChanged: (value) => settingsService.setMapShowChatNodes(value),
|
||||
),
|
||||
const Divider(height: 1, indent: 16),
|
||||
SwitchListTile(
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
||||
secondary: const Icon(Icons.people_outline, size: 20),
|
||||
title: Text(context.l10n.appSettings_showOtherNodes),
|
||||
subtitle: Text(context.l10n.appSettings_showOtherNodesSubtitle),
|
||||
value: settingsService.settings.mapShowOtherNodes,
|
||||
onChanged: (value) => settingsService.setMapShowOtherNodes(value),
|
||||
),
|
||||
const Divider(height: 1, indent: 16),
|
||||
InkWell(
|
||||
onTap: () => _showTimeFilterSheet(context, settingsService),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.timer_outlined,
|
||||
size: 20,
|
||||
color: scheme.onSurfaceVariant,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
context.l10n.appSettings_timeFilter,
|
||||
style: textTheme.bodyMedium?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
settingsService.settings.mapTimeFilterHours == 0
|
||||
? context.l10n.appSettings_timeFilterShowAll
|
||||
: context.l10n.appSettings_timeFilterShowLast(
|
||||
settingsService.settings.mapTimeFilterHours
|
||||
.toInt(),
|
||||
),
|
||||
style: textTheme.bodySmall?.copyWith(
|
||||
color: scheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Icon(
|
||||
Icons.chevron_right,
|
||||
color: scheme.onSurfaceVariant,
|
||||
size: 16,
|
||||
),
|
||||
],
|
||||
),
|
||||
secondary: const Icon(Icons.router_outlined, size: 20),
|
||||
title: Text(context.l10n.appSettings_showRepeaters),
|
||||
subtitle: Text(context.l10n.appSettings_showRepeatersSubtitle),
|
||||
value: settingsService.settings.mapShowRepeaters,
|
||||
onChanged: (value) => settingsService.setMapShowRepeaters(value),
|
||||
),
|
||||
const Divider(height: 1, indent: 16),
|
||||
SwitchListTile(
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 4,
|
||||
),
|
||||
const Divider(height: 1, indent: 16),
|
||||
InkWell(
|
||||
onTap: () => _showUnitsSheet(context, settingsService),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.straighten, size: 20, color: scheme.onSurfaceVariant),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
context.l10n.appSettings_unitsTitle,
|
||||
style: textTheme.bodyMedium?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
settingsService.settings.unitSystem == UnitSystem.imperial
|
||||
? context.l10n.appSettings_unitsImperial
|
||||
: context.l10n.appSettings_unitsMetric,
|
||||
style: textTheme.bodySmall?.copyWith(
|
||||
color: scheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Icon(
|
||||
Icons.chevron_right,
|
||||
color: scheme.onSurfaceVariant,
|
||||
size: 16,
|
||||
),
|
||||
],
|
||||
),
|
||||
secondary: const Icon(Icons.chat_outlined, size: 20),
|
||||
title: Text(context.l10n.appSettings_showChatNodes),
|
||||
subtitle: Text(context.l10n.appSettings_showChatNodesSubtitle),
|
||||
value: settingsService.settings.mapShowChatNodes,
|
||||
onChanged: (value) => settingsService.setMapShowChatNodes(value),
|
||||
),
|
||||
const Divider(height: 1, indent: 16),
|
||||
SwitchListTile(
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 4,
|
||||
),
|
||||
const Divider(height: 1, indent: 16),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => const MapCacheScreen()),
|
||||
);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.download_outlined,
|
||||
size: 20,
|
||||
color: scheme.onSurfaceVariant,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
context.l10n.appSettings_offlineMapCache,
|
||||
style: textTheme.bodyMedium?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
settingsService.settings.mapCacheBounds == null
|
||||
? context.l10n.appSettings_noAreaSelected
|
||||
: context.l10n.appSettings_areaSelectedZoom(
|
||||
settingsService.settings.mapCacheMinZoom,
|
||||
settingsService.settings.mapCacheMaxZoom,
|
||||
),
|
||||
style: textTheme.bodySmall?.copyWith(
|
||||
color: scheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Icon(
|
||||
Icons.chevron_right,
|
||||
color: scheme.onSurfaceVariant,
|
||||
size: 16,
|
||||
),
|
||||
],
|
||||
),
|
||||
secondary: const Icon(Icons.people_outline, size: 20),
|
||||
title: Text(context.l10n.appSettings_showOtherNodes),
|
||||
subtitle: Text(context.l10n.appSettings_showOtherNodesSubtitle),
|
||||
value: settingsService.settings.mapShowOtherNodes,
|
||||
onChanged: (value) => settingsService.setMapShowOtherNodes(value),
|
||||
),
|
||||
),
|
||||
const Divider(height: 1, indent: 16),
|
||||
InkWell(
|
||||
onTap: () => _showMapRasterSourceDialog(context, settingsService),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.layers_outlined,
|
||||
size: 20,
|
||||
color: scheme.onSurfaceVariant,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
context.l10n.appSettings_rasterTileSource,
|
||||
style: textTheme.bodyMedium?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
_mapRasterSourceSummary(settingsService.settings),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: textTheme.bodySmall?.copyWith(
|
||||
color: scheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Icon(
|
||||
Icons.chevron_right,
|
||||
color: scheme.onSurfaceVariant,
|
||||
size: 16,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
];
|
||||
|
||||
if (_isStadiaSource(settingsService.settings)) {
|
||||
children.addAll([
|
||||
const Divider(height: 1, indent: 16),
|
||||
InkWell(
|
||||
onTap: () => _showTimeFilterSheet(context, settingsService),
|
||||
onTap: () => _showMapRasterEndpointDialog(context, settingsService),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.timer_outlined,
|
||||
Icons.public_outlined,
|
||||
size: 20,
|
||||
color: scheme.onSurfaceVariant,
|
||||
),
|
||||
@@ -754,19 +932,14 @@ class AppSettingsScreen extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
context.l10n.appSettings_timeFilter,
|
||||
context.l10n.appSettings_stadiaEndpoint,
|
||||
style: textTheme.bodyMedium?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
settingsService.settings.mapTimeFilterHours == 0
|
||||
? context.l10n.appSettings_timeFilterShowAll
|
||||
: context.l10n.appSettings_timeFilterShowLast(
|
||||
settingsService.settings.mapTimeFilterHours
|
||||
.toInt(),
|
||||
),
|
||||
_mapRasterEndpointSummary(settingsService.settings),
|
||||
style: textTheme.bodySmall?.copyWith(
|
||||
color: scheme.onSurfaceVariant,
|
||||
),
|
||||
@@ -785,13 +958,13 @@ class AppSettingsScreen extends StatelessWidget {
|
||||
),
|
||||
const Divider(height: 1, indent: 16),
|
||||
InkWell(
|
||||
onTap: () => _showUnitsSheet(context, settingsService),
|
||||
onTap: () => _showMapApiKeyDialog(context, settingsService),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.straighten,
|
||||
Icons.key_outlined,
|
||||
size: 20,
|
||||
color: scheme.onSurfaceVariant,
|
||||
),
|
||||
@@ -801,17 +974,14 @@ class AppSettingsScreen extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
context.l10n.appSettings_unitsTitle,
|
||||
context.l10n.appSettings_stadiaApiKey,
|
||||
style: textTheme.bodyMedium?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
settingsService.settings.unitSystem ==
|
||||
UnitSystem.imperial
|
||||
? context.l10n.appSettings_unitsImperial
|
||||
: context.l10n.appSettings_unitsMetric,
|
||||
_mapApiKeySummary(context, settingsService.settings),
|
||||
style: textTheme.bodySmall?.copyWith(
|
||||
color: scheme.onSurfaceVariant,
|
||||
),
|
||||
@@ -828,59 +998,210 @@ class AppSettingsScreen extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
const Divider(height: 1, indent: 16),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => const MapCacheScreen()),
|
||||
);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.download_outlined,
|
||||
size: 20,
|
||||
color: scheme.onSurfaceVariant,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
]);
|
||||
}
|
||||
|
||||
return Column(children: children);
|
||||
}
|
||||
|
||||
String _mapRasterSourceSummary(AppSettings settings) {
|
||||
final source = MapRasterSourceCatalog.fromSettings(settings);
|
||||
return '${source.label} - ${source.description}';
|
||||
}
|
||||
|
||||
bool _isStadiaSource(AppSettings settings) {
|
||||
return MapRasterSourceCatalog.fromSettings(settings).isStadia;
|
||||
}
|
||||
|
||||
String _mapRasterEndpointSummary(AppSettings settings) {
|
||||
final endpoint = MapRasterEndpointCatalog.fromSettings(settings);
|
||||
return '${endpoint.label} - ${endpoint.description}';
|
||||
}
|
||||
|
||||
String _mapApiKeySummary(BuildContext context, AppSettings settings) {
|
||||
return context.l10n.appSettings_stadiaApiKeyConfigured(
|
||||
_maskApiKey(settings.effectiveMapTileApiKey),
|
||||
);
|
||||
}
|
||||
|
||||
String _maskApiKey(String value) {
|
||||
if (value.length <= 8) return '********';
|
||||
return '${value.substring(0, 4)}...${value.substring(value.length - 4)}';
|
||||
}
|
||||
|
||||
void _showMapRasterSourceDialog(
|
||||
BuildContext context,
|
||||
AppSettingsService settingsService,
|
||||
) {
|
||||
String selectedId = settingsService.settings.mapRasterSourceId;
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (dialogContext) => StatefulBuilder(
|
||||
builder: (dialogContext, setState) => AlertDialog(
|
||||
title: Text(context.l10n.appSettings_rasterTileSource),
|
||||
content: SizedBox(
|
||||
width: 360,
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
maxHeight: MediaQuery.of(dialogContext).size.height * 0.6,
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
child: RadioGroup<String>(
|
||||
groupValue: selectedId,
|
||||
onChanged: (value) {
|
||||
if (value == null) return;
|
||||
setState(() {
|
||||
selectedId = value;
|
||||
});
|
||||
},
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
context.l10n.appSettings_offlineMapCache,
|
||||
style: textTheme.bodyMedium?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
for (final preset in MapRasterSourcePreset.values)
|
||||
Builder(
|
||||
builder: (context) {
|
||||
final option = MapRasterSourceCatalog.fromPreset(
|
||||
preset,
|
||||
);
|
||||
return RadioListTile<String>(
|
||||
value: preset.id,
|
||||
title: Text(option.label),
|
||||
subtitle: Text(option.description),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
settingsService.settings.mapCacheBounds == null
|
||||
? context.l10n.appSettings_noAreaSelected
|
||||
: context.l10n.appSettings_areaSelectedZoom(
|
||||
settingsService.settings.mapCacheMinZoom,
|
||||
settingsService.settings.mapCacheMaxZoom,
|
||||
),
|
||||
style: textTheme.bodySmall?.copyWith(
|
||||
color: scheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Icon(
|
||||
Icons.chevron_right,
|
||||
color: scheme.onSurfaceVariant,
|
||||
size: 16,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(dialogContext),
|
||||
child: Text(context.l10n.common_cancel),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
await settingsService.setMapRasterSourceId(selectedId);
|
||||
if (!dialogContext.mounted) return;
|
||||
Navigator.pop(dialogContext);
|
||||
},
|
||||
child: Text(context.l10n.common_save),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showMapRasterEndpointDialog(
|
||||
BuildContext context,
|
||||
AppSettingsService settingsService,
|
||||
) {
|
||||
String selectedId = settingsService.settings.mapTileEndpointId;
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (dialogContext) => StatefulBuilder(
|
||||
builder: (dialogContext, setState) => AlertDialog(
|
||||
title: Text(context.l10n.appSettings_stadiaEndpoint),
|
||||
content: SizedBox(
|
||||
width: 360,
|
||||
child: RadioGroup<String>(
|
||||
groupValue: selectedId,
|
||||
onChanged: (value) {
|
||||
if (value == null) return;
|
||||
setState(() {
|
||||
selectedId = value;
|
||||
});
|
||||
},
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
for (final option in MapRasterEndpointCatalog.presets)
|
||||
RadioListTile<String>(
|
||||
value: option.id,
|
||||
title: Text(option.label),
|
||||
subtitle: Text(option.description),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(dialogContext),
|
||||
child: Text(context.l10n.common_cancel),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
await settingsService.setMapTileEndpointId(selectedId);
|
||||
if (!dialogContext.mounted) return;
|
||||
Navigator.pop(dialogContext);
|
||||
},
|
||||
child: Text(context.l10n.common_save),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showMapApiKeyDialog(
|
||||
BuildContext context,
|
||||
AppSettingsService settingsService,
|
||||
) {
|
||||
final currentApiKey = settingsService.settings.mapTileApiKey?.trim() ?? '';
|
||||
final maskedApiKey = _maskApiKey(
|
||||
currentApiKey.isEmpty
|
||||
? AppSettings.stadiaDemo
|
||||
: currentApiKey,
|
||||
);
|
||||
final controller = TextEditingController(text: maskedApiKey);
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (dialogContext) => AlertDialog(
|
||||
title: Text(context.l10n.appSettings_stadiaApiKey),
|
||||
content: SizedBox(
|
||||
width: 420,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(context.l10n.appSettings_stadiaApiKeyDialogDescription),
|
||||
const SizedBox(height: 12),
|
||||
TextField(
|
||||
controller: controller,
|
||||
autofocus: true,
|
||||
autocorrect: false,
|
||||
enableSuggestions: false,
|
||||
autofillHints: const [AutofillHints.password],
|
||||
decoration: const InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
hintText: '4e1bf343-3d91-4d9c-a8e1-1234567890ab',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(dialogContext),
|
||||
child: Text(context.l10n.common_cancel),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
final apiKey = controller.text.trim();
|
||||
await settingsService.setMapTileApiKey(
|
||||
apiKey == maskedApiKey ? currentApiKey : apiKey,
|
||||
);
|
||||
if (!dialogContext.mounted) return;
|
||||
Navigator.pop(dialogContext);
|
||||
},
|
||||
child: Text(context.l10n.common_save),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ import '../theme/mesh_theme.dart';
|
||||
import '../widgets/adaptive_app_bar_title.dart';
|
||||
import '../widgets/mesh_ui.dart';
|
||||
import '../widgets/path_map_ui.dart';
|
||||
import '../widgets/themed_map_tile_layer.dart';
|
||||
|
||||
class ChannelMessagePathScreen extends StatelessWidget {
|
||||
final ChannelMessage message;
|
||||
@@ -967,7 +966,7 @@ class _ChannelMessagePathMapScreenState
|
||||
},
|
||||
),
|
||||
children: [
|
||||
ThemedMapTileLayer(tileCache: tileCache),
|
||||
tileCache.buildTileLayer(context),
|
||||
AnimatedBuilder(
|
||||
animation: _playback,
|
||||
builder: (context, _) {
|
||||
|
||||
@@ -282,8 +282,6 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
||||
buildDefaultDragHandles: false,
|
||||
itemCount: filteredChannels.length,
|
||||
onReorderItem: (oldIndex, newIndex) {
|
||||
// onReorderItem already adjusts newIndex after the
|
||||
// removed item, unlike the deprecated onReorder.
|
||||
final reordered = List<Channel>.from(
|
||||
filteredChannels,
|
||||
);
|
||||
|
||||
@@ -20,7 +20,6 @@ import '../widgets/app_bar.dart';
|
||||
import '../widgets/quick_switch_bar.dart';
|
||||
import '../icons/los_icon.dart';
|
||||
import '../theme/mesh_theme.dart';
|
||||
import '../widgets/themed_map_tile_layer.dart';
|
||||
|
||||
class LineOfSightEndpoint {
|
||||
final String label;
|
||||
@@ -357,7 +356,7 @@ class _LineOfSightMapScreenState extends State<LineOfSightMapScreen> {
|
||||
Widget build(BuildContext context) {
|
||||
final settings = context.watch<AppSettingsService>().settings;
|
||||
final isImperial = settings.unitSystem == UnitSystem.imperial;
|
||||
final tileCache = context.read<MapTileCacheService>();
|
||||
final tileCache = context.watch<MapTileCacheService>();
|
||||
final endpoints = _visibleEndpoints();
|
||||
final mapPoints = [
|
||||
if (_start != null) _start!.point,
|
||||
@@ -443,8 +442,8 @@ class _LineOfSightMapScreenState extends State<LineOfSightMapScreen> {
|
||||
},
|
||||
),
|
||||
children: [
|
||||
ThemedMapTileLayer(
|
||||
tileCache: tileCache,
|
||||
tileCache.buildTileLayer(
|
||||
context,
|
||||
opacity: _showTerrainLayer ? 1 : 0.72,
|
||||
),
|
||||
if (_result != null && _result!.segments.isNotEmpty)
|
||||
|
||||
@@ -12,7 +12,6 @@ import '../widgets/adaptive_app_bar_title.dart';
|
||||
import '../helpers/snack_bar_builder.dart';
|
||||
import '../theme/mesh_theme.dart';
|
||||
import '../widgets/mesh_ui.dart';
|
||||
import '../widgets/themed_map_tile_layer.dart';
|
||||
|
||||
class MapCacheScreen extends StatefulWidget {
|
||||
const MapCacheScreen({super.key});
|
||||
@@ -34,6 +33,11 @@ class _MapCacheScreenState extends State<MapCacheScreen> {
|
||||
bool _isDownloading = false;
|
||||
int _completedTiles = 0;
|
||||
int _failedTiles = 0;
|
||||
List<CachedTileInfo> _cachedTiles = const [];
|
||||
int _cachedTileBytes = 0;
|
||||
bool _isLoadingCachedTiles = false;
|
||||
double _overlayZoom = 2.0;
|
||||
LatLngBounds? _visibleBounds;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -123,8 +127,10 @@ class _MapCacheScreenState extends State<MapCacheScreen> {
|
||||
_minZoom = safeMin;
|
||||
_maxZoom = safeMax;
|
||||
_selectedBounds = bounds;
|
||||
_visibleBounds = bounds;
|
||||
});
|
||||
_updateEstimate();
|
||||
_refreshCachedTiles();
|
||||
if (bounds != null) {
|
||||
_mapController.fitCamera(
|
||||
CameraFit.bounds(bounds: bounds, padding: const EdgeInsets.all(48)),
|
||||
@@ -181,6 +187,7 @@ class _MapCacheScreenState extends State<MapCacheScreen> {
|
||||
|
||||
Future<void> _startDownload() async {
|
||||
final bounds = _selectedBounds;
|
||||
final cacheService = context.read<MapTileCacheService>();
|
||||
if (bounds == null) {
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
@@ -197,6 +204,18 @@ class _MapCacheScreenState extends State<MapCacheScreen> {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!cacheService.source.allowsBulkDownload) {
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(
|
||||
context.l10n.mapCache_bulkDownloadDisabledInConfig(
|
||||
cacheService.source.label,
|
||||
),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
final confirmed = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (dialogContext) => AlertDialog(
|
||||
@@ -219,8 +238,6 @@ class _MapCacheScreenState extends State<MapCacheScreen> {
|
||||
|
||||
if (confirmed != true || !mounted) return;
|
||||
|
||||
final cacheService = context.read<MapTileCacheService>();
|
||||
|
||||
setState(() {
|
||||
_isDownloading = true;
|
||||
_completedTiles = 0;
|
||||
@@ -254,6 +271,8 @@ class _MapCacheScreenState extends State<MapCacheScreen> {
|
||||
result.failed,
|
||||
)
|
||||
: context.l10n.mapCache_cachedTiles(result.downloaded);
|
||||
await _refreshCachedTiles();
|
||||
if (!mounted) return;
|
||||
showDismissibleSnackBar(context, content: Text(message));
|
||||
}
|
||||
|
||||
@@ -280,16 +299,61 @@ class _MapCacheScreenState extends State<MapCacheScreen> {
|
||||
final cacheService = context.read<MapTileCacheService>();
|
||||
await cacheService.clearCache();
|
||||
if (!mounted) return;
|
||||
await _refreshCachedTiles();
|
||||
if (!mounted) return;
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.mapCache_offlineCacheCleared),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _refreshCachedTiles() async {
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_isLoadingCachedTiles = true;
|
||||
});
|
||||
final cacheService = context.read<MapTileCacheService>();
|
||||
final inventory = await cacheService.getCachedTileInventory();
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_cachedTiles = inventory.tiles;
|
||||
_cachedTileBytes = inventory.totalBytes;
|
||||
_isLoadingCachedTiles = false;
|
||||
});
|
||||
}
|
||||
|
||||
String _formatBytes(int bytes) {
|
||||
if (bytes < 1024) return '$bytes B';
|
||||
if (bytes < 1024 * 1024) {
|
||||
return '${(bytes / 1024).toStringAsFixed(1)} KB';
|
||||
}
|
||||
return '${(bytes / (1024 * 1024)).toStringAsFixed(1)} MB';
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final tileCache = context.read<MapTileCacheService>();
|
||||
final tileCache = context.watch<MapTileCacheService>();
|
||||
final source = tileCache.source;
|
||||
final selectedBounds = _selectedBounds;
|
||||
final activeCachedTiles = tileCache.filterTilesForActiveSource(
|
||||
_cachedTiles,
|
||||
);
|
||||
final cachedInSelection = tileCache.countTilesForBounds(
|
||||
activeCachedTiles,
|
||||
bounds: selectedBounds,
|
||||
minZoom: _minZoom,
|
||||
maxZoom: _maxZoom,
|
||||
);
|
||||
final overlayPolygons = tileCache.buildCachedTilePolygons(
|
||||
activeCachedTiles,
|
||||
zoom: _overlayZoom.round().clamp(0, 19).toInt(),
|
||||
visibleBounds: _visibleBounds,
|
||||
);
|
||||
final cacheSummary =
|
||||
'${context.l10n.mapCache_summarySource(source.label)}\n'
|
||||
'${context.l10n.mapCache_summaryCachedTilesForSource(activeCachedTiles.length)}\n'
|
||||
'${context.l10n.mapCache_summaryCachedInSelection(cachedInSelection)}\n'
|
||||
'${context.l10n.mapCache_summaryApproxCacheSize(_formatBytes(_cachedTileBytes))}';
|
||||
final l10n = context.l10n;
|
||||
final scheme = Theme.of(context).colorScheme;
|
||||
final isDesktop = _isDesktopPlatform(defaultTargetPlatform);
|
||||
@@ -327,9 +391,23 @@ class _MapCacheScreenState extends State<MapCacheScreen> {
|
||||
)
|
||||
: const KeyboardOptions.disabled(),
|
||||
),
|
||||
onPositionChanged: (camera, hasGesture) {
|
||||
final nextZoom = camera.zoom;
|
||||
final nextBounds = camera.visibleBounds;
|
||||
if (_overlayZoom != nextZoom ||
|
||||
_visibleBounds?.north != nextBounds.north ||
|
||||
_visibleBounds?.south != nextBounds.south ||
|
||||
_visibleBounds?.east != nextBounds.east ||
|
||||
_visibleBounds?.west != nextBounds.west) {
|
||||
setState(() {
|
||||
_overlayZoom = nextZoom;
|
||||
_visibleBounds = nextBounds;
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
children: [
|
||||
ThemedMapTileLayer(tileCache: tileCache),
|
||||
tileCache.buildTileLayer(context),
|
||||
if (selectedBounds != null)
|
||||
PolygonLayer(
|
||||
polygons: [
|
||||
@@ -341,9 +419,24 @@ class _MapCacheScreenState extends State<MapCacheScreen> {
|
||||
),
|
||||
],
|
||||
),
|
||||
if (overlayPolygons.isNotEmpty)
|
||||
PolygonLayer(polygons: overlayPolygons),
|
||||
],
|
||||
),
|
||||
if (isDesktop) _buildDesktopMapControls(),
|
||||
Positioned(
|
||||
top: 12,
|
||||
left: isDesktop ? 84 : 12,
|
||||
child: Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Text(
|
||||
'Z: ${_overlayZoom.round()}:',
|
||||
style: const TextStyle(fontSize: 12),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 12,
|
||||
right: 12,
|
||||
@@ -445,6 +538,34 @@ class _MapCacheScreenState extends State<MapCacheScreen> {
|
||||
color: scheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
TextFormField(
|
||||
key: ValueKey(
|
||||
'$cacheSummary|${activeCachedTiles.length}|$cachedInSelection|$_cachedTileBytes',
|
||||
),
|
||||
initialValue: cacheSummary,
|
||||
readOnly: true,
|
||||
minLines: 4,
|
||||
maxLines: 4,
|
||||
decoration: InputDecoration(
|
||||
labelText: _isLoadingCachedTiles
|
||||
? l10n.mapCache_cachedTilesLabel
|
||||
: l10n.mapCache_cachedTileSummaryLabel,
|
||||
border: const OutlineInputBorder(),
|
||||
),
|
||||
),
|
||||
if (!source.allowsBulkDownload) ...[
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
l10n.mapCache_bulkDownloadDisabledForSource(
|
||||
source.label,
|
||||
),
|
||||
style: MeshTheme.mono(
|
||||
fontSize: 12,
|
||||
color: MeshPalette.alert,
|
||||
),
|
||||
),
|
||||
],
|
||||
if (_isDownloading) ...[
|
||||
const SizedBox(height: 8),
|
||||
LinearProgressIndicator(
|
||||
@@ -471,7 +592,10 @@ class _MapCacheScreenState extends State<MapCacheScreen> {
|
||||
child: ElevatedButton.icon(
|
||||
icon: const Icon(Icons.download),
|
||||
label: Text(l10n.mapCache_downloadTilesButton),
|
||||
onPressed: _isDownloading || selectedBounds == null
|
||||
onPressed:
|
||||
_isDownloading ||
|
||||
selectedBounds == null ||
|
||||
!source.allowsBulkDownload
|
||||
? null
|
||||
: _startDownload,
|
||||
),
|
||||
|
||||
+124
-90
@@ -25,7 +25,6 @@ import '../utils/battery_utils.dart';
|
||||
import '../utils/route_transitions.dart';
|
||||
import '../widgets/quick_switch_bar.dart';
|
||||
import '../widgets/sync_progress_overlay.dart';
|
||||
import '../widgets/themed_map_tile_layer.dart';
|
||||
import '../icons/los_icon.dart';
|
||||
import 'channels_screen.dart';
|
||||
import 'chat_screen.dart';
|
||||
@@ -107,6 +106,31 @@ class _MapScreenState extends State<MapScreen> {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
ColorScheme get _overlayScheme => Theme.of(context).colorScheme;
|
||||
|
||||
bool get _useDarkOverlay => Theme.of(context).brightness == Brightness.dark;
|
||||
|
||||
Color get _overlayPanelColor => _useDarkOverlay
|
||||
? MapPalette.panelDark
|
||||
: _overlayScheme.surfaceContainerLow.withValues(alpha: 0.96);
|
||||
|
||||
Color get _overlayPrimaryTextColor =>
|
||||
_useDarkOverlay ? MapPalette.textPrimary : _overlayScheme.onSurface;
|
||||
|
||||
Color get _overlaySecondaryTextColor => _useDarkOverlay
|
||||
? MapPalette.textSecondary
|
||||
: _overlayScheme.onSurfaceVariant;
|
||||
|
||||
Color get _overlayMutedTextColor =>
|
||||
_useDarkOverlay ? MapPalette.textMuted : _overlayScheme.onSurfaceVariant;
|
||||
|
||||
Color get _overlayBorderColor =>
|
||||
_useDarkOverlay ? MapPalette.border : _overlayScheme.outlineVariant;
|
||||
|
||||
Color get _overlayShadowColor => _useDarkOverlay
|
||||
? MapPalette.markerShadow
|
||||
: Colors.black.withValues(alpha: 0.18);
|
||||
|
||||
_NodeAge _ageOf(Contact contact) {
|
||||
final d = DateTime.now().difference(contact.lastSeen);
|
||||
if (d.inMinutes <= 60) return _NodeAge.online;
|
||||
@@ -233,12 +257,12 @@ class _MapScreenState extends State<MapScreen> {
|
||||
bottom: 96,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: MapPalette.panelDark,
|
||||
color: _overlayPanelColor,
|
||||
borderRadius: BorderRadius.circular(MeshRadii.md),
|
||||
border: Border.all(color: MapPalette.border),
|
||||
boxShadow: const [
|
||||
border: Border.all(color: _overlayBorderColor),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: MapPalette.markerShadow,
|
||||
color: _overlayShadowColor,
|
||||
blurRadius: 8,
|
||||
offset: Offset(0, 3),
|
||||
),
|
||||
@@ -250,20 +274,20 @@ class _MapScreenState extends State<MapScreen> {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
IconButton(
|
||||
color: MapPalette.textPrimary,
|
||||
color: _overlayPrimaryTextColor,
|
||||
icon: const Icon(Icons.add),
|
||||
visualDensity: VisualDensity.standard,
|
||||
tooltip: context.l10n.map_zoomIn,
|
||||
onPressed: () => _zoomMapBy(1),
|
||||
),
|
||||
IconButton(
|
||||
color: MapPalette.textPrimary,
|
||||
color: _overlayPrimaryTextColor,
|
||||
icon: const Icon(Icons.remove),
|
||||
tooltip: context.l10n.map_zoomOut,
|
||||
onPressed: () => _zoomMapBy(-1),
|
||||
),
|
||||
IconButton(
|
||||
color: MapPalette.textPrimary,
|
||||
color: _overlayPrimaryTextColor,
|
||||
icon: const Icon(Icons.crop_free),
|
||||
tooltip: context.l10n.map_centerMap,
|
||||
onPressed: () => _mapController.move(center, zoom),
|
||||
@@ -328,6 +352,7 @@ class _MapScreenState extends State<MapScreen> {
|
||||
final settingsService = context.read<AppSettingsService>();
|
||||
final pathHistory = context.read<PathHistoryService>();
|
||||
final tileCache = context.read<MapTileCacheService>();
|
||||
final scheme = Theme.of(context).colorScheme;
|
||||
final isDesktop = _isDesktopPlatform(defaultTargetPlatform);
|
||||
final allContacts = connector.allContacts;
|
||||
|
||||
@@ -579,8 +604,8 @@ class _MapScreenState extends State<MapScreen> {
|
||||
canPop: allowBack,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: MapPalette.panelDark,
|
||||
foregroundColor: MapPalette.textPrimary,
|
||||
backgroundColor: scheme.surface,
|
||||
foregroundColor: scheme.onSurface,
|
||||
title: AppBarTitle(context.l10n.map_title),
|
||||
centerTitle: true,
|
||||
automaticallyImplyLeading: false,
|
||||
@@ -753,7 +778,7 @@ class _MapScreenState extends State<MapScreen> {
|
||||
},
|
||||
),
|
||||
children: [
|
||||
ThemedMapTileLayer(tileCache: tileCache),
|
||||
tileCache.buildTileLayer(context),
|
||||
if (_polylines.isNotEmpty && _isBuildingPathTrace)
|
||||
PolylineLayer(polylines: _polylines),
|
||||
if (sharedMarkerPolylines.isNotEmpty)
|
||||
@@ -906,7 +931,7 @@ class _MapScreenState extends State<MapScreen> {
|
||||
_handleQuickSwitch(index, context),
|
||||
contactsUnreadCount: connector.getTotalContactsUnreadCount(),
|
||||
channelsUnreadCount: connector.getTotalChannelsUnreadCount(),
|
||||
highContrast: true,
|
||||
highContrast: _useDarkOverlay,
|
||||
),
|
||||
),
|
||||
floatingActionButton:
|
||||
@@ -1207,14 +1232,14 @@ class _MapScreenState extends State<MapScreen> {
|
||||
height: 36,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: MapPalette.panelDark,
|
||||
color: _overlayPanelColor,
|
||||
border: Border.all(
|
||||
color: guess.highConfidence ? color : MapPalette.textMuted,
|
||||
color: guess.highConfidence ? color : _overlayMutedTextColor,
|
||||
width: guess.highConfidence ? 2.5 : 2,
|
||||
),
|
||||
boxShadow: const [
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: MapPalette.markerShadow,
|
||||
color: _overlayShadowColor,
|
||||
blurRadius: 7,
|
||||
offset: Offset(0, 2),
|
||||
),
|
||||
@@ -1223,7 +1248,7 @@ class _MapScreenState extends State<MapScreen> {
|
||||
alignment: Alignment.center,
|
||||
child: Icon(
|
||||
Icons.not_listed_location,
|
||||
color: MapPalette.textPrimary,
|
||||
color: _overlayPrimaryTextColor,
|
||||
size: 19,
|
||||
),
|
||||
),
|
||||
@@ -1541,12 +1566,12 @@ class _MapScreenState extends State<MapScreen> {
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: MapPalette.panelDark,
|
||||
color: _overlayPanelColor,
|
||||
borderRadius: BorderRadius.circular(MeshRadii.xs),
|
||||
border: Border.all(color: MapPalette.border),
|
||||
boxShadow: const [
|
||||
border: Border.all(color: _overlayBorderColor),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: MapPalette.markerShadow,
|
||||
color: _overlayShadowColor,
|
||||
blurRadius: 4,
|
||||
offset: Offset(0, 1),
|
||||
),
|
||||
@@ -1560,7 +1585,7 @@ class _MapScreenState extends State<MapScreen> {
|
||||
style: MeshTheme.mono(
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: MapPalette.textPrimary,
|
||||
color: _overlayPrimaryTextColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -1681,7 +1706,7 @@ class _MapScreenState extends State<MapScreen> {
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: statusColor,
|
||||
border: Border.all(color: MapPalette.panelDark, width: 2),
|
||||
border: Border.all(color: _overlayPanelColor, width: 2),
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: batteryLow
|
||||
@@ -1704,10 +1729,10 @@ class _MapScreenState extends State<MapScreen> {
|
||||
Expanded(
|
||||
child: Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: MapPalette.textSecondary,
|
||||
color: _overlaySecondaryTextColor,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
@@ -1765,9 +1790,9 @@ class _MapScreenState extends State<MapScreen> {
|
||||
children: [
|
||||
Expanded(
|
||||
child: Material(
|
||||
color: MapPalette.panelDark,
|
||||
color: _overlayPanelColor,
|
||||
shape: StadiumBorder(
|
||||
side: const BorderSide(color: MapPalette.border),
|
||||
side: BorderSide(color: _overlayBorderColor),
|
||||
),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: TextField(
|
||||
@@ -1775,17 +1800,15 @@ class _MapScreenState extends State<MapScreen> {
|
||||
focusNode: _searchFocus,
|
||||
decoration: InputDecoration(
|
||||
hintText: context.l10n.map_searchHint,
|
||||
hintStyle: const TextStyle(
|
||||
color: MapPalette.textSecondary,
|
||||
),
|
||||
prefixIcon: const Icon(
|
||||
hintStyle: TextStyle(color: _overlaySecondaryTextColor),
|
||||
prefixIcon: Icon(
|
||||
Icons.search,
|
||||
size: 20,
|
||||
color: MapPalette.textPrimary,
|
||||
color: _overlayPrimaryTextColor,
|
||||
),
|
||||
suffixIcon: hasQuery
|
||||
? IconButton(
|
||||
color: MapPalette.textPrimary,
|
||||
color: _overlayPrimaryTextColor,
|
||||
icon: const Icon(Icons.close, size: 18),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
@@ -1805,9 +1828,9 @@ class _MapScreenState extends State<MapScreen> {
|
||||
vertical: 12,
|
||||
),
|
||||
),
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: MapPalette.textPrimary,
|
||||
color: _overlayPrimaryTextColor,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
cursorColor: MapPalette.selected,
|
||||
@@ -1819,9 +1842,9 @@ class _MapScreenState extends State<MapScreen> {
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Material(
|
||||
color: MapPalette.panelDark,
|
||||
color: _overlayPanelColor,
|
||||
shape: StadiumBorder(
|
||||
side: const BorderSide(color: MapPalette.border),
|
||||
side: BorderSide(color: _overlayBorderColor),
|
||||
),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: InkWell(
|
||||
@@ -1845,17 +1868,17 @@ class _MapScreenState extends State<MapScreen> {
|
||||
style: MeshTheme.mono(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: MapPalette.textPrimary,
|
||||
color: _overlayPrimaryTextColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 2),
|
||||
AnimatedRotation(
|
||||
turns: _statsExpanded ? 0.5 : 0,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
child: const Icon(
|
||||
child: Icon(
|
||||
Icons.expand_more,
|
||||
size: 16,
|
||||
color: MapPalette.textPrimary,
|
||||
color: _overlayPrimaryTextColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -1954,12 +1977,12 @@ class _MapScreenState extends State<MapScreen> {
|
||||
color: selected
|
||||
? Color.alphaBlend(
|
||||
accent.withValues(alpha: 0.34),
|
||||
MapPalette.panelDark,
|
||||
_overlayPanelColor,
|
||||
)
|
||||
: MapPalette.panelDark,
|
||||
: _overlayPanelColor,
|
||||
shape: StadiumBorder(
|
||||
side: BorderSide(
|
||||
color: selected ? accent : MapPalette.border,
|
||||
color: selected ? accent : _overlayBorderColor,
|
||||
width: selected ? 1.5 : 1,
|
||||
),
|
||||
),
|
||||
@@ -1975,11 +1998,7 @@ class _MapScreenState extends State<MapScreen> {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (selected) ...[
|
||||
const Icon(
|
||||
Icons.check,
|
||||
size: 13,
|
||||
color: MapPalette.textPrimary,
|
||||
),
|
||||
Icon(Icons.check, size: 13, color: _overlayPrimaryTextColor),
|
||||
const SizedBox(width: 4),
|
||||
],
|
||||
Text(
|
||||
@@ -1988,8 +2007,8 @@ class _MapScreenState extends State<MapScreen> {
|
||||
fontSize: 12.5,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: selected
|
||||
? MapPalette.textPrimary
|
||||
: MapPalette.textSecondary,
|
||||
? _overlayPrimaryTextColor
|
||||
: _overlaySecondaryTextColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -2019,14 +2038,14 @@ class _MapScreenState extends State<MapScreen> {
|
||||
margin: const EdgeInsets.only(top: 6),
|
||||
constraints: const BoxConstraints(maxHeight: 300),
|
||||
decoration: BoxDecoration(
|
||||
color: MapPalette.panelDark,
|
||||
color: _overlayPanelColor,
|
||||
borderRadius: BorderRadius.circular(MeshRadii.md),
|
||||
border: Border.all(color: MapPalette.border),
|
||||
boxShadow: const [
|
||||
border: Border.all(color: _overlayBorderColor),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: MapPalette.markerShadow,
|
||||
color: _overlayShadowColor,
|
||||
blurRadius: 10,
|
||||
offset: Offset(0, 4),
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -2035,8 +2054,8 @@ class _MapScreenState extends State<MapScreen> {
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Text(
|
||||
context.l10n.map_noResults,
|
||||
style: const TextStyle(
|
||||
color: MapPalette.textSecondary,
|
||||
style: TextStyle(
|
||||
color: _overlaySecondaryTextColor,
|
||||
fontSize: 13,
|
||||
),
|
||||
),
|
||||
@@ -2046,7 +2065,7 @@ class _MapScreenState extends State<MapScreen> {
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
itemCount: results.length,
|
||||
separatorBuilder: (_, _) =>
|
||||
const Divider(height: 1, color: MapPalette.border),
|
||||
Divider(height: 1, color: _overlayBorderColor),
|
||||
itemBuilder: (context, index) {
|
||||
final c = results[index];
|
||||
final color = _getNodeColor(c.type);
|
||||
@@ -2067,10 +2086,10 @@ class _MapScreenState extends State<MapScreen> {
|
||||
children: [
|
||||
Text(
|
||||
c.name,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 13.5,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: MapPalette.textPrimary,
|
||||
color: _overlayPrimaryTextColor,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
@@ -2078,7 +2097,7 @@ class _MapScreenState extends State<MapScreen> {
|
||||
c.publicKeyHex.substring(0, 12),
|
||||
style: MeshTheme.mono(
|
||||
fontSize: 10.5,
|
||||
color: MapPalette.textSecondary,
|
||||
color: _overlaySecondaryTextColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -2088,13 +2107,13 @@ class _MapScreenState extends State<MapScreen> {
|
||||
Icon(
|
||||
Icons.chevron_right,
|
||||
size: 18,
|
||||
color: MapPalette.textSecondary,
|
||||
color: _overlaySecondaryTextColor,
|
||||
)
|
||||
else
|
||||
Text(
|
||||
context.l10n.map_noGps.toUpperCase(),
|
||||
style: MeshTheme.accentLabel(
|
||||
color: MapPalette.textMuted,
|
||||
color: _overlayMutedTextColor,
|
||||
fontSize: 8.5,
|
||||
),
|
||||
),
|
||||
@@ -2154,14 +2173,14 @@ class _MapScreenState extends State<MapScreen> {
|
||||
width: 230,
|
||||
padding: const EdgeInsets.fromLTRB(14, 12, 14, 12),
|
||||
decoration: BoxDecoration(
|
||||
color: MapPalette.panelDark,
|
||||
color: _overlayPanelColor,
|
||||
borderRadius: BorderRadius.circular(MeshRadii.md),
|
||||
border: Border.all(color: MapPalette.border),
|
||||
boxShadow: const [
|
||||
border: Border.all(color: _overlayBorderColor),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: MapPalette.markerShadow,
|
||||
color: _overlayShadowColor,
|
||||
blurRadius: 10,
|
||||
offset: Offset(0, 4),
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -2178,7 +2197,7 @@ class _MapScreenState extends State<MapScreen> {
|
||||
),
|
||||
_statRow(context.l10n.map_hidden, hiddenCount, MapPalette.offline),
|
||||
_statRow(context.l10n.map_markers, pinCount, MapPalette.shared),
|
||||
const Divider(height: 16, color: MapPalette.border),
|
||||
Divider(height: 16, color: _overlayBorderColor),
|
||||
_buildLegendItem(
|
||||
Icons.person,
|
||||
context.l10n.map_chat,
|
||||
@@ -2208,7 +2227,7 @@ class _MapScreenState extends State<MapScreen> {
|
||||
_buildLegendItem(
|
||||
Icons.not_listed_location,
|
||||
context.l10n.map_guessedLocation,
|
||||
MapPalette.textMuted,
|
||||
_overlayMutedTextColor,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -2229,7 +2248,10 @@ class _MapScreenState extends State<MapScreen> {
|
||||
Expanded(
|
||||
child: Text(
|
||||
label,
|
||||
style: TextStyle(fontSize: 12.5, color: MapPalette.textSecondary),
|
||||
style: TextStyle(
|
||||
fontSize: 12.5,
|
||||
color: _overlaySecondaryTextColor,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
@@ -2238,7 +2260,7 @@ class _MapScreenState extends State<MapScreen> {
|
||||
style: MeshTheme.mono(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: MapPalette.textPrimary,
|
||||
color: _overlayPrimaryTextColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -2271,8 +2293,8 @@ class _MapScreenState extends State<MapScreen> {
|
||||
child: MeshCard(
|
||||
margin: EdgeInsets.zero,
|
||||
padding: const EdgeInsets.fromLTRB(14, 12, 8, 12),
|
||||
color: MapPalette.panelDark,
|
||||
borderColor: MapPalette.border,
|
||||
color: _overlayPanelColor,
|
||||
borderColor: _overlayBorderColor,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -2295,10 +2317,10 @@ class _MapScreenState extends State<MapScreen> {
|
||||
Flexible(
|
||||
child: Text(
|
||||
contact.name,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: MapPalette.textPrimary,
|
||||
color: _overlayPrimaryTextColor,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
@@ -2326,9 +2348,9 @@ class _MapScreenState extends State<MapScreen> {
|
||||
Flexible(
|
||||
child: Text(
|
||||
contact.typeLabel(context.l10n),
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 11.5,
|
||||
color: MapPalette.textSecondary,
|
||||
color: _overlaySecondaryTextColor,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
@@ -2340,13 +2362,13 @@ class _MapScreenState extends State<MapScreen> {
|
||||
),
|
||||
if (pos != null)
|
||||
IconButton(
|
||||
color: MapPalette.textPrimary,
|
||||
color: _overlayPrimaryTextColor,
|
||||
icon: const Icon(Icons.center_focus_strong, size: 20),
|
||||
tooltip: context.l10n.map_centerOnNode,
|
||||
onPressed: () => _mapController.move(pos, max(_zoom, 15)),
|
||||
),
|
||||
IconButton(
|
||||
color: MapPalette.textPrimary,
|
||||
color: _overlayPrimaryTextColor,
|
||||
icon: const Icon(Icons.close, size: 20),
|
||||
onPressed: _clearSelection,
|
||||
),
|
||||
@@ -2407,14 +2429,17 @@ class _MapScreenState extends State<MapScreen> {
|
||||
Text(
|
||||
label.toUpperCase(),
|
||||
style: MeshTheme.accentLabel(
|
||||
color: MapPalette.textMuted,
|
||||
color: _overlayMutedTextColor,
|
||||
fontSize: 8,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 1),
|
||||
Text(
|
||||
value,
|
||||
style: MeshTheme.mono(fontSize: 11.5, color: MapPalette.textPrimary),
|
||||
style: MeshTheme.mono(
|
||||
fontSize: 11.5,
|
||||
color: _overlayPrimaryTextColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
@@ -3555,14 +3580,14 @@ class _MapScreenState extends State<MapScreen> {
|
||||
right: 16,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: MapPalette.panelDark,
|
||||
color: _overlayPanelColor,
|
||||
borderRadius: BorderRadius.circular(MeshRadii.md),
|
||||
border: Border.all(color: MapPalette.border),
|
||||
boxShadow: const [
|
||||
border: Border.all(color: _overlayBorderColor),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: MapPalette.markerShadow,
|
||||
color: _overlayShadowColor,
|
||||
blurRadius: 10,
|
||||
offset: Offset(0, 4),
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -3575,18 +3600,27 @@ class _MapScreenState extends State<MapScreen> {
|
||||
children: [
|
||||
Text(
|
||||
l10n.contacts_pathTrace,
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: _overlayPrimaryTextColor,
|
||||
),
|
||||
),
|
||||
if (_pathTrace.isEmpty) const SizedBox(height: 8),
|
||||
if (_pathTrace.isEmpty)
|
||||
Text(l10n.map_tapToAdd, style: TextStyle(fontSize: 12)),
|
||||
Text(
|
||||
l10n.map_tapToAdd,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: _overlaySecondaryTextColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
if (_pathTrace.isNotEmpty)
|
||||
Text(
|
||||
"${l10n.path_currentPathLabel} ${formatDistance(getPathDistanceMeters(_points), isImperial: isImperial)}",
|
||||
style: MeshTheme.mono(
|
||||
fontSize: 12,
|
||||
color: MapPalette.textSecondary,
|
||||
color: _overlaySecondaryTextColor,
|
||||
),
|
||||
),
|
||||
SelectableText(
|
||||
|
||||
@@ -19,7 +19,6 @@ import 'package:meshcore_open/services/path_history_service.dart';
|
||||
import 'package:meshcore_open/utils/app_logger.dart';
|
||||
import 'package:meshcore_open/widgets/path_map_ui.dart';
|
||||
import 'package:meshcore_open/widgets/snr_indicator.dart';
|
||||
import 'package:meshcore_open/widgets/themed_map_tile_layer.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../theme/mesh_theme.dart';
|
||||
|
||||
@@ -1399,7 +1398,7 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen>
|
||||
},
|
||||
),
|
||||
children: [
|
||||
ThemedMapTileLayer(tileCache: tileCache),
|
||||
tileCache.buildTileLayer(context),
|
||||
AnimatedBuilder(
|
||||
animation: _playback,
|
||||
builder: (context, _) {
|
||||
|
||||
@@ -120,6 +120,25 @@ class AppSettingsService extends ChangeNotifier {
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> setMapRasterSourceId(String value) async {
|
||||
await updateSettings(_settings.copyWith(mapRasterSourceId: value));
|
||||
}
|
||||
|
||||
Future<void> setMapTileEndpointId(String value) async {
|
||||
await updateSettings(_settings.copyWith(mapTileEndpointId: value));
|
||||
}
|
||||
|
||||
Future<void> setMapTileApiKey(String? value) async {
|
||||
final normalized = value?.trim();
|
||||
await updateSettings(
|
||||
_settings.copyWith(
|
||||
mapTileApiKey: (normalized == null || normalized.isEmpty)
|
||||
? null
|
||||
: normalized,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> setNotificationsEnabled(bool value) async {
|
||||
await updateSettings(_settings.copyWith(notificationsEnabled: value));
|
||||
}
|
||||
|
||||
@@ -1,12 +1,232 @@
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_map/flutter_map.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
||||
import 'package:flutter_map/flutter_map.dart';
|
||||
|
||||
const String kMapTileUrlTemplate =
|
||||
'https://tile.openstreetmap.org/{z}/{x}/{y}.png';
|
||||
import '../models/app_settings.dart';
|
||||
import 'app_settings_service.dart';
|
||||
|
||||
enum MapRasterSourcePreset {
|
||||
osmAuto('osm_auto'),
|
||||
osmStandard('osm_standard'),
|
||||
osmDark('osm_dark'),
|
||||
stamenTerrain('stamen_terrain'),
|
||||
alidadeSmoothDark('alidade_smooth_dark'),
|
||||
outdoors('outdoors'),
|
||||
osmBright('osm_bright'),
|
||||
outdoorsDark('outdoors_dark'),
|
||||
osmBrightDark('osm_bright_dark');
|
||||
|
||||
const MapRasterSourcePreset(this.id);
|
||||
|
||||
final String id;
|
||||
|
||||
static MapRasterSourcePreset fromId(String id) {
|
||||
for (final value in values) {
|
||||
if (value.id == id) return value;
|
||||
}
|
||||
return MapRasterSourcePreset.osmAuto;
|
||||
}
|
||||
}
|
||||
|
||||
enum MapRasterEndpointPreset {
|
||||
standard('standard'),
|
||||
standard2x('standard_2x'),
|
||||
eu('eu'),
|
||||
eu2x('eu_2x');
|
||||
|
||||
const MapRasterEndpointPreset(this.id);
|
||||
|
||||
final String id;
|
||||
|
||||
static MapRasterEndpointPreset fromId(String id) {
|
||||
for (final value in values) {
|
||||
if (value.id == id) return value;
|
||||
}
|
||||
return MapRasterEndpointPreset.standard;
|
||||
}
|
||||
}
|
||||
|
||||
@immutable
|
||||
class MapRasterSourceDefinition {
|
||||
const MapRasterSourceDefinition({
|
||||
required this.id,
|
||||
required this.label,
|
||||
required this.description,
|
||||
this.isStadia = false,
|
||||
this.allowsBulkDownload = false,
|
||||
});
|
||||
|
||||
final String id;
|
||||
final String label;
|
||||
final String description;
|
||||
final bool isStadia;
|
||||
final bool allowsBulkDownload;
|
||||
}
|
||||
|
||||
@immutable
|
||||
class MapRasterEndpointDefinition {
|
||||
const MapRasterEndpointDefinition({
|
||||
required this.id,
|
||||
required this.label,
|
||||
required this.description,
|
||||
required this.host,
|
||||
this.scaleSuffix = '',
|
||||
});
|
||||
|
||||
final String id;
|
||||
final String label;
|
||||
final String description;
|
||||
final String host;
|
||||
final String scaleSuffix;
|
||||
}
|
||||
|
||||
class MapRasterSourceCatalog {
|
||||
static const MapRasterSourceDefinition osmAuto = MapRasterSourceDefinition(
|
||||
id: 'osm_auto',
|
||||
label: 'OpenStreetMap Auto',
|
||||
description:
|
||||
'Automatically uses OpenStreetMap Standard or Dark from the app theme',
|
||||
);
|
||||
static const MapRasterSourceDefinition osmStandard =
|
||||
MapRasterSourceDefinition(
|
||||
id: 'osm_standard',
|
||||
label: 'OpenStreetMap Standard',
|
||||
description: 'Direct tiles from tile.openstreetmap.org',
|
||||
);
|
||||
static const MapRasterSourceDefinition osmDark = MapRasterSourceDefinition(
|
||||
id: 'osm_dark',
|
||||
label: 'OpenStreetMap Dark',
|
||||
description: 'Standard OpenStreetMap tiles with an inverted dark filter',
|
||||
);
|
||||
static const MapRasterSourceDefinition stamenTerrain =
|
||||
MapRasterSourceDefinition(
|
||||
id: 'stamen_terrain',
|
||||
label: 'Stamen Terrain',
|
||||
description: 'Terrain-focused style with hill shading',
|
||||
isStadia: true,
|
||||
allowsBulkDownload: true,
|
||||
);
|
||||
static const MapRasterSourceDefinition alidadeSmoothDark =
|
||||
MapRasterSourceDefinition(
|
||||
id: 'alidade_smooth_dark',
|
||||
label: 'Alidade Smooth Dark',
|
||||
description: 'Dark basemap with smooth contrast',
|
||||
isStadia: true,
|
||||
allowsBulkDownload: true,
|
||||
);
|
||||
static const MapRasterSourceDefinition outdoors = MapRasterSourceDefinition(
|
||||
id: 'outdoors',
|
||||
label: 'Outdoors',
|
||||
description: 'Outdoor-focused map with trails and terrain context',
|
||||
isStadia: true,
|
||||
allowsBulkDownload: true,
|
||||
);
|
||||
static const MapRasterSourceDefinition osmBright = MapRasterSourceDefinition(
|
||||
id: 'osm_bright',
|
||||
label: 'OSM Bright',
|
||||
description: 'Bright general-purpose OpenStreetMap style',
|
||||
isStadia: true,
|
||||
allowsBulkDownload: true,
|
||||
);
|
||||
static const MapRasterSourceDefinition outdoorsDark =
|
||||
MapRasterSourceDefinition(
|
||||
id: 'outdoors',
|
||||
label: 'Outdoors Dark',
|
||||
description: 'Dark version of the Outdoors map style',
|
||||
isStadia: true,
|
||||
allowsBulkDownload: true,
|
||||
);
|
||||
static const MapRasterSourceDefinition osmBrightDark =
|
||||
MapRasterSourceDefinition(
|
||||
id: 'osm_bright',
|
||||
label: 'OSM Bright Dark',
|
||||
description: 'Dark version of the OSM Bright map style',
|
||||
isStadia: true,
|
||||
allowsBulkDownload: true,
|
||||
);
|
||||
|
||||
static MapRasterSourceDefinition fromPreset(MapRasterSourcePreset preset) {
|
||||
switch (preset) {
|
||||
case MapRasterSourcePreset.osmAuto:
|
||||
return osmAuto;
|
||||
case MapRasterSourcePreset.osmStandard:
|
||||
return osmStandard;
|
||||
case MapRasterSourcePreset.osmDark:
|
||||
return osmDark;
|
||||
case MapRasterSourcePreset.alidadeSmoothDark:
|
||||
return alidadeSmoothDark;
|
||||
case MapRasterSourcePreset.outdoors:
|
||||
return outdoors;
|
||||
case MapRasterSourcePreset.outdoorsDark:
|
||||
return outdoorsDark;
|
||||
case MapRasterSourcePreset.osmBright:
|
||||
return osmBright;
|
||||
case MapRasterSourcePreset.osmBrightDark:
|
||||
return osmBrightDark;
|
||||
case MapRasterSourcePreset.stamenTerrain:
|
||||
return stamenTerrain;
|
||||
}
|
||||
}
|
||||
|
||||
static MapRasterSourceDefinition fromSettings(AppSettings settings) {
|
||||
return fromPreset(MapRasterSourcePreset.fromId(settings.mapRasterSourceId));
|
||||
}
|
||||
}
|
||||
|
||||
class MapRasterEndpointCatalog {
|
||||
static const MapRasterEndpointDefinition standard =
|
||||
MapRasterEndpointDefinition(
|
||||
id: 'standard',
|
||||
label: 'Standard Endpoint',
|
||||
description: 'Global CDN routing to the fastest Stadia server',
|
||||
host: 'tiles.stadiamaps.com',
|
||||
);
|
||||
static const MapRasterEndpointDefinition standard2x =
|
||||
MapRasterEndpointDefinition(
|
||||
id: 'standard_2x',
|
||||
label: 'Standard Endpoint (@2x)',
|
||||
description: 'Global Stadia endpoint with HiDPI raster tiles',
|
||||
host: 'tiles.stadiamaps.com',
|
||||
scaleSuffix: '@2x',
|
||||
);
|
||||
static const MapRasterEndpointDefinition eu = MapRasterEndpointDefinition(
|
||||
id: 'eu',
|
||||
label: 'EU Endpoint',
|
||||
description: 'Route tile requests to Stadia EU servers',
|
||||
host: 'tiles-eu.stadiamaps.com',
|
||||
);
|
||||
static const MapRasterEndpointDefinition eu2x = MapRasterEndpointDefinition(
|
||||
id: 'eu_2x',
|
||||
label: 'EU Endpoint (@2x)',
|
||||
description: 'EU Stadia endpoint with HiDPI raster tiles',
|
||||
host: 'tiles-eu.stadiamaps.com',
|
||||
scaleSuffix: '@2x',
|
||||
);
|
||||
|
||||
static const List<MapRasterEndpointDefinition> presets = [
|
||||
standard,
|
||||
standard2x,
|
||||
eu,
|
||||
eu2x,
|
||||
];
|
||||
|
||||
static MapRasterEndpointDefinition fromSettings(AppSettings settings) {
|
||||
final preset = MapRasterEndpointPreset.fromId(settings.mapTileEndpointId);
|
||||
switch (preset) {
|
||||
case MapRasterEndpointPreset.standard2x:
|
||||
return standard2x;
|
||||
case MapRasterEndpointPreset.eu:
|
||||
return eu;
|
||||
case MapRasterEndpointPreset.eu2x:
|
||||
return eu2x;
|
||||
case MapRasterEndpointPreset.standard:
|
||||
return standard;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MapTileCacheProgress {
|
||||
final int completed;
|
||||
@@ -32,36 +252,253 @@ class MapTileCacheResult {
|
||||
});
|
||||
}
|
||||
|
||||
class MapTileCacheService {
|
||||
class CachedTileInfo {
|
||||
final String key;
|
||||
final String host;
|
||||
final String sourceId;
|
||||
final int zoom;
|
||||
final int x;
|
||||
final int y;
|
||||
final int length;
|
||||
|
||||
const CachedTileInfo({
|
||||
required this.key,
|
||||
required this.host,
|
||||
required this.sourceId,
|
||||
required this.zoom,
|
||||
required this.x,
|
||||
required this.y,
|
||||
required this.length,
|
||||
});
|
||||
}
|
||||
|
||||
class CachedTileInventory {
|
||||
final List<CachedTileInfo> tiles;
|
||||
final int totalBytes;
|
||||
|
||||
const CachedTileInventory({required this.tiles, required this.totalBytes});
|
||||
}
|
||||
|
||||
class MapTileCacheService extends ChangeNotifier {
|
||||
static const String cacheKey = 'map_tile_cache';
|
||||
static const String userAgentPackageName = 'com.meshcore.open';
|
||||
static const int defaultMinZoom = 10;
|
||||
static const int defaultMaxZoom = 15;
|
||||
|
||||
final AppSettingsService appSettingsService;
|
||||
final BaseCacheManager cacheManager;
|
||||
late final TileProvider tileProvider;
|
||||
|
||||
MapTileCacheService({BaseCacheManager? cacheManager})
|
||||
: cacheManager =
|
||||
cacheManager ??
|
||||
CacheManager(
|
||||
Config(
|
||||
cacheKey,
|
||||
stalePeriod: const Duration(days: 365),
|
||||
maxNrOfCacheObjects: 200000,
|
||||
),
|
||||
) {
|
||||
MapTileCacheService({
|
||||
required this.appSettingsService,
|
||||
BaseCacheManager? cacheManager,
|
||||
}) : cacheManager =
|
||||
cacheManager ??
|
||||
CacheManager(
|
||||
Config(
|
||||
cacheKey,
|
||||
stalePeriod: const Duration(days: 365),
|
||||
maxNrOfCacheObjects: 200000,
|
||||
),
|
||||
) {
|
||||
tileProvider = CachedNetworkTileProvider(cacheManager: this.cacheManager);
|
||||
appSettingsService.addListener(_handleSettingsChanged);
|
||||
}
|
||||
|
||||
MapRasterSourceDefinition get source {
|
||||
final selectedSource = MapRasterSourceCatalog.fromSettings(
|
||||
appSettingsService.settings,
|
||||
);
|
||||
if (!selectedSource.allowsBulkDownload ||
|
||||
!appSettingsService.settings.usesstadiaDemo) {
|
||||
return selectedSource;
|
||||
}
|
||||
return MapRasterSourceDefinition(
|
||||
id: selectedSource.id,
|
||||
label: selectedSource.label,
|
||||
description: selectedSource.description,
|
||||
isStadia: selectedSource.isStadia,
|
||||
allowsBulkDownload: false, // Explicitly disable bulk download for demo.
|
||||
);
|
||||
}
|
||||
|
||||
MapRasterEndpointDefinition get endpoint =>
|
||||
MapRasterEndpointCatalog.fromSettings(appSettingsService.settings);
|
||||
|
||||
String get urlTemplate => _buildUrlTemplate(appSettingsService.settings);
|
||||
|
||||
TileBuilder? get tileBuilder => null;
|
||||
|
||||
static bool shouldApplyDarkFilterForSettings(
|
||||
AppSettings settings,
|
||||
Brightness brightness,
|
||||
) {
|
||||
switch (MapRasterSourcePreset.fromId(settings.mapRasterSourceId)) {
|
||||
case MapRasterSourcePreset.osmDark:
|
||||
case MapRasterSourcePreset.outdoorsDark:
|
||||
case MapRasterSourcePreset.osmBrightDark:
|
||||
return true;
|
||||
case MapRasterSourcePreset.osmAuto:
|
||||
return brightness == Brightness.dark;
|
||||
case MapRasterSourcePreset.osmStandard:
|
||||
case MapRasterSourcePreset.stamenTerrain:
|
||||
case MapRasterSourcePreset.alidadeSmoothDark:
|
||||
case MapRasterSourcePreset.outdoors:
|
||||
case MapRasterSourcePreset.osmBright:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static const ColorFilter _darkMapFilter = ColorFilter.matrix([
|
||||
-0.0850,
|
||||
-0.2861,
|
||||
-0.0289,
|
||||
0,
|
||||
120,
|
||||
-0.0957,
|
||||
-0.3218,
|
||||
-0.0325,
|
||||
0,
|
||||
140,
|
||||
-0.1169,
|
||||
-0.3934,
|
||||
-0.0397,
|
||||
0,
|
||||
170,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
]);
|
||||
|
||||
CacheManager get _concreteCacheManager => cacheManager as CacheManager;
|
||||
|
||||
Map<String, String> get defaultHeaders => {
|
||||
'User-Agent': 'flutter_map ($userAgentPackageName)',
|
||||
};
|
||||
|
||||
Widget buildTileLayer(BuildContext context, {double opacity = 1}) {
|
||||
Widget layer = TileLayer(
|
||||
urlTemplate: urlTemplate,
|
||||
tileProvider: tileProvider,
|
||||
tileBuilder: tileBuilder,
|
||||
userAgentPackageName: userAgentPackageName,
|
||||
maxZoom: 19,
|
||||
);
|
||||
|
||||
final shouldApplyDarkFilter = shouldApplyDarkFilterForSettings(
|
||||
appSettingsService.settings,
|
||||
Theme.of(context).brightness,
|
||||
);
|
||||
|
||||
if (shouldApplyDarkFilter) {
|
||||
layer = ColorFiltered(colorFilter: _darkMapFilter, child: layer);
|
||||
}
|
||||
|
||||
if (opacity < 1) {
|
||||
layer = Opacity(opacity: opacity, child: layer);
|
||||
}
|
||||
|
||||
return layer;
|
||||
}
|
||||
|
||||
Future<void> clearCache() async {
|
||||
await cacheManager.emptyCache();
|
||||
}
|
||||
|
||||
Future<CachedTileInventory> getCachedTileInventory() async {
|
||||
final repo = _concreteCacheManager.config.repo;
|
||||
await repo.open();
|
||||
final objects = await repo.getAllObjects();
|
||||
final tiles = <CachedTileInfo>[];
|
||||
int totalBytes = 0;
|
||||
|
||||
for (final object in objects) {
|
||||
totalBytes += object.length ?? 0;
|
||||
final tile = _parseCachedTile(object);
|
||||
if (tile != null) {
|
||||
tiles.add(tile);
|
||||
}
|
||||
}
|
||||
|
||||
return CachedTileInventory(tiles: tiles, totalBytes: totalBytes);
|
||||
}
|
||||
|
||||
List<CachedTileInfo> filterTilesForActiveSource(
|
||||
Iterable<CachedTileInfo> tiles,
|
||||
) {
|
||||
final activeSource = source;
|
||||
if (!activeSource.isStadia) {
|
||||
return tiles
|
||||
.where(
|
||||
(tile) =>
|
||||
tile.sourceId == MapRasterSourceCatalog.osmStandard.id &&
|
||||
tile.host == 'tile.openstreetmap.org',
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
|
||||
final activeEndpoint = endpoint;
|
||||
return tiles
|
||||
.where(
|
||||
(tile) =>
|
||||
tile.sourceId == activeSource.id &&
|
||||
tile.host == activeEndpoint.host,
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
|
||||
int countTilesForBounds(
|
||||
Iterable<CachedTileInfo> tiles, {
|
||||
LatLngBounds? bounds,
|
||||
required int minZoom,
|
||||
required int maxZoom,
|
||||
}) {
|
||||
if (bounds == null) return 0;
|
||||
final safeMin = math.min(minZoom, maxZoom);
|
||||
final safeMax = math.max(minZoom, maxZoom);
|
||||
return tiles.where((tile) {
|
||||
if (tile.zoom < safeMin || tile.zoom > safeMax) {
|
||||
return false;
|
||||
}
|
||||
final tileBounds = _tileBoundsForTile(tile.x, tile.y, tile.zoom);
|
||||
return _boundsIntersect(bounds, tileBounds);
|
||||
}).length;
|
||||
}
|
||||
|
||||
List<Polygon> buildCachedTilePolygons(
|
||||
Iterable<CachedTileInfo> tiles, {
|
||||
required int zoom,
|
||||
LatLngBounds? visibleBounds,
|
||||
int limit = 250,
|
||||
}) {
|
||||
final polygons = <Polygon>[];
|
||||
for (final tile in tiles) {
|
||||
if (tile.zoom != zoom) continue;
|
||||
final tileBounds = _tileBoundsForTile(tile.x, tile.y, tile.zoom);
|
||||
if (visibleBounds != null &&
|
||||
!_boundsIntersect(visibleBounds, tileBounds)) {
|
||||
continue;
|
||||
}
|
||||
polygons.add(
|
||||
Polygon(
|
||||
points: [
|
||||
tileBounds.northWest,
|
||||
tileBounds.northEast,
|
||||
tileBounds.southEast,
|
||||
tileBounds.southWest,
|
||||
],
|
||||
borderStrokeWidth: 0.6,
|
||||
color: const Color(0x5532A852),
|
||||
borderColor: const Color(0xCC2F8F46),
|
||||
),
|
||||
);
|
||||
if (polygons.length >= limit) break;
|
||||
}
|
||||
return polygons;
|
||||
}
|
||||
|
||||
int estimateTileCount(LatLngBounds bounds, int minZoom, int maxZoom) {
|
||||
final safeMin = math.min(minZoom, maxZoom);
|
||||
final safeMax = math.max(minZoom, maxZoom);
|
||||
@@ -89,6 +526,7 @@ class MapTileCacheService {
|
||||
final total = estimateTileCount(bounds, safeMin, safeMax);
|
||||
final authHeaders = headers ?? defaultHeaders;
|
||||
final safeConcurrency = math.max(1, concurrentDownloads);
|
||||
final currentTemplate = urlTemplate;
|
||||
int completed = 0;
|
||||
int failed = 0;
|
||||
|
||||
@@ -124,7 +562,7 @@ class MapTileCacheService {
|
||||
final tileBounds = _tileBoundsForBounds(bounds, zoom);
|
||||
for (int x = tileBounds.minX; x <= tileBounds.maxX; x++) {
|
||||
for (int y = tileBounds.minY; y <= tileBounds.maxY; y++) {
|
||||
final url = _buildTileUrl(x, y, zoom);
|
||||
final url = _buildTileUrl(x, y, zoom, urlTemplate: currentTemplate);
|
||||
await queueDownload(url);
|
||||
}
|
||||
}
|
||||
@@ -167,6 +605,16 @@ class MapTileCacheService {
|
||||
);
|
||||
}
|
||||
|
||||
void _handleSettingsChanged() {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
appSettingsService.removeListener(_handleSettingsChanged);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
_TileBounds _tileBoundsForBounds(LatLngBounds bounds, int zoom) {
|
||||
final north = _clampLatitude(bounds.north);
|
||||
final south = _clampLatitude(bounds.south);
|
||||
@@ -185,6 +633,56 @@ class MapTileCacheService {
|
||||
);
|
||||
}
|
||||
|
||||
String _buildUrlTemplate(AppSettings settings) {
|
||||
final source = MapRasterSourceCatalog.fromSettings(settings);
|
||||
if (!source.isStadia) {
|
||||
return 'https://tile.openstreetmap.org/{z}/{x}/{y}.png';
|
||||
}
|
||||
final endpoint = MapRasterEndpointCatalog.fromSettings(settings);
|
||||
final apiKey = settings.effectiveMapTileApiKey;
|
||||
final base =
|
||||
'https://${endpoint.host}/tiles/${source.id}/{z}/{x}/{y}${endpoint.scaleSuffix}.png';
|
||||
final query = Uri(queryParameters: {'api_key': apiKey}).query;
|
||||
return '$base?$query';
|
||||
}
|
||||
|
||||
CachedTileInfo? _parseCachedTile(CacheObject object) {
|
||||
final uri = Uri.tryParse(object.key);
|
||||
if (uri == null) return null;
|
||||
final segments = uri.pathSegments;
|
||||
|
||||
if (segments.length >= 3 &&
|
||||
segments[segments.length - 3].isNotEmpty &&
|
||||
segments[segments.length - 2].isNotEmpty) {
|
||||
final zoom = int.tryParse(segments[segments.length - 3]);
|
||||
final x = int.tryParse(segments[segments.length - 2]);
|
||||
final ySegment = segments.last;
|
||||
final yString = ySegment.split('.').first.replaceAll('@2x', '');
|
||||
final y = int.tryParse(yString);
|
||||
|
||||
if (zoom == null || x == null || y == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var sourceId = MapRasterSourceCatalog.osmStandard.id;
|
||||
if (segments.length >= 5 && segments[0] == 'tiles') {
|
||||
sourceId = segments[1];
|
||||
}
|
||||
|
||||
return CachedTileInfo(
|
||||
key: object.key,
|
||||
host: uri.host,
|
||||
sourceId: sourceId,
|
||||
zoom: zoom,
|
||||
x: x,
|
||||
y: y,
|
||||
length: object.length ?? 0,
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
int _lonToTileX(double lon, int zoom, int maxIndex) {
|
||||
final n = 1 << zoom;
|
||||
final value = ((lon + 180.0) / 360.0 * n).floor();
|
||||
@@ -205,8 +703,34 @@ class MapTileCacheService {
|
||||
return lat.clamp(-maxLat, maxLat);
|
||||
}
|
||||
|
||||
String _buildTileUrl(int x, int y, int zoom) {
|
||||
return kMapTileUrlTemplate
|
||||
LatLngBounds _tileBoundsForTile(int x, int y, int zoom) {
|
||||
return LatLngBounds.unsafe(
|
||||
north: _tileYToLat(y, zoom),
|
||||
south: _tileYToLat(y + 1, zoom),
|
||||
east: _tileXToLon(x + 1, zoom),
|
||||
west: _tileXToLon(x, zoom),
|
||||
);
|
||||
}
|
||||
|
||||
double _tileXToLon(int x, int zoom) {
|
||||
final n = 1 << zoom;
|
||||
return x / n * 360.0 - 180.0;
|
||||
}
|
||||
|
||||
double _tileYToLat(int y, int zoom) {
|
||||
final n = math.pi - (2.0 * math.pi * y) / (1 << zoom);
|
||||
return 180.0 / math.pi * math.atan(0.5 * (math.exp(n) - math.exp(-n)));
|
||||
}
|
||||
|
||||
bool _boundsIntersect(LatLngBounds a, LatLngBounds b) {
|
||||
return a.west <= b.east &&
|
||||
a.east >= b.west &&
|
||||
a.south <= b.north &&
|
||||
a.north >= b.south;
|
||||
}
|
||||
|
||||
String _buildTileUrl(int x, int y, int zoom, {required String urlTemplate}) {
|
||||
return urlTemplate
|
||||
.replaceAll('{z}', zoom.toString())
|
||||
.replaceAll('{x}', x.toString())
|
||||
.replaceAll('{y}', y.toString());
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'package:flutter/cupertino.dart' show CupertinoPageTransitionsBuilder;
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// MeshCore palette — high-contrast slate surfaces with sky-blue accents.
|
||||
@@ -448,7 +447,7 @@ class MeshTheme {
|
||||
pageTransitionsTheme: const PageTransitionsTheme(
|
||||
builders: {
|
||||
TargetPlatform.android: FadeForwardsPageTransitionsBuilder(),
|
||||
TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
|
||||
TargetPlatform.iOS: FadeForwardsPageTransitionsBuilder(),
|
||||
TargetPlatform.linux: FadeForwardsPageTransitionsBuilder(),
|
||||
TargetPlatform.macOS: FadeForwardsPageTransitionsBuilder(),
|
||||
TargetPlatform.windows: FadeForwardsPageTransitionsBuilder(),
|
||||
|
||||
@@ -11,7 +11,6 @@ import '../models/app_settings.dart';
|
||||
import '../models/contact.dart';
|
||||
import '../services/app_settings_service.dart';
|
||||
import '../services/map_tile_cache_service.dart';
|
||||
import 'themed_map_tile_layer.dart';
|
||||
|
||||
class TelemetryLocationMap extends StatefulWidget {
|
||||
final double latitude;
|
||||
@@ -115,7 +114,7 @@ class _TelemetryLocationMapState extends State<TelemetryLocationMap> {
|
||||
),
|
||||
),
|
||||
children: [
|
||||
ThemedMapTileLayer(tileCache: tileCache),
|
||||
tileCache.buildTileLayer(context),
|
||||
MarkerLayer(
|
||||
markers: [
|
||||
...contacts.map(_buildContactMarker),
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_map/flutter_map.dart';
|
||||
|
||||
import '../services/map_tile_cache_service.dart';
|
||||
|
||||
/// Shared cached map tiles with an automatic dark-mode treatment.
|
||||
///
|
||||
/// The dark style transforms the existing OpenStreetMap raster tiles, so light
|
||||
/// and dark maps share the same offline cache and network requests.
|
||||
class ThemedMapTileLayer extends StatelessWidget {
|
||||
final MapTileCacheService tileCache;
|
||||
final double opacity;
|
||||
|
||||
const ThemedMapTileLayer({
|
||||
super.key,
|
||||
required this.tileCache,
|
||||
this.opacity = 1,
|
||||
});
|
||||
|
||||
static const ColorFilter _darkMapFilter = ColorFilter.matrix([
|
||||
-0.0850,
|
||||
-0.2861,
|
||||
-0.0289,
|
||||
0,
|
||||
120,
|
||||
-0.0957,
|
||||
-0.3218,
|
||||
-0.0325,
|
||||
0,
|
||||
140,
|
||||
-0.1169,
|
||||
-0.3934,
|
||||
-0.0397,
|
||||
0,
|
||||
170,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
]);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget layer = TileLayer(
|
||||
urlTemplate: kMapTileUrlTemplate,
|
||||
tileProvider: tileCache.tileProvider,
|
||||
userAgentPackageName: MapTileCacheService.userAgentPackageName,
|
||||
maxZoom: 19,
|
||||
);
|
||||
|
||||
if (Theme.of(context).brightness == Brightness.dark) {
|
||||
layer = ColorFiltered(colorFilter: _darkMapFilter, child: layer);
|
||||
}
|
||||
if (opacity < 1) {
|
||||
layer = Opacity(opacity: opacity, child: layer);
|
||||
}
|
||||
return layer;
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
flserial
|
||||
jni
|
||||
)
|
||||
|
||||
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||
|
||||
@@ -11,6 +11,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
flserial
|
||||
flutter_local_notifications_windows
|
||||
jni
|
||||
)
|
||||
|
||||
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||
|
||||
Reference in New Issue
Block a user