resolved conflict and update doc
@@ -0,0 +1,3 @@
|
|||||||
|
github: [zjs81]
|
||||||
|
custom:
|
||||||
|
- "https://paypal.me/zjs82"
|
||||||
@@ -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
|
- **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
|
- **Node Filtering**: Filter by node type (chat, repeater, sensor) and time range
|
||||||
- **Location Sharing**: Share GPS coordinates and custom markers with contacts
|
- **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
|
- **MGRS Coordinates**: Support for Military Grid Reference System coordinate format
|
||||||
|
|
||||||
### Device Management
|
### Device Management
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
<uses-feature android:name="android.hardware.usb.host" android:required="false"/>
|
<uses-feature android:name="android.hardware.usb.host" android:required="false"/>
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:label="meshcore_open"
|
android:label="MeshCore Open"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
android:icon="@mipmap/ic_launcher">
|
android:icon="@mipmap/ic_launcher">
|
||||||
<service
|
<service
|
||||||
|
|||||||
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 108 KiB |
|
After Width: | Height: | Size: 183 KiB |
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@color/ic_launcher_background"/>
|
||||||
|
<foreground>
|
||||||
|
<inset
|
||||||
|
android:drawable="@drawable/ic_launcher_foreground"
|
||||||
|
android:inset="16%" />
|
||||||
|
</foreground>
|
||||||
|
</adaptive-icon>
|
||||||
|
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 6.1 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 18 KiB |
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="ic_launcher_background">#9F9D95</color>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
Copyright 2020 The Inter Project Authors (https://github.com/rsms/inter)
|
||||||
|
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at:
|
||||||
|
https://scripts.sil.org/OFL
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font creation
|
||||||
|
efforts of academic and linguistic communities, and to provide a free and
|
||||||
|
open framework in which fonts may be shared and improved in partnership
|
||||||
|
with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply
|
||||||
|
to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as
|
||||||
|
distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||||
|
or substituting -- in part or in whole -- any of the components of the
|
||||||
|
Original Version, by changing formats or by porting the Font Software to a
|
||||||
|
new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||||
|
redistribute, and sell modified and unmodified copies of the Font
|
||||||
|
Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components,
|
||||||
|
in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the corresponding
|
||||||
|
Copyright Holder. This restriction only applies to the primary font name as
|
||||||
|
presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created
|
||||||
|
using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
Copyright 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono)
|
||||||
|
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
|
||||||
|
This license is copied below, and is also available with a FAQ at: https://scripts.sil.org/OFL
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font creation
|
||||||
|
efforts of academic and linguistic communities, and to provide a free and
|
||||||
|
open framework in which fonts may be shared and improved in partnership
|
||||||
|
with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply
|
||||||
|
to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as
|
||||||
|
distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||||
|
or substituting -- in part or in whole -- any of the components of the
|
||||||
|
Original Version, by changing formats or by porting the Font Software to a
|
||||||
|
new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||||
|
redistribute, and sell modified and unmodified copies of the Font
|
||||||
|
Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components,
|
||||||
|
in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the corresponding
|
||||||
|
Copyright Holder. This restriction only applies to the primary font name as
|
||||||
|
presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created
|
||||||
|
using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||
|
After Width: | Height: | Size: 669 KiB |
@@ -36,11 +36,11 @@ The connector reads a single-mode byte and clamps to `0..3`, so the supported ho
|
|||||||
|
|
||||||
Paths in messages and storage consist of:
|
Paths in messages and storage consist of:
|
||||||
|
|
||||||
- **`pathLength`**: Encoded path length in bytes as carried by the frame/header. This is not the hop count when `pathHashByteWidth > 1`.
|
- **`pathLength` (model/storage)**: Hop count (number of hops). Negative values (e.g. `-1`) are used as a flood sentinel.
|
||||||
- Note: on-air headers carry `pathLength` as a byte count. However, in the app's data model and storage the `pathLength` field is stored as the decoded hop count (number of hops). When reading or writing model objects (contacts, messages, storage), treat `pathLength` as the hop count; when constructing frames use the encoded byte length.
|
- **On-air `path_len` byte**: A packed byte that encodes hop count + hash width and is decoded into `pathLength` + `pathBytes` when parsing frames.
|
||||||
- **`pathBytes`**: Raw bytes of the path, grouped by `pathHashByteWidth`
|
- **`pathBytes`**: Raw bytes of the path (concatenated hop prefixes), grouped by `pathHashByteWidth`.
|
||||||
- **`hopCount`**: Derived display value computed from bytes and width: `(byteCount + hashByteWidth - 1) ~/ hashByteWidth`
|
- **`hopCount`**: Derived display value computed from bytes and width: `(byteCount + hashByteWidth - 1) ~/ hashByteWidth`.
|
||||||
- **Example**: With `pathHashByteWidth=2`, a 3-hop path needs 6 bytes, so `pathLength = 6` and `hopCount = 3`:
|
- **Example**: With `pathHashByteWidth=2`, a 3-hop path has 6 bytes (`pathBytes.length = 6`) and `pathLength = 3`:
|
||||||
- `pathBytes = [0xA1, 0xA2, 0xB1, 0xB2, 0xC1, 0xC2]`
|
- `pathBytes = [0xA1, 0xA2, 0xB1, 0xB2, 0xC1, 0xC2]`
|
||||||
- Hops: `[0xA1A2]`, `[0xB1B2]`, `[0xC1C2]`
|
- Hops: `[0xA1A2]`, `[0xB1B2]`, `[0xC1C2]`
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ These settings are sent directly to the connected device firmware.
|
|||||||
|
|
||||||
### Radio Settings
|
### Radio Settings
|
||||||
Opens a dialog pre-populated with the device's current radio settings. Contains:
|
Opens a dialog pre-populated with the device's current radio settings. Contains:
|
||||||
- **Preset dropdown**: Regional presets — selecting a preset immediately fills all fields below. Includes presets for Australia, Australia (Narrow), Australia SA WA QLD, Czech Republic, EU 433MHz, EU/UK (Long Range), EU/UK (Medium Range), EU/UK (Narrow), New Zealand, New Zealand (Narrow), Portugal 433, Portugal 869, numerous Russia city presets, Switzerland, USA Arizona, USA/Canada, and Vietnam
|
- **Preset dropdown**: Regional presets — selecting a preset immediately fills all fields below. Includes presets for Australia, Australia (Narrow), Australia (Mid), Australia SA WA QLD, Czech Republic, EU 433MHz, EU/UK (Long Range), EU/UK (Medium Range), EU/UK (Narrow), New Zealand, New Zealand (Narrow), Portugal 433, Portugal 869, numerous Russia city presets, Switzerland, USA Arizona, USA/Canada, and Vietnam
|
||||||
- **Frequency** (MHz): Free text, validated 300–2500 MHz
|
- **Frequency** (MHz): Free text, validated 300–2500 MHz
|
||||||
- **Bandwidth**: Dropdown (7.8 / 10.4 / 15.6 / 20.8 / 31.25 / 41.7 / 62.5 / 125 / 250 / 500 kHz)
|
- **Bandwidth**: Dropdown (7.8 / 10.4 / 15.6 / 20.8 / 31.25 / 41.7 / 62.5 / 125 / 250 / 500 kHz)
|
||||||
- **Spreading Factor**: SF5–SF12
|
- **Spreading Factor**: SF5–SF12
|
||||||
@@ -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
|
- **Show Other Nodes**: Toggle room/sensor markers
|
||||||
- **Time Filter**: All time / Last 1h / Last 6h / Last 24h / Last week
|
- **Time Filter**: All time / Last 1h / Last 6h / Last 24h / Last week
|
||||||
- **Units**: Metric / Imperial
|
- **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
|
- **Offline Map Cache**: Navigate to tile download screen
|
||||||
|
|
||||||
### Translation
|
### Translation
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>method</key>
|
||||||
|
<string>app-store-connect</string>
|
||||||
|
<key>teamID</key>
|
||||||
|
<string>X74VJ4UAST</string>
|
||||||
|
<key>signingStyle</key>
|
||||||
|
<string>automatic</string>
|
||||||
|
<key>uploadSymbols</key>
|
||||||
|
<true/>
|
||||||
|
<key>destination</key>
|
||||||
|
<string>export</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -1,73 +1,21 @@
|
|||||||
PODS:
|
PODS:
|
||||||
- Flutter (1.0.0)
|
- Flutter (1.0.0)
|
||||||
- flutter_blue_plus_darwin (0.0.2):
|
|
||||||
- Flutter
|
|
||||||
- FlutterMacOS
|
|
||||||
- flutter_foreground_task (0.0.1):
|
- flutter_foreground_task (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- flutter_local_notifications (0.0.1):
|
|
||||||
- Flutter
|
|
||||||
- mobile_scanner (7.0.0):
|
|
||||||
- Flutter
|
|
||||||
- FlutterMacOS
|
|
||||||
- package_info_plus (0.4.5):
|
|
||||||
- Flutter
|
|
||||||
- share_plus (0.0.1):
|
|
||||||
- Flutter
|
|
||||||
- shared_preferences_foundation (0.0.1):
|
|
||||||
- Flutter
|
|
||||||
- FlutterMacOS
|
|
||||||
- sqflite_darwin (0.0.4):
|
|
||||||
- Flutter
|
|
||||||
- FlutterMacOS
|
|
||||||
- url_launcher_ios (0.0.1):
|
|
||||||
- Flutter
|
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- Flutter (from `Flutter`)
|
- Flutter (from `Flutter`)
|
||||||
- flutter_blue_plus_darwin (from `.symlinks/plugins/flutter_blue_plus_darwin/darwin`)
|
|
||||||
- flutter_foreground_task (from `.symlinks/plugins/flutter_foreground_task/ios`)
|
- flutter_foreground_task (from `.symlinks/plugins/flutter_foreground_task/ios`)
|
||||||
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
|
|
||||||
- mobile_scanner (from `.symlinks/plugins/mobile_scanner/darwin`)
|
|
||||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
|
||||||
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
|
||||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
|
||||||
- sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`)
|
|
||||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
|
||||||
|
|
||||||
EXTERNAL SOURCES:
|
EXTERNAL SOURCES:
|
||||||
Flutter:
|
Flutter:
|
||||||
:path: Flutter
|
:path: Flutter
|
||||||
flutter_blue_plus_darwin:
|
|
||||||
:path: ".symlinks/plugins/flutter_blue_plus_darwin/darwin"
|
|
||||||
flutter_foreground_task:
|
flutter_foreground_task:
|
||||||
:path: ".symlinks/plugins/flutter_foreground_task/ios"
|
:path: ".symlinks/plugins/flutter_foreground_task/ios"
|
||||||
flutter_local_notifications:
|
|
||||||
:path: ".symlinks/plugins/flutter_local_notifications/ios"
|
|
||||||
mobile_scanner:
|
|
||||||
:path: ".symlinks/plugins/mobile_scanner/darwin"
|
|
||||||
package_info_plus:
|
|
||||||
:path: ".symlinks/plugins/package_info_plus/ios"
|
|
||||||
share_plus:
|
|
||||||
:path: ".symlinks/plugins/share_plus/ios"
|
|
||||||
shared_preferences_foundation:
|
|
||||||
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
|
|
||||||
sqflite_darwin:
|
|
||||||
:path: ".symlinks/plugins/sqflite_darwin/darwin"
|
|
||||||
url_launcher_ios:
|
|
||||||
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
||||||
flutter_blue_plus_darwin: 20a08bfeaa0f7804d524858d3d8744bcc1b6dbc3
|
|
||||||
flutter_foreground_task: a159d2c2173b33699ddb3e6c2a067045d7cebb89
|
flutter_foreground_task: a159d2c2173b33699ddb3e6c2a067045d7cebb89
|
||||||
flutter_local_notifications: a5a732f069baa862e728d839dd2ebb904737effb
|
|
||||||
mobile_scanner: 9157936403f5a0644ca3779a38ff8404c5434a93
|
|
||||||
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
|
|
||||||
share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
|
|
||||||
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
|
|
||||||
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
|
|
||||||
url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b
|
|
||||||
|
|
||||||
PODFILE CHECKSUM: e42b502c78c33aa1ed9d42eaea8960ce2139504b
|
PODFILE CHECKSUM: e42b502c78c33aa1ed9d42eaea8960ce2139504b
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
|
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
|
||||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
||||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
||||||
|
78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; };
|
||||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
||||||
@@ -52,6 +53,7 @@
|
|||||||
718BC7DCCFC5C370705C12E5 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
718BC7DCCFC5C370705C12E5 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
||||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
|
78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = "<group>"; };
|
||||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
||||||
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
||||||
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
|
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
|
||||||
@@ -67,6 +69,7 @@
|
|||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */,
|
||||||
9A698254711B63C3940A64CB /* libPods-Runner.a in Frameworks */,
|
9A698254711B63C3940A64CB /* libPods-Runner.a in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@@ -85,6 +88,7 @@
|
|||||||
9740EEB11CF90186004384FC /* Flutter */ = {
|
9740EEB11CF90186004384FC /* Flutter */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */,
|
||||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
|
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
|
||||||
9740EEB21CF90195004384FC /* Debug.xcconfig */,
|
9740EEB21CF90195004384FC /* Debug.xcconfig */,
|
||||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
|
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
|
||||||
@@ -180,13 +184,15 @@
|
|||||||
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
||||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||||
F0D7F2413C6E4B7A9B1C2D3E /* Fix Native Asset Minimum OS */,
|
F0D7F2413C6E4B7A9B1C2D3E /* Fix Native Asset Minimum OS */,
|
||||||
B788CEDB957A87EE8AC593BB /* [CP] Copy Pods Resources */,
|
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
dependencies = (
|
dependencies = (
|
||||||
);
|
);
|
||||||
name = Runner;
|
name = Runner;
|
||||||
|
packageProductDependencies = (
|
||||||
|
78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */,
|
||||||
|
);
|
||||||
productName = Runner;
|
productName = Runner;
|
||||||
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
|
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
|
||||||
productType = "com.apple.product-type.application";
|
productType = "com.apple.product-type.application";
|
||||||
@@ -220,6 +226,9 @@
|
|||||||
Base,
|
Base,
|
||||||
);
|
);
|
||||||
mainGroup = 97C146E51CF9000F007C117D;
|
mainGroup = 97C146E51CF9000F007C117D;
|
||||||
|
packageReferences = (
|
||||||
|
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */,
|
||||||
|
);
|
||||||
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
|
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
|
||||||
projectDirPath = "";
|
projectDirPath = "";
|
||||||
projectRoot = "";
|
projectRoot = "";
|
||||||
@@ -283,39 +292,6 @@
|
|||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
|
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
|
||||||
};
|
};
|
||||||
B788CEDB957A87EE8AC593BB /* [CP] Copy Pods Resources */ = {
|
|
||||||
isa = PBXShellScriptBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
inputFileListPaths = (
|
|
||||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
|
|
||||||
);
|
|
||||||
name = "[CP] Copy Pods Resources";
|
|
||||||
outputFileListPaths = (
|
|
||||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
shellPath = /bin/sh;
|
|
||||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
|
|
||||||
showEnvVarsInLog = 0;
|
|
||||||
};
|
|
||||||
F0D7F2413C6E4B7A9B1C2D3E /* Fix Native Asset Minimum OS */ = {
|
|
||||||
isa = PBXShellScriptBuildPhase;
|
|
||||||
alwaysOutOfDate = 1;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
inputPaths = (
|
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}",
|
|
||||||
);
|
|
||||||
name = "Fix Native Asset Minimum OS";
|
|
||||||
outputPaths = (
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
shellPath = /bin/sh;
|
|
||||||
shellScript = "set -e\nFRAMEWORKS_DIR=\"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}\"\nMIN_OS=\"${IPHONEOS_DEPLOYMENT_TARGET}\"\nif [ ! -d \"$FRAMEWORKS_DIR\" ] || [ -z \"$MIN_OS\" ]; then\n exit 0\nfi\nfind \"$FRAMEWORKS_DIR\" -maxdepth 2 -name Info.plist | while read -r plist; do\n bundle_id=$(/usr/libexec/PlistBuddy -c 'Print :CFBundleIdentifier' \"$plist\" 2>/dev/null || true)\n case \"$bundle_id\" in\n io.flutter.flutter.native-assets.*)\n /usr/libexec/PlistBuddy -c \"Set :MinimumOSVersion $MIN_OS\" \"$plist\" 2>/dev/null || \\\n /usr/libexec/PlistBuddy -c \"Add :MinimumOSVersion string $MIN_OS\" \"$plist\"\n ;;\n esac\ndone\n";
|
|
||||||
};
|
|
||||||
DE3B2E091393835C0B38492E /* [CP] Check Pods Manifest.lock */ = {
|
DE3B2E091393835C0B38492E /* [CP] Check Pods Manifest.lock */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
@@ -338,6 +314,22 @@
|
|||||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||||
showEnvVarsInLog = 0;
|
showEnvVarsInLog = 0;
|
||||||
};
|
};
|
||||||
|
F0D7F2413C6E4B7A9B1C2D3E /* Fix Native Asset Minimum OS */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
alwaysOutOfDate = 1;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}",
|
||||||
|
);
|
||||||
|
name = "Fix Native Asset Minimum OS";
|
||||||
|
outputPaths = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "set -e\nFRAMEWORKS_DIR=\"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}\"\nMIN_OS=\"${IPHONEOS_DEPLOYMENT_TARGET}\"\nif [ ! -d \"$FRAMEWORKS_DIR\" ] || [ -z \"$MIN_OS\" ]; then\n exit 0\nfi\nfind \"$FRAMEWORKS_DIR\" -maxdepth 2 -name Info.plist | while read -r plist; do\n bundle_id=$(/usr/libexec/PlistBuddy -c 'Print :CFBundleIdentifier' \"$plist\" 2>/dev/null || true)\n case \"$bundle_id\" in\n io.flutter.flutter.native-assets.*)\n /usr/libexec/PlistBuddy -c \"Set :MinimumOSVersion $MIN_OS\" \"$plist\" 2>/dev/null || \\\n /usr/libexec/PlistBuddy -c \"Add :MinimumOSVersion string $MIN_OS\" \"$plist\"\n ;;\n esac\ndone\n";
|
||||||
|
};
|
||||||
/* End PBXShellScriptBuildPhase section */
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
@@ -446,7 +438,9 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
|
DEVELOPMENT_TEAM = X74VJ4UAST;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
@@ -625,7 +619,9 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
|
DEVELOPMENT_TEAM = X74VJ4UAST;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
@@ -647,7 +643,9 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
|
DEVELOPMENT_TEAM = X74VJ4UAST;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
@@ -696,6 +694,20 @@
|
|||||||
defaultConfigurationName = Release;
|
defaultConfigurationName = Release;
|
||||||
};
|
};
|
||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
|
|
||||||
|
/* Begin XCLocalSwiftPackageReference section */
|
||||||
|
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = {
|
||||||
|
isa = XCLocalSwiftPackageReference;
|
||||||
|
relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage;
|
||||||
|
};
|
||||||
|
/* End XCLocalSwiftPackageReference section */
|
||||||
|
|
||||||
|
/* Begin XCSwiftPackageProductDependency section */
|
||||||
|
78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
productName = FlutterGeneratedPluginSwiftPackage;
|
||||||
|
};
|
||||||
|
/* End XCSwiftPackageProductDependency section */
|
||||||
};
|
};
|
||||||
rootObject = 97C146E61CF9000F007C117D /* Project object */;
|
rootObject = 97C146E61CF9000F007C117D /* Project object */;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,24 @@
|
|||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
buildImplicitDependencies = "YES">
|
buildImplicitDependencies = "YES">
|
||||||
|
<PreActions>
|
||||||
|
<ExecutionAction
|
||||||
|
ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
|
||||||
|
<ActionContent
|
||||||
|
title = "Run Prepare Flutter Framework Script"
|
||||||
|
scriptText = "/bin/sh "$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" prepare ">
|
||||||
|
<EnvironmentBuildable>
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||||
|
BuildableName = "Runner.app"
|
||||||
|
BlueprintName = "Runner"
|
||||||
|
ReferencedContainer = "container:Runner.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</EnvironmentBuildable>
|
||||||
|
</ActionContent>
|
||||||
|
</ExecutionAction>
|
||||||
|
</PreActions>
|
||||||
<BuildActionEntries>
|
<BuildActionEntries>
|
||||||
<BuildActionEntry
|
<BuildActionEntry
|
||||||
buildForTesting = "YES"
|
buildForTesting = "YES"
|
||||||
|
|||||||
@@ -162,6 +162,12 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
// Message windowing to limit memory usage
|
// Message windowing to limit memory usage
|
||||||
static const int _messageWindowSize = 200;
|
static const int _messageWindowSize = 200;
|
||||||
|
|
||||||
|
// Cap on discovered (non-contact) nodes retained in memory. Adverts arrive
|
||||||
|
// continuously from the whole mesh, so without a bound this list grows for
|
||||||
|
// as long as the app stays connected. When full, the stalest node (oldest
|
||||||
|
// lastSeen) is evicted to make room for a newly heard one.
|
||||||
|
static const int _maxDiscoveredContacts = 500;
|
||||||
|
|
||||||
MeshCoreConnectionState _state = MeshCoreConnectionState.disconnected;
|
MeshCoreConnectionState _state = MeshCoreConnectionState.disconnected;
|
||||||
BluetoothDevice? _device;
|
BluetoothDevice? _device;
|
||||||
BluetoothCharacteristic? _rxCharacteristic;
|
BluetoothCharacteristic? _rxCharacteristic;
|
||||||
@@ -251,6 +257,9 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
DateTime _lastRadioRxTime = DateTime.fromMillisecondsSinceEpoch(0);
|
DateTime _lastRadioRxTime = DateTime.fromMillisecondsSinceEpoch(0);
|
||||||
DateTime _lastContactMsgRxTime = DateTime.fromMillisecondsSinceEpoch(0);
|
DateTime _lastContactMsgRxTime = DateTime.fromMillisecondsSinceEpoch(0);
|
||||||
DateTime _lastChannelMsgRxTime = DateTime.fromMillisecondsSinceEpoch(0);
|
DateTime _lastChannelMsgRxTime = DateTime.fromMillisecondsSinceEpoch(0);
|
||||||
|
DateTime _lastZeroHopAdvertAt = DateTime.fromMillisecondsSinceEpoch(0);
|
||||||
|
double? _lastZeroHopAdvertLatitude;
|
||||||
|
double? _lastZeroHopAdvertLongitude;
|
||||||
static const int _radioQuietMs = 3000;
|
static const int _radioQuietMs = 3000;
|
||||||
static const int _radioQuietMaxWaitMs = 3000;
|
static const int _radioQuietMaxWaitMs = 3000;
|
||||||
|
|
||||||
@@ -1066,6 +1075,13 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
|
|
||||||
Future<void> _loadDiscoveredContactCache() async {
|
Future<void> _loadDiscoveredContactCache() async {
|
||||||
final cached = await _discoveryContactStore.loadContacts();
|
final cached = await _discoveryContactStore.loadContacts();
|
||||||
|
// Trim a previously-saved oversized list down to the freshest entries so a
|
||||||
|
// device that grew unbounded before the cap existed recovers on load.
|
||||||
|
if (cached.length > _maxDiscoveredContacts) {
|
||||||
|
cached.sort((a, b) => b.lastSeen.compareTo(a.lastSeen));
|
||||||
|
cached.removeRange(_maxDiscoveredContacts, cached.length);
|
||||||
|
unawaited(_discoveryContactStore.saveContacts(cached));
|
||||||
|
}
|
||||||
_discoveredContacts
|
_discoveredContacts
|
||||||
..clear()
|
..clear()
|
||||||
..addAll(cached);
|
..addAll(cached);
|
||||||
@@ -2558,6 +2574,8 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
_selfName = null;
|
_selfName = null;
|
||||||
_selfLatitude = null;
|
_selfLatitude = null;
|
||||||
_selfLongitude = null;
|
_selfLongitude = null;
|
||||||
|
_lastZeroHopAdvertLatitude = null;
|
||||||
|
_lastZeroHopAdvertLongitude = null;
|
||||||
_awaitingSelfInfo = false;
|
_awaitingSelfInfo = false;
|
||||||
_webInitialHandshakeRequestSent = false;
|
_webInitialHandshakeRequestSent = false;
|
||||||
_selfInfoRetryTimer?.cancel();
|
_selfInfoRetryTimer?.cancel();
|
||||||
@@ -2724,6 +2742,8 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
_selfName = null;
|
_selfName = null;
|
||||||
_selfLatitude = null;
|
_selfLatitude = null;
|
||||||
_selfLongitude = null;
|
_selfLongitude = null;
|
||||||
|
_lastZeroHopAdvertLatitude = null;
|
||||||
|
_lastZeroHopAdvertLongitude = null;
|
||||||
_clientRepeat = null;
|
_clientRepeat = null;
|
||||||
_rememberedNonRepeatRadioState = null;
|
_rememberedNonRepeatRadioState = null;
|
||||||
_firmwareVerCode = null;
|
_firmwareVerCode = null;
|
||||||
@@ -3883,6 +3903,11 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
Future<void> sendSelfAdvert({bool flood = true}) async {
|
Future<void> sendSelfAdvert({bool flood = true}) async {
|
||||||
if (!isConnected) return;
|
if (!isConnected) return;
|
||||||
await sendFrame(buildSendSelfAdvertFrame(flood: flood));
|
await sendFrame(buildSendSelfAdvertFrame(flood: flood));
|
||||||
|
if (!flood) {
|
||||||
|
_lastZeroHopAdvertAt = DateTime.now();
|
||||||
|
_lastZeroHopAdvertLatitude = _selfLatitude;
|
||||||
|
_lastZeroHopAdvertLongitude = _selfLongitude;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> rebootDevice() async {
|
Future<void> rebootDevice() async {
|
||||||
@@ -4346,6 +4371,42 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
tag: 'Connector',
|
tag: 'Connector',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const locationChangeEpsilon = 2.25e-4; // ~25 meters in degrees.
|
||||||
|
final lastAdvertLatitude = _lastZeroHopAdvertLatitude;
|
||||||
|
final lastAdvertLongitude = _lastZeroHopAdvertLongitude;
|
||||||
|
final currentLatitude = _selfLatitude;
|
||||||
|
final currentLongitude = _selfLongitude;
|
||||||
|
final latChanged =
|
||||||
|
lastAdvertLatitude != null &&
|
||||||
|
currentLatitude != null &&
|
||||||
|
(currentLatitude - lastAdvertLatitude).abs() >= locationChangeEpsilon;
|
||||||
|
final lonChanged =
|
||||||
|
lastAdvertLongitude != null &&
|
||||||
|
currentLongitude != null &&
|
||||||
|
(currentLongitude - lastAdvertLongitude).abs() >= locationChangeEpsilon;
|
||||||
|
final gpsSampleChanged =
|
||||||
|
hasValidLocation(currentLatitude, currentLongitude) &&
|
||||||
|
(!hasValidLocation(lastAdvertLatitude, lastAdvertLongitude) ||
|
||||||
|
latChanged ||
|
||||||
|
lonChanged);
|
||||||
|
final effectiveGpsIntervalSeconds =
|
||||||
|
_appSettingsService?.resolvedGpsIntervalSeconds(_currentCustomVars) ??
|
||||||
|
0;
|
||||||
|
final timeSinceLastZeroHopAdvert = DateTime.now().difference(
|
||||||
|
_lastZeroHopAdvertAt,
|
||||||
|
);
|
||||||
|
final shouldAutoSendZeroHopAdvert =
|
||||||
|
(gpsSampleChanged || (_clientRepeat ?? false)) &&
|
||||||
|
_advertLocPolicy == 1 &&
|
||||||
|
(_appSettingsService?.settings.autoSendZeroHopAdvertOnGpsUpdate ??
|
||||||
|
false) &&
|
||||||
|
effectiveGpsIntervalSeconds > 0 &&
|
||||||
|
timeSinceLastZeroHopAdvert.inSeconds >= effectiveGpsIntervalSeconds;
|
||||||
|
if (shouldAutoSendZeroHopAdvert) {
|
||||||
|
unawaited(sendSelfAdvert(flood: false));
|
||||||
|
}
|
||||||
|
|
||||||
final selfName = _selfName?.trim();
|
final selfName = _selfName?.trim();
|
||||||
if (_activeTransport == MeshCoreTransportType.usb &&
|
if (_activeTransport == MeshCoreTransportType.usb &&
|
||||||
selfName != null &&
|
selfName != null &&
|
||||||
@@ -7139,6 +7200,10 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
isActive: addActive,
|
isActive: addActive,
|
||||||
flags: 0,
|
flags: 0,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (_discoveredContacts.length >= _maxDiscoveredContacts) {
|
||||||
|
_evictStalestDiscoveredContact();
|
||||||
|
}
|
||||||
_discoveredContacts.add(disContact);
|
_discoveredContacts.add(disContact);
|
||||||
|
|
||||||
unawaited(_persistDiscoveredContacts());
|
unawaited(_persistDiscoveredContacts());
|
||||||
@@ -7156,6 +7221,19 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _evictStalestDiscoveredContact() {
|
||||||
|
if (_discoveredContacts.isEmpty) return;
|
||||||
|
var stalestIndex = 0;
|
||||||
|
for (int i = 1; i < _discoveredContacts.length; i++) {
|
||||||
|
if (_discoveredContacts[i].lastSeen.isBefore(
|
||||||
|
_discoveredContacts[stalestIndex].lastSeen,
|
||||||
|
)) {
|
||||||
|
stalestIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_discoveredContacts.removeAt(stalestIndex);
|
||||||
|
}
|
||||||
|
|
||||||
void removeAllDiscoveredContacts() {
|
void removeAllDiscoveredContacts() {
|
||||||
_discoveredContacts.clear();
|
_discoveredContacts.clear();
|
||||||
unawaited(_persistDiscoveredContacts());
|
unawaited(_persistDiscoveredContacts());
|
||||||
|
|||||||
@@ -100,6 +100,8 @@
|
|||||||
"settings_locationInvalid": "Невалидна ширина или дължина.",
|
"settings_locationInvalid": "Невалидна ширина или дължина.",
|
||||||
"settings_latitude": "Широчина",
|
"settings_latitude": "Широчина",
|
||||||
"settings_longitude": "Дължина",
|
"settings_longitude": "Дължина",
|
||||||
|
"settings_autoZeroHopAdvertOnGpsUpdate": "Автоматична zero-hop обява при GPS обновяване",
|
||||||
|
"settings_autoZeroHopAdvertOnGpsUpdateSubtitle": "Когато GPS местоположението се промени, изпрати zero-hop обява (изисква местоположение в обявата).",
|
||||||
"settings_privacyMode": "Режим на поверителност",
|
"settings_privacyMode": "Режим на поверителност",
|
||||||
"settings_privacyModeSubtitle": "Скриване на име/местоположение в рекламите",
|
"settings_privacyModeSubtitle": "Скриване на име/местоположение в рекламите",
|
||||||
"settings_privacyModeToggle": "Активирайте режим на поверителност, за да скриете името и местоположението си в рекламите.",
|
"settings_privacyModeToggle": "Активирайте режим на поверителност, за да скриете името и местоположението си в рекламите.",
|
||||||
@@ -240,6 +242,19 @@
|
|||||||
"appSettings_last6Hours": "Последни 6 часа",
|
"appSettings_last6Hours": "Последни 6 часа",
|
||||||
"appSettings_last24Hours": "Последно 24 часа",
|
"appSettings_last24Hours": "Последно 24 часа",
|
||||||
"appSettings_lastWeek": "Миналата седмица",
|
"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_offlineMapCache": "Кеш на офлайн карти",
|
||||||
"appSettings_noAreaSelected": "Няма избрана област",
|
"appSettings_noAreaSelected": "Няма избрана област",
|
||||||
"appSettings_areaSelectedZoom": "Избрана е област (мащаб {minZoom}-{maxZoom})",
|
"appSettings_areaSelectedZoom": "Избрана е област (мащаб {minZoom}-{maxZoom})",
|
||||||
@@ -769,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": "Север {north}, Юг {south}, Изток {east}, Запад {west}",
|
||||||
"@mapCache_boundsLabel": {
|
"@mapCache_boundsLabel": {
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
|
|||||||
@@ -100,6 +100,8 @@
|
|||||||
"settings_locationInvalid": "Ungültiger Breiten- oder Längengrad.",
|
"settings_locationInvalid": "Ungültiger Breiten- oder Längengrad.",
|
||||||
"settings_latitude": "Breitengrad",
|
"settings_latitude": "Breitengrad",
|
||||||
"settings_longitude": "Längengrad",
|
"settings_longitude": "Längengrad",
|
||||||
|
"settings_autoZeroHopAdvertOnGpsUpdate": "Automatische Zero-Hop-Ankündigung bei GPS-Update",
|
||||||
|
"settings_autoZeroHopAdvertOnGpsUpdateSubtitle": "Wenn sich der GPS-Standort ändert, eine Zero-Hop-Ankündigung senden (erfordert Standort in der Ankündigung).",
|
||||||
"settings_privacyMode": "Privatsphärenmodus",
|
"settings_privacyMode": "Privatsphärenmodus",
|
||||||
"settings_privacyModeSubtitle": "Name und Standort in Ankündigungen verbergen",
|
"settings_privacyModeSubtitle": "Name und Standort in Ankündigungen verbergen",
|
||||||
"settings_privacyModeToggle": "Privatsphärenmodus aktivieren, um Namen und Standort in Ankündigungen zu verbergen.",
|
"settings_privacyModeToggle": "Privatsphärenmodus aktivieren, um Namen und Standort in Ankündigungen zu verbergen.",
|
||||||
@@ -240,6 +242,19 @@
|
|||||||
"appSettings_last6Hours": "Letzte 6 Stunden",
|
"appSettings_last6Hours": "Letzte 6 Stunden",
|
||||||
"appSettings_last24Hours": "Letzte 24 Stunden",
|
"appSettings_last24Hours": "Letzte 24 Stunden",
|
||||||
"appSettings_lastWeek": "Letzte Woche",
|
"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_offlineMapCache": "Offline-Karten-Cache",
|
||||||
"appSettings_noAreaSelected": "Kein Bereich ausgewählt",
|
"appSettings_noAreaSelected": "Kein Bereich ausgewählt",
|
||||||
"appSettings_areaSelectedZoom": "Bereich ausgewählt (Zoom {minZoom}-{maxZoom})",
|
"appSettings_areaSelectedZoom": "Bereich ausgewählt (Zoom {minZoom}-{maxZoom})",
|
||||||
@@ -791,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": "N {north}, S {south}, E {east}, W {west}",
|
||||||
"@mapCache_boundsLabel": {
|
"@mapCache_boundsLabel": {
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
|
|||||||
@@ -206,6 +206,8 @@
|
|||||||
"settings_telemetryEnvironmentMode": "Telemetry Environment Mode",
|
"settings_telemetryEnvironmentMode": "Telemetry Environment Mode",
|
||||||
"settings_advertLocation": "Advert Location",
|
"settings_advertLocation": "Advert Location",
|
||||||
"settings_advertLocationSubtitle": "Include location in advert.",
|
"settings_advertLocationSubtitle": "Include location in advert.",
|
||||||
|
"settings_autoZeroHopAdvertOnGpsUpdate": "Auto Zero-Hop Advert On GPS Update",
|
||||||
|
"settings_autoZeroHopAdvertOnGpsUpdateSubtitle": "When GPS location changes, send a zero-hop advert (requires Advert Location).",
|
||||||
"settings_multiAck": "Multi-ACKs",
|
"settings_multiAck": "Multi-ACKs",
|
||||||
"settings_telemetryModeUpdated": "Telemetry mode updated",
|
"settings_telemetryModeUpdated": "Telemetry mode updated",
|
||||||
"settings_actions": "Actions",
|
"settings_actions": "Actions",
|
||||||
@@ -365,6 +367,19 @@
|
|||||||
"appSettings_last6Hours": "Last 6 hours",
|
"appSettings_last6Hours": "Last 6 hours",
|
||||||
"appSettings_last24Hours": "Last 24 hours",
|
"appSettings_last24Hours": "Last 24 hours",
|
||||||
"appSettings_lastWeek": "Last week",
|
"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_offlineMapCache": "Offline Map Cache",
|
||||||
"appSettings_unitsTitle": "Units",
|
"appSettings_unitsTitle": "Units",
|
||||||
"appSettings_unitsMetric": "Metric (m / km)",
|
"appSettings_unitsMetric": "Metric (m / km)",
|
||||||
@@ -723,6 +738,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"chat_sendGif": "Send GIF",
|
"chat_sendGif": "Send GIF",
|
||||||
|
"chat_receivedGif": "Received a GIF",
|
||||||
"chat_reply": "Reply",
|
"chat_reply": "Reply",
|
||||||
"chat_addReaction": "Add Reaction",
|
"chat_addReaction": "Add Reaction",
|
||||||
"chat_me": "Me",
|
"chat_me": "Me",
|
||||||
@@ -1095,6 +1111,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": "N {north}, S {south}, E {east}, W {west}",
|
||||||
"@mapCache_boundsLabel": {
|
"@mapCache_boundsLabel": {
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
@@ -1413,6 +1479,23 @@
|
|||||||
"repeater_advancedSettingsSubtitle": "Tuning knobs for experienced operators",
|
"repeater_advancedSettingsSubtitle": "Tuning knobs for experienced operators",
|
||||||
"repeater_pathHashMode": "Path hash mode",
|
"repeater_pathHashMode": "Path hash mode",
|
||||||
"repeater_pathHashModeHelper": "Bytes used to encode this repeater's ID in flood path/loop-detect tags. 0=1 byte (256 IDs, up to 64 hops), 1=2 bytes (65K IDs, up to 32 hops), 2=3 bytes (16M IDs, up to 21 hops). Firmware before v1.14 always used 1-byte paths; v1.14 and newer can be configured for 2- or 3-byte paths.",
|
"repeater_pathHashModeHelper": "Bytes used to encode this repeater's ID in flood path/loop-detect tags. 0=1 byte (256 IDs, up to 64 hops), 1=2 bytes (65K IDs, up to 32 hops), 2=3 bytes (16M IDs, up to 21 hops). Firmware before v1.14 always used 1-byte paths; v1.14 and newer can be configured for 2- or 3-byte paths.",
|
||||||
|
"repeater_keySettings": "Change Identity Keys",
|
||||||
|
"repeater_keySettingsSubtitle": "Change the public/private keypair",
|
||||||
|
"repeater_prvKey": "Private key",
|
||||||
|
"repeater_prvKeyHelper": "A new private key for the repeater, a 128-character hex string.",
|
||||||
|
"repeater_generatePrvKey": "Generate a random keypair",
|
||||||
|
"repeater_stopGeneratingPrvKey": "Interrupt search for keypair",
|
||||||
|
"repeater_pubKey": "Public key",
|
||||||
|
"repeater_pubKeyHelper": "This is the public key that goes with the generated private key. You can't set this directly.",
|
||||||
|
"repeater_pubKeyPrefix": "Desired prefix",
|
||||||
|
"repeater_pubKeyPrefixHelper": "Find a public key that starts with these hex digits. Expected tries needed: {tries}.",
|
||||||
|
"@repeater_pubKeyPrefixHelper": {
|
||||||
|
"placeholders": {
|
||||||
|
"tries": {
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"repeater_txDelay": "Flood TX delay",
|
"repeater_txDelay": "Flood TX delay",
|
||||||
"repeater_txDelayHelper": "Retransmit spacing for flood traffic, as a multiplier of the packet's airtime (0-2, default 0.5). Higher = fewer collisions but slower delivery.",
|
"repeater_txDelayHelper": "Retransmit spacing for flood traffic, as a multiplier of the packet's airtime (0-2, default 0.5). Higher = fewer collisions but slower delivery.",
|
||||||
"repeater_directTxDelay": "Direct TX delay",
|
"repeater_directTxDelay": "Direct TX delay",
|
||||||
@@ -1593,7 +1676,7 @@
|
|||||||
"repeater_cliHelpPowersavingOnOff": "Enables or disables powersaving mode (where supported).",
|
"repeater_cliHelpPowersavingOnOff": "Enables or disables powersaving mode (where supported).",
|
||||||
"repeater_cliHelpErase": "(Serial only) Formats the device file system. Wipes all settings and contacts.",
|
"repeater_cliHelpErase": "(Serial only) Formats the device file system. Wipes all settings and contacts.",
|
||||||
"repeater_cliHelpSetDutyCycle": "Sets the maximum allowed transmit duty cycle as a percentage (1-100). Internally adjusts the airtime factor.",
|
"repeater_cliHelpSetDutyCycle": "Sets the maximum allowed transmit duty cycle as a percentage (1-100). Internally adjusts the airtime factor.",
|
||||||
"repeater_cliHelpSetPrvKey": "(Serial only) Replaces the device identity private key. Reboot required to apply. Generates a new public key.",
|
"repeater_cliHelpSetPrvKey": "Replaces the device identity private key. Reboot required to apply. Generates a new public key.",
|
||||||
"repeater_cliHelpSetRadioRxGain": "(SX126x only) Toggles boosted RX gain for improved sensitivity at higher current draw.",
|
"repeater_cliHelpSetRadioRxGain": "(SX126x only) Toggles boosted RX gain for improved sensitivity at higher current draw.",
|
||||||
"repeater_cliHelpSetOwnerInfo": "Sets the owner contact info string included in adverts. Use '|' for newlines.",
|
"repeater_cliHelpSetOwnerInfo": "Sets the owner contact info string included in adverts. Use '|' for newlines.",
|
||||||
"repeater_cliHelpSetPathHashMode": "Sets the path-hash mode. 0 = legacy, 1 = standard, 2 = strict. Affects how routing paths are matched.",
|
"repeater_cliHelpSetPathHashMode": "Sets the path-hash mode. 0 = legacy, 1 = standard, 2 = strict. Affects how routing paths are matched.",
|
||||||
|
|||||||
@@ -100,6 +100,8 @@
|
|||||||
"settings_locationInvalid": "Latitud o longitud inválidos.",
|
"settings_locationInvalid": "Latitud o longitud inválidos.",
|
||||||
"settings_latitude": "Latitud",
|
"settings_latitude": "Latitud",
|
||||||
"settings_longitude": "Longitud",
|
"settings_longitude": "Longitud",
|
||||||
|
"settings_autoZeroHopAdvertOnGpsUpdate": "Anuncio de cero saltos automático al actualizar GPS",
|
||||||
|
"settings_autoZeroHopAdvertOnGpsUpdateSubtitle": "Cuando cambie la ubicación GPS, enviar un anuncio de cero saltos (requiere ubicación en anuncio).",
|
||||||
"settings_privacyMode": "Modo de privacidad",
|
"settings_privacyMode": "Modo de privacidad",
|
||||||
"settings_privacyModeSubtitle": "Ocultar nombre/ubicación en anuncios",
|
"settings_privacyModeSubtitle": "Ocultar nombre/ubicación en anuncios",
|
||||||
"settings_privacyModeToggle": "Activar el modo de privacidad para ocultar tu nombre y ubicación en los anuncios.",
|
"settings_privacyModeToggle": "Activar el modo de privacidad para ocultar tu nombre y ubicación en los anuncios.",
|
||||||
@@ -240,6 +242,19 @@
|
|||||||
"appSettings_last6Hours": "Últimas 6 horas",
|
"appSettings_last6Hours": "Últimas 6 horas",
|
||||||
"appSettings_last24Hours": "Últimas 24 horas",
|
"appSettings_last24Hours": "Últimas 24 horas",
|
||||||
"appSettings_lastWeek": "La semana pasada",
|
"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_offlineMapCache": "Caché de mapa sin conexión",
|
||||||
"appSettings_noAreaSelected": "No se ha seleccionado ningún área",
|
"appSettings_noAreaSelected": "No se ha seleccionado ningún área",
|
||||||
"appSettings_areaSelectedZoom": "Área seleccionada (zoom {minZoom}-{maxZoom})",
|
"appSettings_areaSelectedZoom": "Área seleccionada (zoom {minZoom}-{maxZoom})",
|
||||||
@@ -791,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": "N {north}, S {south}, E {east}, W {west}",
|
||||||
"@mapCache_boundsLabel": {
|
"@mapCache_boundsLabel": {
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
|
|||||||
@@ -100,6 +100,8 @@
|
|||||||
"settings_locationInvalid": "Latitude ou longitude invalide.",
|
"settings_locationInvalid": "Latitude ou longitude invalide.",
|
||||||
"settings_latitude": "Latitude",
|
"settings_latitude": "Latitude",
|
||||||
"settings_longitude": "Longitude",
|
"settings_longitude": "Longitude",
|
||||||
|
"settings_autoZeroHopAdvertOnGpsUpdate": "Annonce zéro saut automatique lors de la mise à jour GPS",
|
||||||
|
"settings_autoZeroHopAdvertOnGpsUpdateSubtitle": "Lorsque la position GPS change, envoyer une annonce zéro saut (nécessite la position dans l'annonce).",
|
||||||
"settings_privacyMode": "Mode de confidentialité",
|
"settings_privacyMode": "Mode de confidentialité",
|
||||||
"settings_privacyModeSubtitle": "Masquer le nom et la localisation dans les annonces",
|
"settings_privacyModeSubtitle": "Masquer le nom et la localisation dans les annonces",
|
||||||
"settings_privacyModeToggle": "Activez le mode confidentialité pour masquer votre nom et votre localisation dans les annonces.",
|
"settings_privacyModeToggle": "Activez le mode confidentialité pour masquer votre nom et votre localisation dans les annonces.",
|
||||||
@@ -240,6 +242,19 @@
|
|||||||
"appSettings_last6Hours": "Dernières 6 heures",
|
"appSettings_last6Hours": "Dernières 6 heures",
|
||||||
"appSettings_last24Hours": "Dernières 24 heures",
|
"appSettings_last24Hours": "Dernières 24 heures",
|
||||||
"appSettings_lastWeek": "La semaine dernière",
|
"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_offlineMapCache": "Cache de carte hors ligne",
|
||||||
"appSettings_noAreaSelected": "Aucune zone sélectionnée",
|
"appSettings_noAreaSelected": "Aucune zone sélectionnée",
|
||||||
"appSettings_areaSelectedZoom": "Zone sélectionnée (zoom {minZoom}-{maxZoom})",
|
"appSettings_areaSelectedZoom": "Zone sélectionnée (zoom {minZoom}-{maxZoom})",
|
||||||
@@ -791,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": "N {north}, S {south}, E {east}, O {west}",
|
||||||
"@mapCache_boundsLabel": {
|
"@mapCache_boundsLabel": {
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
|
|||||||
@@ -161,6 +161,8 @@
|
|||||||
"settings_locationIntervalInvalid": "Az intervallumnak legalább 60 másodpercnek kell lennie, és kevesebbnek kell lennie 86 400 másodpercnél.",
|
"settings_locationIntervalInvalid": "Az intervallumnak legalább 60 másodpercnek kell lennie, és kevesebbnek kell lennie 86 400 másodpercnél.",
|
||||||
"settings_latitude": "Szélesség",
|
"settings_latitude": "Szélesség",
|
||||||
"settings_longitude": "Hosszúság",
|
"settings_longitude": "Hosszúság",
|
||||||
|
"settings_autoZeroHopAdvertOnGpsUpdate": "Automatikus zero-hop hirdetés GPS-frissítéskor",
|
||||||
|
"settings_autoZeroHopAdvertOnGpsUpdateSubtitle": "Ha a GPS-helyzet megváltozik, küldjön zero-hop hirdetést (a hirdetésben helymegadás szükséges).",
|
||||||
"settings_contactSettings": "Kapcsolati beállítások",
|
"settings_contactSettings": "Kapcsolati beállítások",
|
||||||
"settings_contactSettingsSubtitle": "A névjegyek hozzáadásának beállításai.",
|
"settings_contactSettingsSubtitle": "A névjegyek hozzáadásának beállításai.",
|
||||||
"settings_privacyMode": "Adatvédelmi mód",
|
"settings_privacyMode": "Adatvédelmi mód",
|
||||||
@@ -332,6 +334,19 @@
|
|||||||
"appSettings_last6Hours": "Utolsó 6 óra",
|
"appSettings_last6Hours": "Utolsó 6 óra",
|
||||||
"appSettings_last24Hours": "Az elmúlt 24 óra",
|
"appSettings_last24Hours": "Az elmúlt 24 óra",
|
||||||
"appSettings_lastWeek": "Múlt héten",
|
"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_offlineMapCache": "Offline térképgyorsítótár",
|
||||||
"appSettings_unitsTitle": "Egységek",
|
"appSettings_unitsTitle": "Egységek",
|
||||||
"appSettings_unitsMetric": "Metrikus (m/km)",
|
"appSettings_unitsMetric": "Metrikus (m/km)",
|
||||||
@@ -952,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": "É {north}, D {south}, K {east}, Ny {west}",
|
||||||
"@mapCache_boundsLabel": {
|
"@mapCache_boundsLabel": {
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
|
|||||||
@@ -240,6 +240,19 @@
|
|||||||
"appSettings_last6Hours": "Ultimi 6 ore",
|
"appSettings_last6Hours": "Ultimi 6 ore",
|
||||||
"appSettings_last24Hours": "Ultime 24 ore",
|
"appSettings_last24Hours": "Ultime 24 ore",
|
||||||
"appSettings_lastWeek": "La settimana scorsa",
|
"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_offlineMapCache": "Cache Mappa Offline",
|
||||||
"appSettings_noAreaSelected": "Nessun'area selezionata",
|
"appSettings_noAreaSelected": "Nessun'area selezionata",
|
||||||
"appSettings_areaSelectedZoom": "Area selezionata (zoom {minZoom}-{maxZoom})",
|
"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": "N {north}, S {south}, E {east}, W {west}",
|
||||||
"@mapCache_boundsLabel": {
|
"@mapCache_boundsLabel": {
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
@@ -2207,6 +2270,8 @@
|
|||||||
"settings_telemetryEnvironmentMode": "Modalità di ambiente di telemetria",
|
"settings_telemetryEnvironmentMode": "Modalità di ambiente di telemetria",
|
||||||
"settings_advertLocation": "Posizione dell'annuncio",
|
"settings_advertLocation": "Posizione dell'annuncio",
|
||||||
"settings_advertLocationSubtitle": "Includi la posizione nell'annuncio",
|
"settings_advertLocationSubtitle": "Includi la posizione nell'annuncio",
|
||||||
|
"settings_autoZeroHopAdvertOnGpsUpdate": "Annuncio zero-hop automatico all'aggiornamento GPS",
|
||||||
|
"settings_autoZeroHopAdvertOnGpsUpdateSubtitle": "Quando la posizione GPS cambia, invia un annuncio zero-hop (richiede la posizione nell'annuncio).",
|
||||||
"settings_privacy": "Impostazioni sulla privacy",
|
"settings_privacy": "Impostazioni sulla privacy",
|
||||||
"settings_denyAll": "Negare tutto",
|
"settings_denyAll": "Negare tutto",
|
||||||
"settings_privacySubtitle": "Controlla le informazioni che vengono condivise.",
|
"settings_privacySubtitle": "Controlla le informazioni che vengono condivise.",
|
||||||
|
|||||||
@@ -167,6 +167,8 @@
|
|||||||
"settings_locationIntervalInvalid": "間隔は少なくとも60秒で、86400秒未満でなければなりません。",
|
"settings_locationIntervalInvalid": "間隔は少なくとも60秒で、86400秒未満でなければなりません。",
|
||||||
"settings_latitude": "緯度",
|
"settings_latitude": "緯度",
|
||||||
"settings_longitude": "経度",
|
"settings_longitude": "経度",
|
||||||
|
"settings_autoZeroHopAdvertOnGpsUpdate": "GPS更新時にゼロホップ広告を自動送信",
|
||||||
|
"settings_autoZeroHopAdvertOnGpsUpdateSubtitle": "GPS位置が変化したときにゼロホップ広告を送信します(広告への位置情報の含有が必要)。",
|
||||||
"settings_contactSettings": "連絡先設定",
|
"settings_contactSettings": "連絡先設定",
|
||||||
"settings_contactSettingsSubtitle": "連絡先の追加方法に関する設定",
|
"settings_contactSettingsSubtitle": "連絡先の追加方法に関する設定",
|
||||||
"settings_privacyMode": "プライバシーモード",
|
"settings_privacyMode": "プライバシーモード",
|
||||||
@@ -338,6 +340,19 @@
|
|||||||
"appSettings_last6Hours": "過去6時間",
|
"appSettings_last6Hours": "過去6時間",
|
||||||
"appSettings_last24Hours": "過去24時間",
|
"appSettings_last24Hours": "過去24時間",
|
||||||
"appSettings_lastWeek": "過去1週間",
|
"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_offlineMapCache": "オフライン地図キャッシュ",
|
||||||
"appSettings_unitsTitle": "単位",
|
"appSettings_unitsTitle": "単位",
|
||||||
"appSettings_unitsMetric": "メートル法 (m / km)",
|
"appSettings_unitsMetric": "メートル法 (m / km)",
|
||||||
@@ -970,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": "N {north}, S {south}, E {east}, W {west}",
|
||||||
"@mapCache_boundsLabel": {
|
"@mapCache_boundsLabel": {
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
|
|||||||
@@ -161,6 +161,8 @@
|
|||||||
"settings_locationIntervalInvalid": "간격은 최소 60초 이상, 86400초 미만이어야 합니다.",
|
"settings_locationIntervalInvalid": "간격은 최소 60초 이상, 86400초 미만이어야 합니다.",
|
||||||
"settings_latitude": "위도",
|
"settings_latitude": "위도",
|
||||||
"settings_longitude": "경도",
|
"settings_longitude": "경도",
|
||||||
|
"settings_autoZeroHopAdvertOnGpsUpdate": "GPS 업데이트 시 제로 홉 광고 자동 전송",
|
||||||
|
"settings_autoZeroHopAdvertOnGpsUpdateSubtitle": "GPS 위치가 변경되면 제로 홉 광고를 전송합니다(광고에 위치 포함 필요).",
|
||||||
"settings_contactSettings": "연락처 설정",
|
"settings_contactSettings": "연락처 설정",
|
||||||
"settings_contactSettingsSubtitle": "연락처 추가 방식 설정",
|
"settings_contactSettingsSubtitle": "연락처 추가 방식 설정",
|
||||||
"settings_privacyMode": "개인 정보 보호 모드",
|
"settings_privacyMode": "개인 정보 보호 모드",
|
||||||
@@ -332,6 +334,19 @@
|
|||||||
"appSettings_last6Hours": "지난 6시간",
|
"appSettings_last6Hours": "지난 6시간",
|
||||||
"appSettings_last24Hours": "지난 24시간",
|
"appSettings_last24Hours": "지난 24시간",
|
||||||
"appSettings_lastWeek": "지난 주",
|
"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_offlineMapCache": "오프라인 지도 캐시",
|
||||||
"appSettings_unitsTitle": "단위",
|
"appSettings_unitsTitle": "단위",
|
||||||
"appSettings_unitsMetric": "미터법 (m / km)",
|
"appSettings_unitsMetric": "미터법 (m / km)",
|
||||||
@@ -952,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": "N {north}, S {south}, E {east}, W {west}",
|
||||||
"@mapCache_boundsLabel": {
|
"@mapCache_boundsLabel": {
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
|
|||||||
@@ -1030,6 +1030,18 @@ abstract class AppLocalizations {
|
|||||||
/// **'Include location in advert.'**
|
/// **'Include location in advert.'**
|
||||||
String get settings_advertLocationSubtitle;
|
String get settings_advertLocationSubtitle;
|
||||||
|
|
||||||
|
/// No description provided for @settings_autoZeroHopAdvertOnGpsUpdate.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Auto Zero-Hop Advert On GPS Update'**
|
||||||
|
String get settings_autoZeroHopAdvertOnGpsUpdate;
|
||||||
|
|
||||||
|
/// No description provided for @settings_autoZeroHopAdvertOnGpsUpdateSubtitle.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'When GPS location changes, send a zero-hop advert (requires Advert Location).'**
|
||||||
|
String get settings_autoZeroHopAdvertOnGpsUpdateSubtitle;
|
||||||
|
|
||||||
/// No description provided for @settings_multiAck.
|
/// No description provided for @settings_multiAck.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
@@ -1816,6 +1828,42 @@ abstract class AppLocalizations {
|
|||||||
/// **'Last week'**
|
/// **'Last week'**
|
||||||
String get appSettings_lastWeek;
|
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.
|
/// No description provided for @appSettings_offlineMapCache.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
@@ -2668,6 +2716,12 @@ abstract class AppLocalizations {
|
|||||||
/// **'Send GIF'**
|
/// **'Send GIF'**
|
||||||
String get chat_sendGif;
|
String get chat_sendGif;
|
||||||
|
|
||||||
|
/// No description provided for @chat_receivedGif.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Received a GIF'**
|
||||||
|
String get chat_receivedGif;
|
||||||
|
|
||||||
/// No description provided for @chat_reply.
|
/// No description provided for @chat_reply.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
@@ -3820,6 +3874,54 @@ abstract class AppLocalizations {
|
|||||||
/// **'Failed downloads: {count}'**
|
/// **'Failed downloads: {count}'**
|
||||||
String mapCache_failedDownloads(int 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.
|
/// No description provided for @mapCache_boundsLabel.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
@@ -4772,6 +4874,66 @@ abstract class AppLocalizations {
|
|||||||
/// **'Bytes used to encode this repeater\'s ID in flood path/loop-detect tags. 0=1 byte (256 IDs, up to 64 hops), 1=2 bytes (65K IDs, up to 32 hops), 2=3 bytes (16M IDs, up to 21 hops). Firmware before v1.14 always used 1-byte paths; v1.14 and newer can be configured for 2- or 3-byte paths.'**
|
/// **'Bytes used to encode this repeater\'s ID in flood path/loop-detect tags. 0=1 byte (256 IDs, up to 64 hops), 1=2 bytes (65K IDs, up to 32 hops), 2=3 bytes (16M IDs, up to 21 hops). Firmware before v1.14 always used 1-byte paths; v1.14 and newer can be configured for 2- or 3-byte paths.'**
|
||||||
String get repeater_pathHashModeHelper;
|
String get repeater_pathHashModeHelper;
|
||||||
|
|
||||||
|
/// No description provided for @repeater_keySettings.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Change Identity Keys'**
|
||||||
|
String get repeater_keySettings;
|
||||||
|
|
||||||
|
/// No description provided for @repeater_keySettingsSubtitle.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Change the public/private keypair'**
|
||||||
|
String get repeater_keySettingsSubtitle;
|
||||||
|
|
||||||
|
/// No description provided for @repeater_prvKey.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Private key'**
|
||||||
|
String get repeater_prvKey;
|
||||||
|
|
||||||
|
/// No description provided for @repeater_prvKeyHelper.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'A new private key for the repeater, a 128-character hex string.'**
|
||||||
|
String get repeater_prvKeyHelper;
|
||||||
|
|
||||||
|
/// No description provided for @repeater_generatePrvKey.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Generate a random keypair'**
|
||||||
|
String get repeater_generatePrvKey;
|
||||||
|
|
||||||
|
/// No description provided for @repeater_stopGeneratingPrvKey.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Interrupt search for keypair'**
|
||||||
|
String get repeater_stopGeneratingPrvKey;
|
||||||
|
|
||||||
|
/// No description provided for @repeater_pubKey.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Public key'**
|
||||||
|
String get repeater_pubKey;
|
||||||
|
|
||||||
|
/// No description provided for @repeater_pubKeyHelper.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'This is the public key that goes with the generated private key. You can\'t set this directly.'**
|
||||||
|
String get repeater_pubKeyHelper;
|
||||||
|
|
||||||
|
/// No description provided for @repeater_pubKeyPrefix.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Desired prefix'**
|
||||||
|
String get repeater_pubKeyPrefix;
|
||||||
|
|
||||||
|
/// No description provided for @repeater_pubKeyPrefixHelper.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Find a public key that starts with these hex digits. Expected tries needed: {tries}.'**
|
||||||
|
String repeater_pubKeyPrefixHelper(int tries);
|
||||||
|
|
||||||
/// No description provided for @repeater_txDelay.
|
/// No description provided for @repeater_txDelay.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
@@ -5543,7 +5705,7 @@ abstract class AppLocalizations {
|
|||||||
/// No description provided for @repeater_cliHelpSetPrvKey.
|
/// No description provided for @repeater_cliHelpSetPrvKey.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'(Serial only) Replaces the device identity private key. Reboot required to apply. Generates a new public key.'**
|
/// **'Replaces the device identity private key. Reboot required to apply. Generates a new public key.'**
|
||||||
String get repeater_cliHelpSetPrvKey;
|
String get repeater_cliHelpSetPrvKey;
|
||||||
|
|
||||||
/// No description provided for @repeater_cliHelpSetRadioRxGain.
|
/// No description provided for @repeater_cliHelpSetRadioRxGain.
|
||||||
|
|||||||
@@ -506,6 +506,14 @@ class AppLocalizationsBg extends AppLocalizations {
|
|||||||
String get settings_advertLocationSubtitle =>
|
String get settings_advertLocationSubtitle =>
|
||||||
'Включи местоположение в обявата';
|
'Включи местоположение в обявата';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_autoZeroHopAdvertOnGpsUpdate =>
|
||||||
|
'Автоматична zero-hop обява при GPS обновяване';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_autoZeroHopAdvertOnGpsUpdateSubtitle =>
|
||||||
|
'Когато GPS местоположението се промени, изпрати zero-hop обява (изисква местоположение в обявата).';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_multiAck => 'Множество ACK';
|
String get settings_multiAck => 'Множество ACK';
|
||||||
|
|
||||||
@@ -951,6 +959,28 @@ class AppLocalizationsBg extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get appSettings_lastWeek => 'Миналата седмица';
|
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
|
@override
|
||||||
String get appSettings_offlineMapCache => 'Кеш на офлайн карти';
|
String get appSettings_offlineMapCache => 'Кеш на офлайн карти';
|
||||||
|
|
||||||
@@ -1450,6 +1480,9 @@ class AppLocalizationsBg extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get chat_sendGif => 'Изпрати GIF';
|
String get chat_sendGif => 'Изпрати GIF';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get chat_receivedGif => 'Received a GIF';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get chat_reply => 'Отговори';
|
String get chat_reply => 'Отговори';
|
||||||
|
|
||||||
@@ -2102,6 +2135,42 @@ class AppLocalizationsBg extends AppLocalizations {
|
|||||||
return 'Неуспешни изтегляния: $count';
|
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
|
@override
|
||||||
String mapCache_boundsLabel(
|
String mapCache_boundsLabel(
|
||||||
String north,
|
String north,
|
||||||
@@ -2651,6 +2720,41 @@ class AppLocalizationsBg extends AppLocalizations {
|
|||||||
String get repeater_pathHashModeHelper =>
|
String get repeater_pathHashModeHelper =>
|
||||||
'Байтовете, използвани за кодиране на идентификатора на този повторител в таговете за откриване на потоци/цикли, са: 0=1 байт (256 идентификатора, до 64 скока), 1=2 байта (65 000 идентификатора, до 32 скока), 2=3 байта (16 милиона идентификатора, до 21 скока). Версиите 1.13 и по-старите фърмуери използват многобайтови пътища - само след като мрежата е актуализирана до версия 1.14 или по-нова.';
|
'Байтовете, използвани за кодиране на идентификатора на този повторител в таговете за откриване на потоци/цикли, са: 0=1 байт (256 идентификатора, до 64 скока), 1=2 байта (65 000 идентификатора, до 32 скока), 2=3 байта (16 милиона идентификатора, до 21 скока). Версиите 1.13 и по-старите фърмуери използват многобайтови пътища - само след като мрежата е актуализирана до версия 1.14 или по-нова.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_keySettings => 'Change Identity Keys';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_keySettingsSubtitle =>
|
||||||
|
'Change the public/private keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_prvKey => 'Private key';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_prvKeyHelper =>
|
||||||
|
'A new private key for the repeater, a 128-character hex string.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_generatePrvKey => 'Generate a random keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_stopGeneratingPrvKey => 'Interrupt search for keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKey => 'Public key';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKeyHelper =>
|
||||||
|
'This is the public key that goes with the generated private key. You can\'t set this directly.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKeyPrefix => 'Desired prefix';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String repeater_pubKeyPrefixHelper(int tries) {
|
||||||
|
return 'Find a public key that starts with these hex digits. Expected tries needed: $tries.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_txDelay => 'Забавяне на Flood TX';
|
String get repeater_txDelay => 'Забавяне на Flood TX';
|
||||||
|
|
||||||
|
|||||||
@@ -506,6 +506,14 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
String get settings_advertLocationSubtitle =>
|
String get settings_advertLocationSubtitle =>
|
||||||
'Standort in die Ankündigung einschließen.';
|
'Standort in die Ankündigung einschließen.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_autoZeroHopAdvertOnGpsUpdate =>
|
||||||
|
'Automatische Zero-Hop-Ankündigung bei GPS-Update';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_autoZeroHopAdvertOnGpsUpdateSubtitle =>
|
||||||
|
'Wenn sich der GPS-Standort ändert, eine Zero-Hop-Ankündigung senden (erfordert Standort in der Ankündigung).';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_multiAck => 'Mehrfach-ACKs';
|
String get settings_multiAck => 'Mehrfach-ACKs';
|
||||||
|
|
||||||
@@ -948,6 +956,28 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get appSettings_lastWeek => 'Letzte Woche';
|
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
|
@override
|
||||||
String get appSettings_offlineMapCache => 'Offline-Karten-Cache';
|
String get appSettings_offlineMapCache => 'Offline-Karten-Cache';
|
||||||
|
|
||||||
@@ -1451,6 +1481,9 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get chat_sendGif => 'GIF senden';
|
String get chat_sendGif => 'GIF senden';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get chat_receivedGif => 'Received a GIF';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get chat_reply => 'Antworten';
|
String get chat_reply => 'Antworten';
|
||||||
|
|
||||||
@@ -2102,6 +2135,42 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
return 'Fehlgeschlagene Downloads: $count';
|
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
|
@override
|
||||||
String mapCache_boundsLabel(
|
String mapCache_boundsLabel(
|
||||||
String north,
|
String north,
|
||||||
@@ -2647,6 +2716,41 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
String get repeater_pathHashModeHelper =>
|
String get repeater_pathHashModeHelper =>
|
||||||
'Bytes, die zur Kodierung der ID dieses Repeaters in Flood-Pfad-/Schleifen-Erkennung-Tags verwendet werden. 0 = 1 Byte (256 IDs, bis zu 64 Hops), 1 = 2 Bytes (65.000 IDs, bis zu 32 Hops), 2 = 3 Bytes (16 Millionen IDs, bis zu 21 Hops). Firmware-Versionen 1.13 und älter verwenden mehrstellige Pfade – ab Version 1.14+ wird nur ein Pfad erstellt, sobald das Netzwerk aktiv ist.';
|
'Bytes, die zur Kodierung der ID dieses Repeaters in Flood-Pfad-/Schleifen-Erkennung-Tags verwendet werden. 0 = 1 Byte (256 IDs, bis zu 64 Hops), 1 = 2 Bytes (65.000 IDs, bis zu 32 Hops), 2 = 3 Bytes (16 Millionen IDs, bis zu 21 Hops). Firmware-Versionen 1.13 und älter verwenden mehrstellige Pfade – ab Version 1.14+ wird nur ein Pfad erstellt, sobald das Netzwerk aktiv ist.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_keySettings => 'Change Identity Keys';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_keySettingsSubtitle =>
|
||||||
|
'Change the public/private keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_prvKey => 'Private key';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_prvKeyHelper =>
|
||||||
|
'A new private key for the repeater, a 128-character hex string.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_generatePrvKey => 'Generate a random keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_stopGeneratingPrvKey => 'Interrupt search for keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKey => 'Public key';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKeyHelper =>
|
||||||
|
'This is the public key that goes with the generated private key. You can\'t set this directly.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKeyPrefix => 'Desired prefix';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String repeater_pubKeyPrefixHelper(int tries) {
|
||||||
|
return 'Find a public key that starts with these hex digits. Expected tries needed: $tries.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_txDelay => 'Flood-TX-Verzögerung';
|
String get repeater_txDelay => 'Flood-TX-Verzögerung';
|
||||||
|
|
||||||
|
|||||||
@@ -496,6 +496,14 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get settings_advertLocationSubtitle => 'Include location in advert.';
|
String get settings_advertLocationSubtitle => 'Include location in advert.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_autoZeroHopAdvertOnGpsUpdate =>
|
||||||
|
'Auto Zero-Hop Advert On GPS Update';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_autoZeroHopAdvertOnGpsUpdateSubtitle =>
|
||||||
|
'When GPS location changes, send a zero-hop advert (requires Advert Location).';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_multiAck => 'Multi-ACKs';
|
String get settings_multiAck => 'Multi-ACKs';
|
||||||
|
|
||||||
@@ -931,6 +939,28 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get appSettings_lastWeek => 'Last week';
|
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
|
@override
|
||||||
String get appSettings_offlineMapCache => 'Offline Map Cache';
|
String get appSettings_offlineMapCache => 'Offline Map Cache';
|
||||||
|
|
||||||
@@ -1420,6 +1450,9 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get chat_sendGif => 'Send GIF';
|
String get chat_sendGif => 'Send GIF';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get chat_receivedGif => 'Received a GIF';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get chat_reply => 'Reply';
|
String get chat_reply => 'Reply';
|
||||||
|
|
||||||
@@ -2063,6 +2096,42 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
return 'Failed downloads: $count';
|
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
|
@override
|
||||||
String mapCache_boundsLabel(
|
String mapCache_boundsLabel(
|
||||||
String north,
|
String north,
|
||||||
@@ -2601,6 +2670,41 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
String get repeater_pathHashModeHelper =>
|
String get repeater_pathHashModeHelper =>
|
||||||
'Bytes used to encode this repeater\'s ID in flood path/loop-detect tags. 0=1 byte (256 IDs, up to 64 hops), 1=2 bytes (65K IDs, up to 32 hops), 2=3 bytes (16M IDs, up to 21 hops). Firmware before v1.14 always used 1-byte paths; v1.14 and newer can be configured for 2- or 3-byte paths.';
|
'Bytes used to encode this repeater\'s ID in flood path/loop-detect tags. 0=1 byte (256 IDs, up to 64 hops), 1=2 bytes (65K IDs, up to 32 hops), 2=3 bytes (16M IDs, up to 21 hops). Firmware before v1.14 always used 1-byte paths; v1.14 and newer can be configured for 2- or 3-byte paths.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_keySettings => 'Change Identity Keys';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_keySettingsSubtitle =>
|
||||||
|
'Change the public/private keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_prvKey => 'Private key';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_prvKeyHelper =>
|
||||||
|
'A new private key for the repeater, a 128-character hex string.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_generatePrvKey => 'Generate a random keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_stopGeneratingPrvKey => 'Interrupt search for keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKey => 'Public key';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKeyHelper =>
|
||||||
|
'This is the public key that goes with the generated private key. You can\'t set this directly.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKeyPrefix => 'Desired prefix';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String repeater_pubKeyPrefixHelper(int tries) {
|
||||||
|
return 'Find a public key that starts with these hex digits. Expected tries needed: $tries.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_txDelay => 'Flood TX delay';
|
String get repeater_txDelay => 'Flood TX delay';
|
||||||
|
|
||||||
@@ -3071,7 +3175,7 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_cliHelpSetPrvKey =>
|
String get repeater_cliHelpSetPrvKey =>
|
||||||
'(Serial only) Replaces the device identity private key. Reboot required to apply. Generates a new public key.';
|
'Replaces the device identity private key. Reboot required to apply. Generates a new public key.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_cliHelpSetRadioRxGain =>
|
String get repeater_cliHelpSetRadioRxGain =>
|
||||||
|
|||||||
@@ -503,6 +503,14 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get settings_advertLocationSubtitle => 'Incluir ubicación en anuncio';
|
String get settings_advertLocationSubtitle => 'Incluir ubicación en anuncio';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_autoZeroHopAdvertOnGpsUpdate =>
|
||||||
|
'Anuncio de cero saltos automático al actualizar GPS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_autoZeroHopAdvertOnGpsUpdateSubtitle =>
|
||||||
|
'Cuando cambie la ubicación GPS, enviar un anuncio de cero saltos (requiere ubicación en anuncio).';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_multiAck => 'Múltiples respuestas de confirmación';
|
String get settings_multiAck => 'Múltiples respuestas de confirmación';
|
||||||
|
|
||||||
@@ -945,6 +953,28 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get appSettings_lastWeek => 'La semana pasada';
|
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
|
@override
|
||||||
String get appSettings_offlineMapCache => 'Caché de mapa sin conexión';
|
String get appSettings_offlineMapCache => 'Caché de mapa sin conexión';
|
||||||
|
|
||||||
@@ -1447,6 +1477,9 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get chat_sendGif => 'Enviar GIF';
|
String get chat_sendGif => 'Enviar GIF';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get chat_receivedGif => 'Received a GIF';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get chat_reply => 'Responder';
|
String get chat_reply => 'Responder';
|
||||||
|
|
||||||
@@ -2096,6 +2129,42 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||||||
return 'Descargas fallidas: $count';
|
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
|
@override
|
||||||
String mapCache_boundsLabel(
|
String mapCache_boundsLabel(
|
||||||
String north,
|
String north,
|
||||||
@@ -2646,6 +2715,41 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||||||
String get repeater_pathHashModeHelper =>
|
String get repeater_pathHashModeHelper =>
|
||||||
'Bytes utilizados para codificar el ID de este repetidor en las etiquetas de ruta/detección de bucles. 0=1 byte (256 IDs, hasta 64 saltos), 1=2 bytes (65.000 IDs, hasta 32 saltos), 2=3 bytes (16 millones de IDs, hasta 21 saltos). Las versiones 1.13 y anteriores de firmware eliminan rutas de múltiples bytes; solo se detectan una vez que la red está activa en la versión 1.14 o posterior.';
|
'Bytes utilizados para codificar el ID de este repetidor en las etiquetas de ruta/detección de bucles. 0=1 byte (256 IDs, hasta 64 saltos), 1=2 bytes (65.000 IDs, hasta 32 saltos), 2=3 bytes (16 millones de IDs, hasta 21 saltos). Las versiones 1.13 y anteriores de firmware eliminan rutas de múltiples bytes; solo se detectan una vez que la red está activa en la versión 1.14 o posterior.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_keySettings => 'Change Identity Keys';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_keySettingsSubtitle =>
|
||||||
|
'Change the public/private keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_prvKey => 'Private key';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_prvKeyHelper =>
|
||||||
|
'A new private key for the repeater, a 128-character hex string.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_generatePrvKey => 'Generate a random keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_stopGeneratingPrvKey => 'Interrupt search for keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKey => 'Public key';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKeyHelper =>
|
||||||
|
'This is the public key that goes with the generated private key. You can\'t set this directly.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKeyPrefix => 'Desired prefix';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String repeater_pubKeyPrefixHelper(int tries) {
|
||||||
|
return 'Find a public key that starts with these hex digits. Expected tries needed: $tries.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_txDelay => 'Retraso TX por inundación';
|
String get repeater_txDelay => 'Retraso TX por inundación';
|
||||||
|
|
||||||
|
|||||||
@@ -507,6 +507,14 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
String get settings_advertLocationSubtitle =>
|
String get settings_advertLocationSubtitle =>
|
||||||
'Inclure la localisation dans l\'annonce';
|
'Inclure la localisation dans l\'annonce';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_autoZeroHopAdvertOnGpsUpdate =>
|
||||||
|
'Annonce zéro saut automatique lors de la mise à jour GPS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_autoZeroHopAdvertOnGpsUpdateSubtitle =>
|
||||||
|
'Lorsque la position GPS change, envoyer une annonce zéro saut (nécessite la position dans l\'annonce).';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_multiAck => 'Plusieurs accusés de réception';
|
String get settings_multiAck => 'Plusieurs accusés de réception';
|
||||||
|
|
||||||
@@ -949,6 +957,28 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get appSettings_lastWeek => 'La semaine dernière';
|
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
|
@override
|
||||||
String get appSettings_offlineMapCache => 'Cache de carte hors ligne';
|
String get appSettings_offlineMapCache => 'Cache de carte hors ligne';
|
||||||
|
|
||||||
@@ -1449,6 +1479,9 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get chat_sendGif => 'Envoyer un GIF';
|
String get chat_sendGif => 'Envoyer un GIF';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get chat_receivedGif => 'Received a GIF';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get chat_reply => 'Répondre';
|
String get chat_reply => 'Répondre';
|
||||||
|
|
||||||
@@ -2103,6 +2136,42 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
return 'Téléchargements échoués : $count';
|
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
|
@override
|
||||||
String mapCache_boundsLabel(
|
String mapCache_boundsLabel(
|
||||||
String north,
|
String north,
|
||||||
@@ -2657,6 +2726,41 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
String get repeater_pathHashModeHelper =>
|
String get repeater_pathHashModeHelper =>
|
||||||
'Octets utilisés pour encoder l\'ID de ce routeur dans les balises de détection de flux/boucles. 0 = 1 octet (256 ID, jusqu\'à 64 sauts), 1 = 2 octets (65 000 ID, jusqu\'à 32 sauts), 2 = 3 octets (16 millions d\'ID, jusqu\'à 21 sauts). Les versions 1.13 et antérieures utilisent des chemins multi-octets ; à partir de la version 1.14, cela n\'est plus nécessaire.';
|
'Octets utilisés pour encoder l\'ID de ce routeur dans les balises de détection de flux/boucles. 0 = 1 octet (256 ID, jusqu\'à 64 sauts), 1 = 2 octets (65 000 ID, jusqu\'à 32 sauts), 2 = 3 octets (16 millions d\'ID, jusqu\'à 21 sauts). Les versions 1.13 et antérieures utilisent des chemins multi-octets ; à partir de la version 1.14, cela n\'est plus nécessaire.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_keySettings => 'Change Identity Keys';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_keySettingsSubtitle =>
|
||||||
|
'Change the public/private keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_prvKey => 'Private key';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_prvKeyHelper =>
|
||||||
|
'A new private key for the repeater, a 128-character hex string.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_generatePrvKey => 'Generate a random keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_stopGeneratingPrvKey => 'Interrupt search for keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKey => 'Public key';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKeyHelper =>
|
||||||
|
'This is the public key that goes with the generated private key. You can\'t set this directly.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKeyPrefix => 'Desired prefix';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String repeater_pubKeyPrefixHelper(int tries) {
|
||||||
|
return 'Find a public key that starts with these hex digits. Expected tries needed: $tries.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_txDelay => 'Délai de transmission en flood';
|
String get repeater_txDelay => 'Délai de transmission en flood';
|
||||||
|
|
||||||
|
|||||||
@@ -502,6 +502,14 @@ class AppLocalizationsHu extends AppLocalizations {
|
|||||||
String get settings_advertLocationSubtitle =>
|
String get settings_advertLocationSubtitle =>
|
||||||
'A hirdetésben szerepeljen a helyszín.';
|
'A hirdetésben szerepeljen a helyszín.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_autoZeroHopAdvertOnGpsUpdate =>
|
||||||
|
'Automatikus zero-hop hirdetés GPS-frissítéskor';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_autoZeroHopAdvertOnGpsUpdateSubtitle =>
|
||||||
|
'Ha a GPS-helyzet megváltozik, küldjön zero-hop hirdetést (a hirdetésben helymegadás szükséges).';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_multiAck => 'Multi-ACK';
|
String get settings_multiAck => 'Multi-ACK';
|
||||||
|
|
||||||
@@ -941,6 +949,28 @@ class AppLocalizationsHu extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get appSettings_lastWeek => 'Múlt héten';
|
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
|
@override
|
||||||
String get appSettings_offlineMapCache => 'Offline térképgyorsítótár';
|
String get appSettings_offlineMapCache => 'Offline térképgyorsítótár';
|
||||||
|
|
||||||
@@ -1442,6 +1472,9 @@ class AppLocalizationsHu extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get chat_sendGif => 'GIF küldése';
|
String get chat_sendGif => 'GIF küldése';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get chat_receivedGif => 'Received a GIF';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get chat_reply => 'Válasz';
|
String get chat_reply => 'Válasz';
|
||||||
|
|
||||||
@@ -2094,6 +2127,42 @@ class AppLocalizationsHu extends AppLocalizations {
|
|||||||
return 'Sikertelen letöltések: $count';
|
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
|
@override
|
||||||
String mapCache_boundsLabel(
|
String mapCache_boundsLabel(
|
||||||
String north,
|
String north,
|
||||||
@@ -2640,6 +2709,41 @@ class AppLocalizationsHu extends AppLocalizations {
|
|||||||
String get repeater_pathHashModeHelper =>
|
String get repeater_pathHashModeHelper =>
|
||||||
'Az átjátszó azonosítójának elárasztási útvonal/hurokészlelési címkékbe való kódolására használt bájtok. 0 = 1 bájt (256 azonosító, legfeljebb 64 ugrás), 1 = 2 bájt (65 000 azonosító, legfeljebb 32 ugrás), 2 = 3 bájt (16 millió azonosító, legfeljebb 21 ugrás). A v1.13 és régebbi firmware eldobja a többbájtos elérési utat – csak akkor emelje meg, ha a hálózat a v1.14+ verziót használja.';
|
'Az átjátszó azonosítójának elárasztási útvonal/hurokészlelési címkékbe való kódolására használt bájtok. 0 = 1 bájt (256 azonosító, legfeljebb 64 ugrás), 1 = 2 bájt (65 000 azonosító, legfeljebb 32 ugrás), 2 = 3 bájt (16 millió azonosító, legfeljebb 21 ugrás). A v1.13 és régebbi firmware eldobja a többbájtos elérési utat – csak akkor emelje meg, ha a hálózat a v1.14+ verziót használja.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_keySettings => 'Change Identity Keys';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_keySettingsSubtitle =>
|
||||||
|
'Change the public/private keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_prvKey => 'Private key';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_prvKeyHelper =>
|
||||||
|
'A new private key for the repeater, a 128-character hex string.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_generatePrvKey => 'Generate a random keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_stopGeneratingPrvKey => 'Interrupt search for keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKey => 'Public key';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKeyHelper =>
|
||||||
|
'This is the public key that goes with the generated private key. You can\'t set this directly.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKeyPrefix => 'Desired prefix';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String repeater_pubKeyPrefixHelper(int tries) {
|
||||||
|
return 'Find a public key that starts with these hex digits. Expected tries needed: $tries.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_txDelay => 'Áradási TX késleltetés';
|
String get repeater_txDelay => 'Áradási TX késleltetés';
|
||||||
|
|
||||||
|
|||||||
@@ -507,6 +507,14 @@ class AppLocalizationsIt extends AppLocalizations {
|
|||||||
String get settings_advertLocationSubtitle =>
|
String get settings_advertLocationSubtitle =>
|
||||||
'Includi la posizione nell\'annuncio';
|
'Includi la posizione nell\'annuncio';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_autoZeroHopAdvertOnGpsUpdate =>
|
||||||
|
'Annuncio zero-hop automatico all\'aggiornamento GPS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_autoZeroHopAdvertOnGpsUpdateSubtitle =>
|
||||||
|
'Quando la posizione GPS cambia, invia un annuncio zero-hop (richiede la posizione nell\'annuncio).';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_multiAck => 'ACK multipli';
|
String get settings_multiAck => 'ACK multipli';
|
||||||
|
|
||||||
@@ -948,6 +956,28 @@ class AppLocalizationsIt extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get appSettings_lastWeek => 'La settimana scorsa';
|
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
|
@override
|
||||||
String get appSettings_offlineMapCache => 'Cache Mappa Offline';
|
String get appSettings_offlineMapCache => 'Cache Mappa Offline';
|
||||||
|
|
||||||
@@ -1448,6 +1478,9 @@ class AppLocalizationsIt extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get chat_sendGif => 'Invia GIF';
|
String get chat_sendGif => 'Invia GIF';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get chat_receivedGif => 'Received a GIF';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get chat_reply => 'Rispondi';
|
String get chat_reply => 'Rispondi';
|
||||||
|
|
||||||
@@ -2098,6 +2131,42 @@ class AppLocalizationsIt extends AppLocalizations {
|
|||||||
return 'Download falliti: $count';
|
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
|
@override
|
||||||
String mapCache_boundsLabel(
|
String mapCache_boundsLabel(
|
||||||
String north,
|
String north,
|
||||||
@@ -2648,6 +2717,41 @@ class AppLocalizationsIt extends AppLocalizations {
|
|||||||
String get repeater_pathHashModeHelper =>
|
String get repeater_pathHashModeHelper =>
|
||||||
'Byte utilizzati per codificare l\'ID di questo ripetitore nei tag di percorso flood/rilevamento loop. 0=1 byte (256 ID, fino a 64 salti), 1=2 byte (65.000 ID, fino a 32 salti), 2=3 byte (16 milioni di ID, fino a 21 salti). Il firmware precedente alla v1.14 usava sempre percorsi a 1 byte; v1.14 e versioni successive possono essere configurate per percorsi a 2 o 3 byte.';
|
'Byte utilizzati per codificare l\'ID di questo ripetitore nei tag di percorso flood/rilevamento loop. 0=1 byte (256 ID, fino a 64 salti), 1=2 byte (65.000 ID, fino a 32 salti), 2=3 byte (16 milioni di ID, fino a 21 salti). Il firmware precedente alla v1.14 usava sempre percorsi a 1 byte; v1.14 e versioni successive possono essere configurate per percorsi a 2 o 3 byte.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_keySettings => 'Change Identity Keys';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_keySettingsSubtitle =>
|
||||||
|
'Change the public/private keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_prvKey => 'Private key';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_prvKeyHelper =>
|
||||||
|
'A new private key for the repeater, a 128-character hex string.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_generatePrvKey => 'Generate a random keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_stopGeneratingPrvKey => 'Interrupt search for keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKey => 'Public key';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKeyHelper =>
|
||||||
|
'This is the public key that goes with the generated private key. You can\'t set this directly.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKeyPrefix => 'Desired prefix';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String repeater_pubKeyPrefixHelper(int tries) {
|
||||||
|
return 'Find a public key that starts with these hex digits. Expected tries needed: $tries.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_txDelay => 'Ritardo a Flood, TX';
|
String get repeater_txDelay => 'Ritardo a Flood, TX';
|
||||||
|
|
||||||
|
|||||||
@@ -482,6 +482,13 @@ class AppLocalizationsJa extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get settings_advertLocationSubtitle => '広告に場所を記載してください。';
|
String get settings_advertLocationSubtitle => '広告に場所を記載してください。';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_autoZeroHopAdvertOnGpsUpdate => 'GPS更新時にゼロホップ広告を自動送信';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_autoZeroHopAdvertOnGpsUpdateSubtitle =>
|
||||||
|
'GPS位置が変化したときにゼロホップ広告を送信します(広告への位置情報の含有が必要)。';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_multiAck => 'マルチ ACK';
|
String get settings_multiAck => 'マルチ ACK';
|
||||||
|
|
||||||
@@ -896,6 +903,27 @@ class AppLocalizationsJa extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get appSettings_lastWeek => '過去1週間';
|
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
|
@override
|
||||||
String get appSettings_offlineMapCache => 'オフライン地図キャッシュ';
|
String get appSettings_offlineMapCache => 'オフライン地図キャッシュ';
|
||||||
|
|
||||||
@@ -1379,6 +1407,9 @@ class AppLocalizationsJa extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get chat_sendGif => 'GIF を送信';
|
String get chat_sendGif => 'GIF を送信';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get chat_receivedGif => 'Received a GIF';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get chat_reply => '返信';
|
String get chat_reply => '返信';
|
||||||
|
|
||||||
@@ -2011,6 +2042,42 @@ class AppLocalizationsJa extends AppLocalizations {
|
|||||||
return '失敗したダウンロード: $count';
|
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
|
@override
|
||||||
String mapCache_boundsLabel(
|
String mapCache_boundsLabel(
|
||||||
String north,
|
String north,
|
||||||
@@ -2531,6 +2598,41 @@ class AppLocalizationsJa extends AppLocalizations {
|
|||||||
String get repeater_pathHashModeHelper =>
|
String get repeater_pathHashModeHelper =>
|
||||||
'このリピータのIDをフローパス/ループ検出タグにエンコードするために使用されるバイト数。 0=1バイト (256個のID、最大64ホップ)、1=2バイト (65,000個のID、最大32ホップ)、2=3バイト (160万個のID、最大21ホップ)。 v1.13およびそれ以前のファームウェアでは、マルチバイトパスがサポートされていません。 v1.14以降のバージョンでは、一度ネットワークが起動されると、パスが一度だけ検出されます。';
|
'このリピータのIDをフローパス/ループ検出タグにエンコードするために使用されるバイト数。 0=1バイト (256個のID、最大64ホップ)、1=2バイト (65,000個のID、最大32ホップ)、2=3バイト (160万個のID、最大21ホップ)。 v1.13およびそれ以前のファームウェアでは、マルチバイトパスがサポートされていません。 v1.14以降のバージョンでは、一度ネットワークが起動されると、パスが一度だけ検出されます。';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_keySettings => 'Change Identity Keys';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_keySettingsSubtitle =>
|
||||||
|
'Change the public/private keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_prvKey => 'Private key';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_prvKeyHelper =>
|
||||||
|
'A new private key for the repeater, a 128-character hex string.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_generatePrvKey => 'Generate a random keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_stopGeneratingPrvKey => 'Interrupt search for keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKey => 'Public key';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKeyHelper =>
|
||||||
|
'This is the public key that goes with the generated private key. You can\'t set this directly.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKeyPrefix => 'Desired prefix';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String repeater_pubKeyPrefixHelper(int tries) {
|
||||||
|
return 'Find a public key that starts with these hex digits. Expected tries needed: $tries.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_txDelay => 'フロイド・TXでの遅延';
|
String get repeater_txDelay => 'フロイド・TXでの遅延';
|
||||||
|
|
||||||
|
|||||||
@@ -483,6 +483,14 @@ class AppLocalizationsKo extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get settings_advertLocationSubtitle => '광고에 위치 정보를 포함하세요.';
|
String get settings_advertLocationSubtitle => '광고에 위치 정보를 포함하세요.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_autoZeroHopAdvertOnGpsUpdate =>
|
||||||
|
'GPS 업데이트 시 제로 홉 광고 자동 전송';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_autoZeroHopAdvertOnGpsUpdateSubtitle =>
|
||||||
|
'GPS 위치가 변경되면 제로 홉 광고를 전송합니다(광고에 위치 포함 필요).';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_multiAck => '다중 ACK';
|
String get settings_multiAck => '다중 ACK';
|
||||||
|
|
||||||
@@ -898,6 +906,27 @@ class AppLocalizationsKo extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get appSettings_lastWeek => '지난 주';
|
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
|
@override
|
||||||
String get appSettings_offlineMapCache => '오프라인 지도 캐시';
|
String get appSettings_offlineMapCache => '오프라인 지도 캐시';
|
||||||
|
|
||||||
@@ -1381,6 +1410,9 @@ class AppLocalizationsKo extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get chat_sendGif => 'GIF 보내기';
|
String get chat_sendGif => 'GIF 보내기';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get chat_receivedGif => 'Received a GIF';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get chat_reply => '답변';
|
String get chat_reply => '답변';
|
||||||
|
|
||||||
@@ -2014,6 +2046,42 @@ class AppLocalizationsKo extends AppLocalizations {
|
|||||||
return '실패한 다운로드: $count';
|
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
|
@override
|
||||||
String mapCache_boundsLabel(
|
String mapCache_boundsLabel(
|
||||||
String north,
|
String north,
|
||||||
@@ -2536,6 +2604,41 @@ class AppLocalizationsKo extends AppLocalizations {
|
|||||||
String get repeater_pathHashModeHelper =>
|
String get repeater_pathHashModeHelper =>
|
||||||
'이 리피터의 ID를 플러드 경로/루프 감지 태그에 인코딩하는 데 사용되는 바이트 수입니다. 0=1바이트(256개 ID, 최대 64홉), 1=2바이트(65,000개 ID, 최대 32홉), 2=3바이트(1,600만 개 ID, 최대 21홉). v1.14 이전 펌웨어는 항상 1바이트 경로를 사용했으며, v1.14 이상은 2바이트 또는 3바이트 경로로 설정할 수 있습니다.';
|
'이 리피터의 ID를 플러드 경로/루프 감지 태그에 인코딩하는 데 사용되는 바이트 수입니다. 0=1바이트(256개 ID, 최대 64홉), 1=2바이트(65,000개 ID, 최대 32홉), 2=3바이트(1,600만 개 ID, 최대 21홉). v1.14 이전 펌웨어는 항상 1바이트 경로를 사용했으며, v1.14 이상은 2바이트 또는 3바이트 경로로 설정할 수 있습니다.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_keySettings => 'Change Identity Keys';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_keySettingsSubtitle =>
|
||||||
|
'Change the public/private keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_prvKey => 'Private key';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_prvKeyHelper =>
|
||||||
|
'A new private key for the repeater, a 128-character hex string.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_generatePrvKey => 'Generate a random keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_stopGeneratingPrvKey => 'Interrupt search for keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKey => 'Public key';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKeyHelper =>
|
||||||
|
'This is the public key that goes with the generated private key. You can\'t set this directly.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKeyPrefix => 'Desired prefix';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String repeater_pubKeyPrefixHelper(int tries) {
|
||||||
|
return 'Find a public key that starts with these hex digits. Expected tries needed: $tries.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_txDelay => '플러드 TX 지연';
|
String get repeater_txDelay => '플러드 TX 지연';
|
||||||
|
|
||||||
|
|||||||
@@ -501,6 +501,14 @@ class AppLocalizationsNl extends AppLocalizations {
|
|||||||
String get settings_advertLocationSubtitle =>
|
String get settings_advertLocationSubtitle =>
|
||||||
'Locatie opnemen in advertentie';
|
'Locatie opnemen in advertentie';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_autoZeroHopAdvertOnGpsUpdate =>
|
||||||
|
'Automatische zero-hop-advertentie bij GPS-update';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_autoZeroHopAdvertOnGpsUpdateSubtitle =>
|
||||||
|
'Wanneer de GPS-locatie verandert, een zero-hop-advertentie verzenden (vereist locatie in advertentie).';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_multiAck => 'Meerdere bevestigingen';
|
String get settings_multiAck => 'Meerdere bevestigingen';
|
||||||
|
|
||||||
@@ -940,6 +948,28 @@ class AppLocalizationsNl extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get appSettings_lastWeek => 'Afgelopen week';
|
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
|
@override
|
||||||
String get appSettings_offlineMapCache => 'Offline Kaartcache';
|
String get appSettings_offlineMapCache => 'Offline Kaartcache';
|
||||||
|
|
||||||
@@ -1436,6 +1466,9 @@ class AppLocalizationsNl extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get chat_sendGif => 'GIF verzenden';
|
String get chat_sendGif => 'GIF verzenden';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get chat_receivedGif => 'Received a GIF';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get chat_reply => 'Reageren';
|
String get chat_reply => 'Reageren';
|
||||||
|
|
||||||
@@ -2086,6 +2119,42 @@ class AppLocalizationsNl extends AppLocalizations {
|
|||||||
return 'Mislukte downloads: $count';
|
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
|
@override
|
||||||
String mapCache_boundsLabel(
|
String mapCache_boundsLabel(
|
||||||
String north,
|
String north,
|
||||||
@@ -2629,6 +2698,41 @@ class AppLocalizationsNl extends AppLocalizations {
|
|||||||
String get repeater_pathHashModeHelper =>
|
String get repeater_pathHashModeHelper =>
|
||||||
'Bytes die gebruikt worden om de ID van deze repeater te coderen in flood-pad/lusdetectietags. 0=1 byte (256 ID\'s, tot 64 hops), 1=2 bytes (65.000 ID\'s, tot 32 hops), 2=3 bytes (16 miljoen ID\'s, tot 21 hops). Firmware vóór v1.14 gebruikte altijd 1-byte paden; v1.14 en nieuwer kunnen worden ingesteld op 2- of 3-byte paden.';
|
'Bytes die gebruikt worden om de ID van deze repeater te coderen in flood-pad/lusdetectietags. 0=1 byte (256 ID\'s, tot 64 hops), 1=2 bytes (65.000 ID\'s, tot 32 hops), 2=3 bytes (16 miljoen ID\'s, tot 21 hops). Firmware vóór v1.14 gebruikte altijd 1-byte paden; v1.14 en nieuwer kunnen worden ingesteld op 2- of 3-byte paden.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_keySettings => 'Change Identity Keys';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_keySettingsSubtitle =>
|
||||||
|
'Change the public/private keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_prvKey => 'Private key';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_prvKeyHelper =>
|
||||||
|
'A new private key for the repeater, a 128-character hex string.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_generatePrvKey => 'Generate a random keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_stopGeneratingPrvKey => 'Interrupt search for keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKey => 'Public key';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKeyHelper =>
|
||||||
|
'This is the public key that goes with the generated private key. You can\'t set this directly.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKeyPrefix => 'Desired prefix';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String repeater_pubKeyPrefixHelper(int tries) {
|
||||||
|
return 'Find a public key that starts with these hex digits. Expected tries needed: $tries.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_txDelay => 'Vertraging bij Flood TX';
|
String get repeater_txDelay => 'Vertraging bij Flood TX';
|
||||||
|
|
||||||
|
|||||||
@@ -508,6 +508,14 @@ class AppLocalizationsPl extends AppLocalizations {
|
|||||||
String get settings_advertLocationSubtitle =>
|
String get settings_advertLocationSubtitle =>
|
||||||
'Uwzględnij lokalizację w ogłoszeniu';
|
'Uwzględnij lokalizację w ogłoszeniu';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_autoZeroHopAdvertOnGpsUpdate =>
|
||||||
|
'Automatyczne ogłoszenie zero-hop przy aktualizacji GPS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_autoZeroHopAdvertOnGpsUpdateSubtitle =>
|
||||||
|
'Gdy lokalizacja GPS się zmieni, wyślij ogłoszenie zero-hop (wymaga lokalizacji w ogłoszeniu).';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_multiAck => 'Wielokrotne potwierdzenia odbioru';
|
String get settings_multiAck => 'Wielokrotne potwierdzenia odbioru';
|
||||||
|
|
||||||
@@ -949,6 +957,28 @@ class AppLocalizationsPl extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get appSettings_lastWeek => 'Ostatni tydzień';
|
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
|
@override
|
||||||
String get appSettings_offlineMapCache => 'Pamięć podręczna map offline';
|
String get appSettings_offlineMapCache => 'Pamięć podręczna map offline';
|
||||||
|
|
||||||
@@ -1459,6 +1489,9 @@ class AppLocalizationsPl extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get chat_sendGif => 'Wyślij GIF';
|
String get chat_sendGif => 'Wyślij GIF';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get chat_receivedGif => 'Received a GIF';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get chat_reply => 'Odpowiedz';
|
String get chat_reply => 'Odpowiedz';
|
||||||
|
|
||||||
@@ -2116,6 +2149,42 @@ class AppLocalizationsPl extends AppLocalizations {
|
|||||||
return 'Nieudane pobrania: $count';
|
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
|
@override
|
||||||
String mapCache_boundsLabel(
|
String mapCache_boundsLabel(
|
||||||
String north,
|
String north,
|
||||||
@@ -2662,6 +2731,41 @@ class AppLocalizationsPl extends AppLocalizations {
|
|||||||
String get repeater_pathHashModeHelper =>
|
String get repeater_pathHashModeHelper =>
|
||||||
'Bajty używane do kodowania identyfikatora tego repeatera w tagach ścieżki flood/wykrywania pętli. 0=1 bajt (256 identyfikatorów, do 64 skoków), 1=2 bajty (65 000 identyfikatorów, do 32 skoków), 2=3 bajty (16 milionów identyfikatorów, do 21 skoków). Firmware sprzed v1.14 zawsze używał ścieżek 1-bajtowych; v1.14 i nowsze można skonfigurować na ścieżki 2- lub 3-bajtowe.';
|
'Bajty używane do kodowania identyfikatora tego repeatera w tagach ścieżki flood/wykrywania pętli. 0=1 bajt (256 identyfikatorów, do 64 skoków), 1=2 bajty (65 000 identyfikatorów, do 32 skoków), 2=3 bajty (16 milionów identyfikatorów, do 21 skoków). Firmware sprzed v1.14 zawsze używał ścieżek 1-bajtowych; v1.14 i nowsze można skonfigurować na ścieżki 2- lub 3-bajtowe.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_keySettings => 'Change Identity Keys';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_keySettingsSubtitle =>
|
||||||
|
'Change the public/private keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_prvKey => 'Private key';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_prvKeyHelper =>
|
||||||
|
'A new private key for the repeater, a 128-character hex string.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_generatePrvKey => 'Generate a random keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_stopGeneratingPrvKey => 'Interrupt search for keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKey => 'Public key';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKeyHelper =>
|
||||||
|
'This is the public key that goes with the generated private key. You can\'t set this directly.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKeyPrefix => 'Desired prefix';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String repeater_pubKeyPrefixHelper(int tries) {
|
||||||
|
return 'Find a public key that starts with these hex digits. Expected tries needed: $tries.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_txDelay => 'Opóźnienie w Flood, TX';
|
String get repeater_txDelay => 'Opóźnienie w Flood, TX';
|
||||||
|
|
||||||
|
|||||||
@@ -505,6 +505,14 @@ class AppLocalizationsPt extends AppLocalizations {
|
|||||||
String get settings_advertLocationSubtitle =>
|
String get settings_advertLocationSubtitle =>
|
||||||
'Incluir localização no anúncio';
|
'Incluir localização no anúncio';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_autoZeroHopAdvertOnGpsUpdate =>
|
||||||
|
'Anúncio zero-hop automático na atualização do GPS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_autoZeroHopAdvertOnGpsUpdateSubtitle =>
|
||||||
|
'Quando a localização GPS mudar, enviar um anúncio zero-hop (requer localização no anúncio).';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_multiAck => 'Multi-ACKs';
|
String get settings_multiAck => 'Multi-ACKs';
|
||||||
|
|
||||||
@@ -946,6 +954,28 @@ class AppLocalizationsPt extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get appSettings_lastWeek => 'Da última semana';
|
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
|
@override
|
||||||
String get appSettings_offlineMapCache => 'Cache de Mapa Offline';
|
String get appSettings_offlineMapCache => 'Cache de Mapa Offline';
|
||||||
|
|
||||||
@@ -1446,6 +1476,9 @@ class AppLocalizationsPt extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get chat_sendGif => 'Enviar GIF';
|
String get chat_sendGif => 'Enviar GIF';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get chat_receivedGif => 'Received a GIF';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get chat_reply => 'Responder';
|
String get chat_reply => 'Responder';
|
||||||
|
|
||||||
@@ -2099,6 +2132,42 @@ class AppLocalizationsPt extends AppLocalizations {
|
|||||||
return 'Downloads falhas: $count';
|
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
|
@override
|
||||||
String mapCache_boundsLabel(
|
String mapCache_boundsLabel(
|
||||||
String north,
|
String north,
|
||||||
@@ -2647,6 +2716,41 @@ class AppLocalizationsPt extends AppLocalizations {
|
|||||||
String get repeater_pathHashModeHelper =>
|
String get repeater_pathHashModeHelper =>
|
||||||
'Bytes usados para codificar o ID deste repetidor nas tags de caminho flood/detecção de loop. 0=1 byte (256 IDs, até 64 saltos), 1=2 bytes (65.000 IDs, até 32 saltos), 2=3 bytes (16 milhões de IDs, até 21 saltos). O firmware anterior à v1.14 sempre usava caminhos de 1 byte; v1.14 e versões mais recentes podem ser configuradas para caminhos de 2 ou 3 bytes.';
|
'Bytes usados para codificar o ID deste repetidor nas tags de caminho flood/detecção de loop. 0=1 byte (256 IDs, até 64 saltos), 1=2 bytes (65.000 IDs, até 32 saltos), 2=3 bytes (16 milhões de IDs, até 21 saltos). O firmware anterior à v1.14 sempre usava caminhos de 1 byte; v1.14 e versões mais recentes podem ser configuradas para caminhos de 2 ou 3 bytes.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_keySettings => 'Change Identity Keys';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_keySettingsSubtitle =>
|
||||||
|
'Change the public/private keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_prvKey => 'Private key';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_prvKeyHelper =>
|
||||||
|
'A new private key for the repeater, a 128-character hex string.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_generatePrvKey => 'Generate a random keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_stopGeneratingPrvKey => 'Interrupt search for keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKey => 'Public key';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKeyHelper =>
|
||||||
|
'This is the public key that goes with the generated private key. You can\'t set this directly.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKeyPrefix => 'Desired prefix';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String repeater_pubKeyPrefixHelper(int tries) {
|
||||||
|
return 'Find a public key that starts with these hex digits. Expected tries needed: $tries.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_txDelay => 'Atraso na entrega em Flood, TX';
|
String get repeater_txDelay => 'Atraso na entrega em Flood, TX';
|
||||||
|
|
||||||
|
|||||||
@@ -505,6 +505,14 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||||||
String get settings_advertLocationSubtitle =>
|
String get settings_advertLocationSubtitle =>
|
||||||
'Включить местоположение в объявление';
|
'Включить местоположение в объявление';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_autoZeroHopAdvertOnGpsUpdate =>
|
||||||
|
'Авто-объявление без хопов при обновлении GPS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_autoZeroHopAdvertOnGpsUpdateSubtitle =>
|
||||||
|
'Когда GPS-местоположение меняется, отправлять объявление без хопов (требуется геопозиция в объявлении).';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_multiAck => 'Несколько подтверждений';
|
String get settings_multiAck => 'Несколько подтверждений';
|
||||||
|
|
||||||
@@ -949,6 +957,28 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get appSettings_lastWeek => 'Последнюю неделю';
|
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
|
@override
|
||||||
String get appSettings_offlineMapCache => 'Кэш офлайн-карты';
|
String get appSettings_offlineMapCache => 'Кэш офлайн-карты';
|
||||||
|
|
||||||
@@ -1447,6 +1477,9 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get chat_sendGif => 'Отправить GIF';
|
String get chat_sendGif => 'Отправить GIF';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get chat_receivedGif => 'Received a GIF';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get chat_reply => 'Ответить';
|
String get chat_reply => 'Ответить';
|
||||||
|
|
||||||
@@ -2103,6 +2136,42 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||||||
return 'Неудачных загрузок: $count';
|
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
|
@override
|
||||||
String mapCache_boundsLabel(
|
String mapCache_boundsLabel(
|
||||||
String north,
|
String north,
|
||||||
@@ -2652,6 +2721,41 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||||||
String get repeater_pathHashModeHelper =>
|
String get repeater_pathHashModeHelper =>
|
||||||
'Байты, используемые для кодирования идентификатора этого ретранслятора в тегах flood-маршрута/обнаружения циклов. 0 = 1 байт (256 идентификаторов, до 64 переходов), 1 = 2 байта (65 000 идентификаторов, до 32 переходов), 2 = 3 байта (16 миллионов идентификаторов, до 21 перехода). Прошивки до v1.14 всегда использовали 1-байтовые маршруты; v1.14 и новее можно настроить на 2- или 3-байтовые маршруты.';
|
'Байты, используемые для кодирования идентификатора этого ретранслятора в тегах flood-маршрута/обнаружения циклов. 0 = 1 байт (256 идентификаторов, до 64 переходов), 1 = 2 байта (65 000 идентификаторов, до 32 переходов), 2 = 3 байта (16 миллионов идентификаторов, до 21 перехода). Прошивки до v1.14 всегда использовали 1-байтовые маршруты; v1.14 и новее можно настроить на 2- или 3-байтовые маршруты.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_keySettings => 'Change Identity Keys';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_keySettingsSubtitle =>
|
||||||
|
'Change the public/private keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_prvKey => 'Private key';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_prvKeyHelper =>
|
||||||
|
'A new private key for the repeater, a 128-character hex string.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_generatePrvKey => 'Generate a random keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_stopGeneratingPrvKey => 'Interrupt search for keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKey => 'Public key';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKeyHelper =>
|
||||||
|
'This is the public key that goes with the generated private key. You can\'t set this directly.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKeyPrefix => 'Desired prefix';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String repeater_pubKeyPrefixHelper(int tries) {
|
||||||
|
return 'Find a public key that starts with these hex digits. Expected tries needed: $tries.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_txDelay => 'Задержка в работе системы Flood TX';
|
String get repeater_txDelay => 'Задержка в работе системы Flood TX';
|
||||||
|
|
||||||
|
|||||||
@@ -499,6 +499,14 @@ class AppLocalizationsSk extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get settings_advertLocationSubtitle => 'Zahrnúť polohu do inzerátu';
|
String get settings_advertLocationSubtitle => 'Zahrnúť polohu do inzerátu';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_autoZeroHopAdvertOnGpsUpdate =>
|
||||||
|
'Automatický zero-hop inzerát pri aktualizácii GPS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_autoZeroHopAdvertOnGpsUpdateSubtitle =>
|
||||||
|
'Keď sa GPS poloha zmení, odoslať zero-hop inzerát (vyžaduje polohu v inzeráte).';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_multiAck => 'Viaceré ACK';
|
String get settings_multiAck => 'Viaceré ACK';
|
||||||
|
|
||||||
@@ -936,6 +944,28 @@ class AppLocalizationsSk extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get appSettings_lastWeek => 'Minul týždeň';
|
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
|
@override
|
||||||
String get appSettings_offlineMapCache => 'Offline Mapa Pamäť';
|
String get appSettings_offlineMapCache => 'Offline Mapa Pamäť';
|
||||||
|
|
||||||
@@ -1436,6 +1466,9 @@ class AppLocalizationsSk extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get chat_sendGif => 'Odoslať GIF';
|
String get chat_sendGif => 'Odoslať GIF';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get chat_receivedGif => 'Received a GIF';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get chat_reply => 'Odpovedať';
|
String get chat_reply => 'Odpovedať';
|
||||||
|
|
||||||
@@ -2089,6 +2122,42 @@ class AppLocalizationsSk extends AppLocalizations {
|
|||||||
return 'Neúspešné stiahnutia: $count';
|
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
|
@override
|
||||||
String mapCache_boundsLabel(
|
String mapCache_boundsLabel(
|
||||||
String north,
|
String north,
|
||||||
@@ -2633,6 +2702,41 @@ class AppLocalizationsSk extends AppLocalizations {
|
|||||||
String get repeater_pathHashModeHelper =>
|
String get repeater_pathHashModeHelper =>
|
||||||
'Bajty použité na zakódovanie ID tohto opakovača v tagoch flood trasy/detekcie slučky. 0=1 bajt (256 ID, až 64 skokov), 1=2 bajty (65 000 ID, až 32 skokov), 2=3 bajty (16 miliónov ID, až 21 skokov). Firmvér pred v1.14 vždy používal 1-bajtové trasy; v1.14 a novšie možno nakonfigurovať na 2- alebo 3-bajtové trasy.';
|
'Bajty použité na zakódovanie ID tohto opakovača v tagoch flood trasy/detekcie slučky. 0=1 bajt (256 ID, až 64 skokov), 1=2 bajty (65 000 ID, až 32 skokov), 2=3 bajty (16 miliónov ID, až 21 skokov). Firmvér pred v1.14 vždy používal 1-bajtové trasy; v1.14 a novšie možno nakonfigurovať na 2- alebo 3-bajtové trasy.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_keySettings => 'Change Identity Keys';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_keySettingsSubtitle =>
|
||||||
|
'Change the public/private keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_prvKey => 'Private key';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_prvKeyHelper =>
|
||||||
|
'A new private key for the repeater, a 128-character hex string.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_generatePrvKey => 'Generate a random keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_stopGeneratingPrvKey => 'Interrupt search for keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKey => 'Public key';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKeyHelper =>
|
||||||
|
'This is the public key that goes with the generated private key. You can\'t set this directly.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKeyPrefix => 'Desired prefix';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String repeater_pubKeyPrefixHelper(int tries) {
|
||||||
|
return 'Find a public key that starts with these hex digits. Expected tries needed: $tries.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_txDelay => 'Zpoždanie v Flood, TX';
|
String get repeater_txDelay => 'Zpoždanie v Flood, TX';
|
||||||
|
|
||||||
|
|||||||
@@ -500,6 +500,14 @@ class AppLocalizationsSl extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get settings_advertLocationSubtitle => 'Vključi lokacijo v oglas.';
|
String get settings_advertLocationSubtitle => 'Vključi lokacijo v oglas.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_autoZeroHopAdvertOnGpsUpdate =>
|
||||||
|
'Samodejni zero-hop oglas ob posodobitvi GPS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_autoZeroHopAdvertOnGpsUpdateSubtitle =>
|
||||||
|
'Ko se GPS lokacija spremeni, pošlji zero-hop oglas (zahteva lokacijo v oglasu).';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_multiAck => 'Več potrdil';
|
String get settings_multiAck => 'Več potrdil';
|
||||||
|
|
||||||
@@ -937,6 +945,28 @@ class AppLocalizationsSl extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get appSettings_lastWeek => 'Prejšnji teden';
|
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
|
@override
|
||||||
String get appSettings_offlineMapCache => 'Shramba zemljevidov brez povezave';
|
String get appSettings_offlineMapCache => 'Shramba zemljevidov brez povezave';
|
||||||
|
|
||||||
@@ -1435,6 +1465,9 @@ class AppLocalizationsSl extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get chat_sendGif => 'Pošlji GIF';
|
String get chat_sendGif => 'Pošlji GIF';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get chat_receivedGif => 'Received a GIF';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get chat_reply => 'Odgovori';
|
String get chat_reply => 'Odgovori';
|
||||||
|
|
||||||
@@ -2086,6 +2119,42 @@ class AppLocalizationsSl extends AppLocalizations {
|
|||||||
return 'Poslovniški izniki: $count';
|
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
|
@override
|
||||||
String mapCache_boundsLabel(
|
String mapCache_boundsLabel(
|
||||||
String north,
|
String north,
|
||||||
@@ -2629,6 +2698,41 @@ class AppLocalizationsSl extends AppLocalizations {
|
|||||||
String get repeater_pathHashModeHelper =>
|
String get repeater_pathHashModeHelper =>
|
||||||
'Bajti, uporabljeni za kodiranje ID-ja tega repetitorja v oznakah flood poti/zaznavanja zank. 0=1 bajt (256 ID-jev, do 64 skokov), 1=2 bajta (65.000 ID-jev, do 32 skokov), 2=3 bajti (16 milijonov ID-jev, do 21 skokov). Vdelana programska oprema pred v1.14 je vedno uporabljala 1-bajtne poti; v1.14 in novejše je mogoče nastaviti na 2- ali 3-bajtne poti.';
|
'Bajti, uporabljeni za kodiranje ID-ja tega repetitorja v oznakah flood poti/zaznavanja zank. 0=1 bajt (256 ID-jev, do 64 skokov), 1=2 bajta (65.000 ID-jev, do 32 skokov), 2=3 bajti (16 milijonov ID-jev, do 21 skokov). Vdelana programska oprema pred v1.14 je vedno uporabljala 1-bajtne poti; v1.14 in novejše je mogoče nastaviti na 2- ali 3-bajtne poti.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_keySettings => 'Change Identity Keys';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_keySettingsSubtitle =>
|
||||||
|
'Change the public/private keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_prvKey => 'Private key';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_prvKeyHelper =>
|
||||||
|
'A new private key for the repeater, a 128-character hex string.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_generatePrvKey => 'Generate a random keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_stopGeneratingPrvKey => 'Interrupt search for keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKey => 'Public key';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKeyHelper =>
|
||||||
|
'This is the public key that goes with the generated private key. You can\'t set this directly.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKeyPrefix => 'Desired prefix';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String repeater_pubKeyPrefixHelper(int tries) {
|
||||||
|
return 'Find a public key that starts with these hex digits. Expected tries needed: $tries.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_txDelay => 'Zatemnitevanje zaradi poplav v Texasu';
|
String get repeater_txDelay => 'Zatemnitevanje zaradi poplav v Texasu';
|
||||||
|
|
||||||
|
|||||||
@@ -497,6 +497,14 @@ class AppLocalizationsSv extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get settings_advertLocationSubtitle => 'Inkludera plats i annonsen';
|
String get settings_advertLocationSubtitle => 'Inkludera plats i annonsen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_autoZeroHopAdvertOnGpsUpdate =>
|
||||||
|
'Automatisk zero-hop-annons vid GPS-uppdatering';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_autoZeroHopAdvertOnGpsUpdateSubtitle =>
|
||||||
|
'När GPS-positionen ändras, skicka en zero-hop-annons (kräver plats i annonsen).';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_multiAck => 'Flera bekräftelser';
|
String get settings_multiAck => 'Flera bekräftelser';
|
||||||
|
|
||||||
@@ -930,6 +938,28 @@ class AppLocalizationsSv extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get appSettings_lastWeek => 'Förra veckan';
|
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
|
@override
|
||||||
String get appSettings_offlineMapCache => 'Offline Kartcache';
|
String get appSettings_offlineMapCache => 'Offline Kartcache';
|
||||||
|
|
||||||
@@ -1428,6 +1458,9 @@ class AppLocalizationsSv extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get chat_sendGif => 'Skicka GIF';
|
String get chat_sendGif => 'Skicka GIF';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get chat_receivedGif => 'Received a GIF';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get chat_reply => 'Svara';
|
String get chat_reply => 'Svara';
|
||||||
|
|
||||||
@@ -2074,6 +2107,42 @@ class AppLocalizationsSv extends AppLocalizations {
|
|||||||
return 'Misslyckade nedladdningar: $count';
|
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
|
@override
|
||||||
String mapCache_boundsLabel(
|
String mapCache_boundsLabel(
|
||||||
String north,
|
String north,
|
||||||
@@ -2617,6 +2686,41 @@ class AppLocalizationsSv extends AppLocalizations {
|
|||||||
String get repeater_pathHashModeHelper =>
|
String get repeater_pathHashModeHelper =>
|
||||||
'Byte som används för att koda denna repeaters ID i taggar för flood-väg/loopdetektering. 0=1 byte (256 ID:n, upp till 64 hopp), 1=2 byte (65 000 ID:n, upp till 32 hopp), 2=3 byte (16 miljoner ID:n, upp till 21 hopp). Firmware före v1.14 använde alltid 1-byte-vägar; v1.14 och nyare kan konfigureras för 2- eller 3-byte-vägar.';
|
'Byte som används för att koda denna repeaters ID i taggar för flood-väg/loopdetektering. 0=1 byte (256 ID:n, upp till 64 hopp), 1=2 byte (65 000 ID:n, upp till 32 hopp), 2=3 byte (16 miljoner ID:n, upp till 21 hopp). Firmware före v1.14 använde alltid 1-byte-vägar; v1.14 och nyare kan konfigureras för 2- eller 3-byte-vägar.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_keySettings => 'Change Identity Keys';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_keySettingsSubtitle =>
|
||||||
|
'Change the public/private keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_prvKey => 'Private key';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_prvKeyHelper =>
|
||||||
|
'A new private key for the repeater, a 128-character hex string.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_generatePrvKey => 'Generate a random keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_stopGeneratingPrvKey => 'Interrupt search for keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKey => 'Public key';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKeyHelper =>
|
||||||
|
'This is the public key that goes with the generated private key. You can\'t set this directly.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKeyPrefix => 'Desired prefix';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String repeater_pubKeyPrefixHelper(int tries) {
|
||||||
|
return 'Find a public key that starts with these hex digits. Expected tries needed: $tries.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_txDelay => 'Försening i Flood TX';
|
String get repeater_txDelay => 'Försening i Flood TX';
|
||||||
|
|
||||||
|
|||||||
@@ -501,6 +501,14 @@ class AppLocalizationsUk extends AppLocalizations {
|
|||||||
String get settings_advertLocationSubtitle =>
|
String get settings_advertLocationSubtitle =>
|
||||||
'Включити геопозицію в оголошення';
|
'Включити геопозицію в оголошення';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_autoZeroHopAdvertOnGpsUpdate =>
|
||||||
|
'Автооголошення без хопів при оновленні GPS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_autoZeroHopAdvertOnGpsUpdateSubtitle =>
|
||||||
|
'Коли GPS-локація змінюється, надсилати оголошення без хопів (потрібна геопозиція в оголошенні).';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_multiAck => 'Багато підтверджень';
|
String get settings_multiAck => 'Багато підтверджень';
|
||||||
|
|
||||||
@@ -943,6 +951,28 @@ class AppLocalizationsUk extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get appSettings_lastWeek => 'Минулий тиждень';
|
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
|
@override
|
||||||
String get appSettings_offlineMapCache => 'Офлайн-кеш карти';
|
String get appSettings_offlineMapCache => 'Офлайн-кеш карти';
|
||||||
|
|
||||||
@@ -1441,6 +1471,9 @@ class AppLocalizationsUk extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get chat_sendGif => 'Надіслати GIF';
|
String get chat_sendGif => 'Надіслати GIF';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get chat_receivedGif => 'Received a GIF';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get chat_reply => 'Відповісти';
|
String get chat_reply => 'Відповісти';
|
||||||
|
|
||||||
@@ -2096,6 +2129,42 @@ class AppLocalizationsUk extends AppLocalizations {
|
|||||||
return 'Невдалі завантаження: $count';
|
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
|
@override
|
||||||
String mapCache_boundsLabel(
|
String mapCache_boundsLabel(
|
||||||
String north,
|
String north,
|
||||||
@@ -2649,6 +2718,41 @@ class AppLocalizationsUk extends AppLocalizations {
|
|||||||
String get repeater_pathHashModeHelper =>
|
String get repeater_pathHashModeHelper =>
|
||||||
'Байти, що використовуються для кодування ідентифікатора цього ретранслятора в тегах flood-шляху/виявлення петель. 0=1 байт (256 ідентифікаторів, до 64 переходів), 1=2 байти (65 000 ідентифікаторів, до 32 переходів), 2=3 байти (16 мільйонів ідентифікаторів, до 21 переходу). Прошивки до v1.14 завжди використовували 1-байтові шляхи; v1.14 і новіші можна налаштувати на 2- або 3-байтові шляхи.';
|
'Байти, що використовуються для кодування ідентифікатора цього ретранслятора в тегах flood-шляху/виявлення петель. 0=1 байт (256 ідентифікаторів, до 64 переходів), 1=2 байти (65 000 ідентифікаторів, до 32 переходів), 2=3 байти (16 мільйонів ідентифікаторів, до 21 переходу). Прошивки до v1.14 завжди використовували 1-байтові шляхи; v1.14 і новіші можна налаштувати на 2- або 3-байтові шляхи.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_keySettings => 'Change Identity Keys';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_keySettingsSubtitle =>
|
||||||
|
'Change the public/private keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_prvKey => 'Private key';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_prvKeyHelper =>
|
||||||
|
'A new private key for the repeater, a 128-character hex string.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_generatePrvKey => 'Generate a random keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_stopGeneratingPrvKey => 'Interrupt search for keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKey => 'Public key';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKeyHelper =>
|
||||||
|
'This is the public key that goes with the generated private key. You can\'t set this directly.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKeyPrefix => 'Desired prefix';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String repeater_pubKeyPrefixHelper(int tries) {
|
||||||
|
return 'Find a public key that starts with these hex digits. Expected tries needed: $tries.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_txDelay => 'Затримка у Flood, штат Техас';
|
String get repeater_txDelay => 'Затримка у Flood, штат Техас';
|
||||||
|
|
||||||
|
|||||||
@@ -476,6 +476,13 @@ class AppLocalizationsZh extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get settings_advertLocationSubtitle => '在广告中包含位置';
|
String get settings_advertLocationSubtitle => '在广告中包含位置';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_autoZeroHopAdvertOnGpsUpdate => 'GPS 更新时自动发送零跳广告';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_autoZeroHopAdvertOnGpsUpdateSubtitle =>
|
||||||
|
'当 GPS 位置变化时,发送零跳广告(需要在广告中包含位置)。';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_multiAck => '多重ACK';
|
String get settings_multiAck => '多重ACK';
|
||||||
|
|
||||||
@@ -883,6 +890,27 @@ class AppLocalizationsZh extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get appSettings_lastWeek => '上周';
|
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
|
@override
|
||||||
String get appSettings_offlineMapCache => '离线地图缓存';
|
String get appSettings_offlineMapCache => '离线地图缓存';
|
||||||
|
|
||||||
@@ -1365,6 +1393,9 @@ class AppLocalizationsZh extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get chat_sendGif => '发送 GIF';
|
String get chat_sendGif => '发送 GIF';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get chat_receivedGif => 'Received a GIF';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get chat_reply => '回复';
|
String get chat_reply => '回复';
|
||||||
|
|
||||||
@@ -1990,6 +2021,42 @@ class AppLocalizationsZh extends AppLocalizations {
|
|||||||
return '下载失败:$count';
|
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
|
@override
|
||||||
String mapCache_boundsLabel(
|
String mapCache_boundsLabel(
|
||||||
String north,
|
String north,
|
||||||
@@ -2506,6 +2573,41 @@ class AppLocalizationsZh extends AppLocalizations {
|
|||||||
String get repeater_pathHashModeHelper =>
|
String get repeater_pathHashModeHelper =>
|
||||||
'用于在洪泛路径/环路检测标签中编码此中继器 ID 的字节数。0=1 字节(256 个 ID,最多 64 跳),1=2 字节(65K 个 ID,最多 32 跳),2=3 字节(16M 个 ID,最多 21 跳)。v1.14 之前的固件始终使用 1 字节路径;v1.14 及更新版本可配置为 2 或 3 字节路径。';
|
'用于在洪泛路径/环路检测标签中编码此中继器 ID 的字节数。0=1 字节(256 个 ID,最多 64 跳),1=2 字节(65K 个 ID,最多 32 跳),2=3 字节(16M 个 ID,最多 21 跳)。v1.14 之前的固件始终使用 1 字节路径;v1.14 及更新版本可配置为 2 或 3 字节路径。';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_keySettings => 'Change Identity Keys';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_keySettingsSubtitle =>
|
||||||
|
'Change the public/private keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_prvKey => 'Private key';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_prvKeyHelper =>
|
||||||
|
'A new private key for the repeater, a 128-character hex string.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_generatePrvKey => 'Generate a random keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_stopGeneratingPrvKey => 'Interrupt search for keypair';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKey => 'Public key';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKeyHelper =>
|
||||||
|
'This is the public key that goes with the generated private key. You can\'t set this directly.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get repeater_pubKeyPrefix => 'Desired prefix';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String repeater_pubKeyPrefixHelper(int tries) {
|
||||||
|
return 'Find a public key that starts with these hex digits. Expected tries needed: $tries.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_txDelay => '洪水(德克萨斯州)延误';
|
String get repeater_txDelay => '洪水(德克萨斯州)延误';
|
||||||
|
|
||||||
|
|||||||
@@ -100,6 +100,8 @@
|
|||||||
"settings_locationInvalid": "Ongeldige breedtegraad of lengtegraad.",
|
"settings_locationInvalid": "Ongeldige breedtegraad of lengtegraad.",
|
||||||
"settings_latitude": "Breedtegraad",
|
"settings_latitude": "Breedtegraad",
|
||||||
"settings_longitude": "Lengtegraad",
|
"settings_longitude": "Lengtegraad",
|
||||||
|
"settings_autoZeroHopAdvertOnGpsUpdate": "Automatische zero-hop-advertentie bij GPS-update",
|
||||||
|
"settings_autoZeroHopAdvertOnGpsUpdateSubtitle": "Wanneer de GPS-locatie verandert, een zero-hop-advertentie verzenden (vereist locatie in advertentie).",
|
||||||
"settings_privacyMode": "Privacy-modus",
|
"settings_privacyMode": "Privacy-modus",
|
||||||
"settings_privacyModeSubtitle": "Naam/locatie verbergen in advertenties",
|
"settings_privacyModeSubtitle": "Naam/locatie verbergen in advertenties",
|
||||||
"settings_privacyModeToggle": "Schakel privacy modus in om je naam en locatie in advertenties te verbergen.",
|
"settings_privacyModeToggle": "Schakel privacy modus in om je naam en locatie in advertenties te verbergen.",
|
||||||
@@ -240,6 +242,19 @@
|
|||||||
"appSettings_last6Hours": "Afgelopen 6 uur",
|
"appSettings_last6Hours": "Afgelopen 6 uur",
|
||||||
"appSettings_last24Hours": "Afgelopen 24 uur",
|
"appSettings_last24Hours": "Afgelopen 24 uur",
|
||||||
"appSettings_lastWeek": "Afgelopen week",
|
"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_offlineMapCache": "Offline Kaartcache",
|
||||||
"appSettings_noAreaSelected": "Geen gebied geselecteerd",
|
"appSettings_noAreaSelected": "Geen gebied geselecteerd",
|
||||||
"appSettings_areaSelectedZoom": "Geselecteerd gebied (zoom {minZoom}-{maxZoom})",
|
"appSettings_areaSelectedZoom": "Geselecteerd gebied (zoom {minZoom}-{maxZoom})",
|
||||||
@@ -769,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": "N {north}, S {south}, E {east}, W {west}",
|
||||||
"@mapCache_boundsLabel": {
|
"@mapCache_boundsLabel": {
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
|
|||||||
@@ -100,6 +100,8 @@
|
|||||||
"settings_locationInvalid": "Nieprawidłowa szerokość geograficzna lub długość geograficzna.",
|
"settings_locationInvalid": "Nieprawidłowa szerokość geograficzna lub długość geograficzna.",
|
||||||
"settings_latitude": "Szerokość",
|
"settings_latitude": "Szerokość",
|
||||||
"settings_longitude": "Długość",
|
"settings_longitude": "Długość",
|
||||||
|
"settings_autoZeroHopAdvertOnGpsUpdate": "Automatyczne ogłoszenie zero-hop przy aktualizacji GPS",
|
||||||
|
"settings_autoZeroHopAdvertOnGpsUpdateSubtitle": "Gdy lokalizacja GPS się zmieni, wyślij ogłoszenie zero-hop (wymaga lokalizacji w ogłoszeniu).",
|
||||||
"settings_privacyMode": "Tryb prywatności",
|
"settings_privacyMode": "Tryb prywatności",
|
||||||
"settings_privacyModeSubtitle": "Ukryj imię/lokalizację w rozgłoszeniach",
|
"settings_privacyModeSubtitle": "Ukryj imię/lokalizację w rozgłoszeniach",
|
||||||
"settings_privacyModeToggle": "Włącz tryb prywatności, aby ukryć swoje imię i lokalizację w rozgłoszeniach.",
|
"settings_privacyModeToggle": "Włącz tryb prywatności, aby ukryć swoje imię i lokalizację w rozgłoszeniach.",
|
||||||
@@ -240,6 +242,19 @@
|
|||||||
"appSettings_last6Hours": "Ostatnie 6 godzin",
|
"appSettings_last6Hours": "Ostatnie 6 godzin",
|
||||||
"appSettings_last24Hours": "Ostatnie 24 godziny",
|
"appSettings_last24Hours": "Ostatnie 24 godziny",
|
||||||
"appSettings_lastWeek": "Ostatni tydzień",
|
"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_offlineMapCache": "Pamięć podręczna map offline",
|
||||||
"appSettings_noAreaSelected": "Nie wybrano żadnego obszaru.",
|
"appSettings_noAreaSelected": "Nie wybrano żadnego obszaru.",
|
||||||
"appSettings_areaSelectedZoom": "Wybrany obszar (skala {minZoom}-{maxZoom})",
|
"appSettings_areaSelectedZoom": "Wybrany obszar (skala {minZoom}-{maxZoom})",
|
||||||
@@ -779,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": "N {north}, S {south}, E {east}, W {west}",
|
||||||
"@mapCache_boundsLabel": {
|
"@mapCache_boundsLabel": {
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
|
|||||||
@@ -100,6 +100,8 @@
|
|||||||
"settings_locationInvalid": "Latitude ou longitude inválidos.",
|
"settings_locationInvalid": "Latitude ou longitude inválidos.",
|
||||||
"settings_latitude": "Latitude",
|
"settings_latitude": "Latitude",
|
||||||
"settings_longitude": "Longitude",
|
"settings_longitude": "Longitude",
|
||||||
|
"settings_autoZeroHopAdvertOnGpsUpdate": "Anúncio zero-hop automático na atualização do GPS",
|
||||||
|
"settings_autoZeroHopAdvertOnGpsUpdateSubtitle": "Quando a localização GPS mudar, enviar um anúncio zero-hop (requer localização no anúncio).",
|
||||||
"settings_privacyMode": "Modo de Privacidade",
|
"settings_privacyMode": "Modo de Privacidade",
|
||||||
"settings_privacyModeSubtitle": "Esconder nome/localização em anúncios",
|
"settings_privacyModeSubtitle": "Esconder nome/localização em anúncios",
|
||||||
"settings_privacyModeToggle": "Ative o modo de privacidade para ocultar seu nome e localização em anúncios.",
|
"settings_privacyModeToggle": "Ative o modo de privacidade para ocultar seu nome e localização em anúncios.",
|
||||||
@@ -240,6 +242,19 @@
|
|||||||
"appSettings_last6Hours": "Últimos 6 horas",
|
"appSettings_last6Hours": "Últimos 6 horas",
|
||||||
"appSettings_last24Hours": "Últimas 24 horas",
|
"appSettings_last24Hours": "Últimas 24 horas",
|
||||||
"appSettings_lastWeek": "Da última semana",
|
"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_offlineMapCache": "Cache de Mapa Offline",
|
||||||
"appSettings_noAreaSelected": "Nenhuma área selecionada",
|
"appSettings_noAreaSelected": "Nenhuma área selecionada",
|
||||||
"appSettings_areaSelectedZoom": "Área selecionada (zoom {minZoom}-{maxZoom})",
|
"appSettings_areaSelectedZoom": "Área selecionada (zoom {minZoom}-{maxZoom})",
|
||||||
@@ -769,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": "N {north}, S {south}, E {east}, W {west}",
|
||||||
"@mapCache_boundsLabel": {
|
"@mapCache_boundsLabel": {
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
|
|||||||
@@ -77,6 +77,8 @@
|
|||||||
"settings_locationIntervalInvalid": "Интервал должен составлять не менее 60 секунд и не более 86400 секунд.",
|
"settings_locationIntervalInvalid": "Интервал должен составлять не менее 60 секунд и не более 86400 секунд.",
|
||||||
"settings_latitude": "Широта",
|
"settings_latitude": "Широта",
|
||||||
"settings_longitude": "Долгота",
|
"settings_longitude": "Долгота",
|
||||||
|
"settings_autoZeroHopAdvertOnGpsUpdate": "Авто-объявление без хопов при обновлении GPS",
|
||||||
|
"settings_autoZeroHopAdvertOnGpsUpdateSubtitle": "Когда GPS-местоположение меняется, отправлять объявление без хопов (требуется геопозиция в объявлении).",
|
||||||
"settings_privacyMode": "Режим конфиденциальности",
|
"settings_privacyMode": "Режим конфиденциальности",
|
||||||
"settings_privacyModeSubtitle": "Скрыть имя/позицию в анонсировании",
|
"settings_privacyModeSubtitle": "Скрыть имя/позицию в анонсировании",
|
||||||
"settings_privacyModeToggle": "Включите режим конфиденциальности, чтобы скрыть свое имя и местоположение в анонсировании.",
|
"settings_privacyModeToggle": "Включите режим конфиденциальности, чтобы скрыть свое имя и местоположение в анонсировании.",
|
||||||
@@ -190,6 +192,19 @@
|
|||||||
"appSettings_last6Hours": "Последние 6 часов",
|
"appSettings_last6Hours": "Последние 6 часов",
|
||||||
"appSettings_last24Hours": "Последние 24 часа",
|
"appSettings_last24Hours": "Последние 24 часа",
|
||||||
"appSettings_lastWeek": "Последнюю неделю",
|
"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_offlineMapCache": "Кэш офлайн-карты",
|
||||||
"appSettings_noAreaSelected": "Область не выбрана",
|
"appSettings_noAreaSelected": "Область не выбрана",
|
||||||
"appSettings_areaSelectedZoom": "Область выбрана (масштаб {minZoom}–{maxZoom})",
|
"appSettings_areaSelectedZoom": "Область выбрана (масштаб {minZoom}–{maxZoom})",
|
||||||
@@ -438,6 +453,56 @@
|
|||||||
"mapCache_downloadTilesButton": "Загрузить плитки",
|
"mapCache_downloadTilesButton": "Загрузить плитки",
|
||||||
"mapCache_clearCacheButton": "Очистить кэш",
|
"mapCache_clearCacheButton": "Очистить кэш",
|
||||||
"mapCache_failedDownloads": "Неудачных загрузок: {count}",
|
"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}",
|
"mapCache_boundsLabel": "С {north}, Ю {south}, В {east}, З {west}",
|
||||||
"time_justNow": "Только что",
|
"time_justNow": "Только что",
|
||||||
"time_minutesAgo": "{minutes} мин назад",
|
"time_minutesAgo": "{minutes} мин назад",
|
||||||
|
|||||||
@@ -100,6 +100,8 @@
|
|||||||
"settings_locationInvalid": "Neplatná šírka alebo dĺžka.",
|
"settings_locationInvalid": "Neplatná šírka alebo dĺžka.",
|
||||||
"settings_latitude": "Súradnica",
|
"settings_latitude": "Súradnica",
|
||||||
"settings_longitude": "Dĺžka",
|
"settings_longitude": "Dĺžka",
|
||||||
|
"settings_autoZeroHopAdvertOnGpsUpdate": "Automatický zero-hop inzerát pri aktualizácii GPS",
|
||||||
|
"settings_autoZeroHopAdvertOnGpsUpdateSubtitle": "Keď sa GPS poloha zmení, odoslať zero-hop inzerát (vyžaduje polohu v inzeráte).",
|
||||||
"settings_privacyMode": "Režim ochrany súkromia",
|
"settings_privacyMode": "Režim ochrany súkromia",
|
||||||
"settings_privacyModeSubtitle": "Skryť meno/poloha v reklamách",
|
"settings_privacyModeSubtitle": "Skryť meno/poloha v reklamách",
|
||||||
"settings_privacyModeToggle": "Prepínač súkromného režimu skryje vaše meno a polohu v reklamách.",
|
"settings_privacyModeToggle": "Prepínač súkromného režimu skryje vaše meno a polohu v reklamách.",
|
||||||
@@ -240,6 +242,19 @@
|
|||||||
"appSettings_last6Hours": "Posledné 6 hodín",
|
"appSettings_last6Hours": "Posledné 6 hodín",
|
||||||
"appSettings_last24Hours": "Posledných 24 hodín",
|
"appSettings_last24Hours": "Posledných 24 hodín",
|
||||||
"appSettings_lastWeek": "Minul týždeň",
|
"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_offlineMapCache": "Offline Mapa Pamäť",
|
||||||
"appSettings_noAreaSelected": "Neoznačila sa žiadna oblasť",
|
"appSettings_noAreaSelected": "Neoznačila sa žiadna oblasť",
|
||||||
"appSettings_areaSelectedZoom": "Vyberená oblasť (zoom {minZoom}-{maxZoom})",
|
"appSettings_areaSelectedZoom": "Vyberená oblasť (zoom {minZoom}-{maxZoom})",
|
||||||
@@ -769,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": "N {north}, S {south}, E {east}, W {west}",
|
||||||
"@mapCache_boundsLabel": {
|
"@mapCache_boundsLabel": {
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
|
|||||||
@@ -100,6 +100,8 @@
|
|||||||
"settings_locationInvalid": "Neveljavna zemeljska širina ali dolžina.",
|
"settings_locationInvalid": "Neveljavna zemeljska širina ali dolžina.",
|
||||||
"settings_latitude": "Širina",
|
"settings_latitude": "Širina",
|
||||||
"settings_longitude": "Dolžina",
|
"settings_longitude": "Dolžina",
|
||||||
|
"settings_autoZeroHopAdvertOnGpsUpdate": "Samodejni zero-hop oglas ob posodobitvi GPS",
|
||||||
|
"settings_autoZeroHopAdvertOnGpsUpdateSubtitle": "Ko se GPS lokacija spremeni, pošlji zero-hop oglas (zahteva lokacijo v oglasu).",
|
||||||
"settings_privacyMode": "Zasebnost",
|
"settings_privacyMode": "Zasebnost",
|
||||||
"settings_privacyModeSubtitle": "Skrita imena/lokacije v oglasih",
|
"settings_privacyModeSubtitle": "Skrita imena/lokacije v oglasih",
|
||||||
"settings_privacyModeToggle": "Omogoči način zasebnosti, da skrijemo tvoje ime in lokacijo v oglasih.",
|
"settings_privacyModeToggle": "Omogoči način zasebnosti, da skrijemo tvoje ime in lokacijo v oglasih.",
|
||||||
@@ -240,6 +242,19 @@
|
|||||||
"appSettings_last6Hours": "Zadnjih 6 ur",
|
"appSettings_last6Hours": "Zadnjih 6 ur",
|
||||||
"appSettings_last24Hours": "Zadnjih 24 ur",
|
"appSettings_last24Hours": "Zadnjih 24 ur",
|
||||||
"appSettings_lastWeek": "Prejšnji teden",
|
"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_offlineMapCache": "Shramba zemljevidov brez povezave",
|
||||||
"appSettings_noAreaSelected": "Območje ni izbrano",
|
"appSettings_noAreaSelected": "Območje ni izbrano",
|
||||||
"appSettings_areaSelectedZoom": "Izbrano območje (povečava {minZoom}-{maxZoom})",
|
"appSettings_areaSelectedZoom": "Izbrano območje (povečava {minZoom}-{maxZoom})",
|
||||||
@@ -769,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": "N {north}, S {south}, E {east}, W {west}",
|
||||||
"@mapCache_boundsLabel": {
|
"@mapCache_boundsLabel": {
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
|
|||||||
@@ -100,6 +100,8 @@
|
|||||||
"settings_locationInvalid": "Ogiltig latitud eller longitud.",
|
"settings_locationInvalid": "Ogiltig latitud eller longitud.",
|
||||||
"settings_latitude": "Latitud",
|
"settings_latitude": "Latitud",
|
||||||
"settings_longitude": "Längdgrad",
|
"settings_longitude": "Längdgrad",
|
||||||
|
"settings_autoZeroHopAdvertOnGpsUpdate": "Automatisk zero-hop-annons vid GPS-uppdatering",
|
||||||
|
"settings_autoZeroHopAdvertOnGpsUpdateSubtitle": "När GPS-positionen ändras, skicka en zero-hop-annons (kräver plats i annonsen).",
|
||||||
"settings_privacyMode": "Privatläge",
|
"settings_privacyMode": "Privatläge",
|
||||||
"settings_privacyModeSubtitle": "Dölj namn/plats i annonser",
|
"settings_privacyModeSubtitle": "Dölj namn/plats i annonser",
|
||||||
"settings_privacyModeToggle": "Aktivera privatläge för att dölja ditt namn och din plats i annonser.",
|
"settings_privacyModeToggle": "Aktivera privatläge för att dölja ditt namn och din plats i annonser.",
|
||||||
@@ -240,6 +242,19 @@
|
|||||||
"appSettings_last6Hours": "De senaste 6 timmarna",
|
"appSettings_last6Hours": "De senaste 6 timmarna",
|
||||||
"appSettings_last24Hours": "De senaste 24 timmarna",
|
"appSettings_last24Hours": "De senaste 24 timmarna",
|
||||||
"appSettings_lastWeek": "Förra veckan",
|
"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_offlineMapCache": "Offline Kartcache",
|
||||||
"appSettings_noAreaSelected": "Ingen area markerad",
|
"appSettings_noAreaSelected": "Ingen area markerad",
|
||||||
"appSettings_areaSelectedZoom": "Område markerat (zoom {minZoom}-{maxZoom})",
|
"appSettings_areaSelectedZoom": "Område markerat (zoom {minZoom}-{maxZoom})",
|
||||||
@@ -769,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": "N {north}, S {south}, E {east}, W {west}",
|
||||||
"@mapCache_boundsLabel": {
|
"@mapCache_boundsLabel": {
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
|
|||||||
@@ -101,6 +101,8 @@
|
|||||||
"settings_locationInvalid": "Некоректна широта або довгота.",
|
"settings_locationInvalid": "Некоректна широта або довгота.",
|
||||||
"settings_latitude": "Широта",
|
"settings_latitude": "Широта",
|
||||||
"settings_longitude": "Довгота",
|
"settings_longitude": "Довгота",
|
||||||
|
"settings_autoZeroHopAdvertOnGpsUpdate": "Автооголошення без хопів при оновленні GPS",
|
||||||
|
"settings_autoZeroHopAdvertOnGpsUpdateSubtitle": "Коли GPS-локація змінюється, надсилати оголошення без хопів (потрібна геопозиція в оголошенні).",
|
||||||
"settings_privacyMode": "Режим приватності",
|
"settings_privacyMode": "Режим приватності",
|
||||||
"settings_privacyModeSubtitle": "Приховати ім'я/геопозицію в оголошеннях",
|
"settings_privacyModeSubtitle": "Приховати ім'я/геопозицію в оголошеннях",
|
||||||
"settings_privacyModeToggle": "Увімкніть режим приватності, щоб приховати своє ім'я та геопозицію в оголошеннях.",
|
"settings_privacyModeToggle": "Увімкніть режим приватності, щоб приховати своє ім'я та геопозицію в оголошеннях.",
|
||||||
@@ -242,6 +244,19 @@
|
|||||||
"appSettings_last6Hours": "Останні 6 годин",
|
"appSettings_last6Hours": "Останні 6 годин",
|
||||||
"appSettings_last24Hours": "Останні 24 години",
|
"appSettings_last24Hours": "Останні 24 години",
|
||||||
"appSettings_lastWeek": "Минулий тиждень",
|
"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_offlineMapCache": "Офлайн-кеш карти",
|
||||||
"appSettings_noAreaSelected": "Область не вибрано",
|
"appSettings_noAreaSelected": "Область не вибрано",
|
||||||
"appSettings_areaSelectedZoom": "Вибрана область (зум {minZoom}-{maxZoom})",
|
"appSettings_areaSelectedZoom": "Вибрана область (зум {minZoom}-{maxZoom})",
|
||||||
@@ -779,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": "Пн {north}, Пд {south}, Сх {east}, Зх {west}",
|
||||||
"@mapCache_boundsLabel": {
|
"@mapCache_boundsLabel": {
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
|
|||||||
@@ -105,6 +105,8 @@
|
|||||||
"settings_locationIntervalInvalid": "间隔时间必须至少为 60 秒,但不超过 86400 秒。",
|
"settings_locationIntervalInvalid": "间隔时间必须至少为 60 秒,但不超过 86400 秒。",
|
||||||
"settings_latitude": "纬度",
|
"settings_latitude": "纬度",
|
||||||
"settings_longitude": "经度",
|
"settings_longitude": "经度",
|
||||||
|
"settings_autoZeroHopAdvertOnGpsUpdate": "GPS 更新时自动发送零跳广告",
|
||||||
|
"settings_autoZeroHopAdvertOnGpsUpdateSubtitle": "当 GPS 位置变化时,发送零跳广告(需要在广告中包含位置)。",
|
||||||
"settings_privacyMode": "隐私模式",
|
"settings_privacyMode": "隐私模式",
|
||||||
"settings_privacyModeSubtitle": "在广告中隐藏姓名/位置",
|
"settings_privacyModeSubtitle": "在广告中隐藏姓名/位置",
|
||||||
"settings_privacyModeToggle": "切换隐私模式以在广告中隐藏姓名和位置,保护个人信息。",
|
"settings_privacyModeToggle": "切换隐私模式以在广告中隐藏姓名和位置,保护个人信息。",
|
||||||
@@ -254,6 +256,19 @@
|
|||||||
"appSettings_last6Hours": "过去6小时",
|
"appSettings_last6Hours": "过去6小时",
|
||||||
"appSettings_last24Hours": "过去24小时",
|
"appSettings_last24Hours": "过去24小时",
|
||||||
"appSettings_lastWeek": "上周",
|
"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_offlineMapCache": "离线地图缓存",
|
||||||
"appSettings_noAreaSelected": "未选择任何区域",
|
"appSettings_noAreaSelected": "未选择任何区域",
|
||||||
"appSettings_areaSelectedZoom": "已选择区域(缩放 {minZoom} - {maxZoom})",
|
"appSettings_areaSelectedZoom": "已选择区域(缩放 {minZoom} - {maxZoom})",
|
||||||
@@ -796,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": "北 {north}, 南 {south}, 东 {east}, 西 {west}",
|
||||||
"@mapCache_boundsLabel": {
|
"@mapCache_boundsLabel": {
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
|
|||||||
@@ -51,7 +51,9 @@ void main() async {
|
|||||||
final bleDebugLogService = BleDebugLogService();
|
final bleDebugLogService = BleDebugLogService();
|
||||||
final appDebugLogService = AppDebugLogService();
|
final appDebugLogService = AppDebugLogService();
|
||||||
final backgroundService = BackgroundService();
|
final backgroundService = BackgroundService();
|
||||||
final mapTileCacheService = MapTileCacheService();
|
final mapTileCacheService = MapTileCacheService(
|
||||||
|
appSettingsService: appSettingsService,
|
||||||
|
);
|
||||||
final chatTextScaleService = ChatTextScaleService();
|
final chatTextScaleService = ChatTextScaleService();
|
||||||
final translationService = TranslationService(appSettingsService);
|
final translationService = TranslationService(appSettingsService);
|
||||||
final uiViewStateService = UiViewStateService();
|
final uiViewStateService = UiViewStateService();
|
||||||
@@ -183,7 +185,7 @@ class MeshCoreApp extends StatelessWidget {
|
|||||||
ChangeNotifierProvider.value(value: translationService),
|
ChangeNotifierProvider.value(value: translationService),
|
||||||
ChangeNotifierProvider.value(value: uiViewStateService),
|
ChangeNotifierProvider.value(value: uiViewStateService),
|
||||||
Provider.value(value: storage),
|
Provider.value(value: storage),
|
||||||
Provider.value(value: mapTileCacheService),
|
ChangeNotifierProvider.value(value: mapTileCacheService),
|
||||||
ChangeNotifierProvider.value(value: timeoutPredictionService),
|
ChangeNotifierProvider.value(value: timeoutPredictionService),
|
||||||
],
|
],
|
||||||
child: Consumer<AppSettingsService>(
|
child: Consumer<AppSettingsService>(
|
||||||
|
|||||||
@@ -76,6 +76,8 @@ class Cyr2LatProfile {
|
|||||||
|
|
||||||
class AppSettings {
|
class AppSettings {
|
||||||
static const Object _unset = Object();
|
static const Object _unset = Object();
|
||||||
|
static const String stadiaDemo =
|
||||||
|
'51bd0381-4685-4666-bae8-48940f6d77c0';
|
||||||
|
|
||||||
final bool clearPathOnMaxRetry;
|
final bool clearPathOnMaxRetry;
|
||||||
final bool mapShowRepeaters;
|
final bool mapShowRepeaters;
|
||||||
@@ -91,10 +93,15 @@ class AppSettings {
|
|||||||
final Map<String, double>? mapCacheBounds;
|
final Map<String, double>? mapCacheBounds;
|
||||||
final int mapCacheMinZoom;
|
final int mapCacheMinZoom;
|
||||||
final int mapCacheMaxZoom;
|
final int mapCacheMaxZoom;
|
||||||
|
final String mapRasterSourceId;
|
||||||
|
final String mapTileEndpointId;
|
||||||
|
final String? mapTileApiKey;
|
||||||
final bool notificationsEnabled;
|
final bool notificationsEnabled;
|
||||||
final bool notifyOnNewMessage;
|
final bool notifyOnNewMessage;
|
||||||
final bool notifyOnNewChannelMessage;
|
final bool notifyOnNewChannelMessage;
|
||||||
final bool notifyOnNewAdvert;
|
final bool notifyOnNewAdvert;
|
||||||
|
final bool autoSendZeroHopAdvertOnGpsUpdate;
|
||||||
|
final int gpsIntervalSeconds;
|
||||||
final bool autoRouteRotationEnabled;
|
final bool autoRouteRotationEnabled;
|
||||||
final double maxRouteWeight;
|
final double maxRouteWeight;
|
||||||
final double initialRouteWeight;
|
final double initialRouteWeight;
|
||||||
@@ -122,6 +129,17 @@ class AppSettings {
|
|||||||
final List<Cyr2LatProfile> cyr2latProfiles;
|
final List<Cyr2LatProfile> cyr2latProfiles;
|
||||||
final String selectedCyr2latProfileId;
|
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 {
|
Map<String, String> get cyr2latCharMap {
|
||||||
final profile = cyr2latProfiles.firstWhere(
|
final profile = cyr2latProfiles.firstWhere(
|
||||||
(p) => p.id == selectedCyr2latProfileId,
|
(p) => p.id == selectedCyr2latProfileId,
|
||||||
@@ -145,10 +163,15 @@ class AppSettings {
|
|||||||
this.mapCacheBounds,
|
this.mapCacheBounds,
|
||||||
this.mapCacheMinZoom = 10,
|
this.mapCacheMinZoom = 10,
|
||||||
this.mapCacheMaxZoom = 15,
|
this.mapCacheMaxZoom = 15,
|
||||||
|
this.mapRasterSourceId = 'osm_auto',
|
||||||
|
this.mapTileEndpointId = 'standard_2x',
|
||||||
|
this.mapTileApiKey,
|
||||||
this.notificationsEnabled = true,
|
this.notificationsEnabled = true,
|
||||||
this.notifyOnNewMessage = true,
|
this.notifyOnNewMessage = true,
|
||||||
this.notifyOnNewChannelMessage = true,
|
this.notifyOnNewChannelMessage = true,
|
||||||
this.notifyOnNewAdvert = true,
|
this.notifyOnNewAdvert = true,
|
||||||
|
this.autoSendZeroHopAdvertOnGpsUpdate = false,
|
||||||
|
this.gpsIntervalSeconds = 900,
|
||||||
this.autoRouteRotationEnabled = true,
|
this.autoRouteRotationEnabled = true,
|
||||||
this.maxRouteWeight = 5.0,
|
this.maxRouteWeight = 5.0,
|
||||||
this.initialRouteWeight = 3.0,
|
this.initialRouteWeight = 3.0,
|
||||||
@@ -206,10 +229,16 @@ class AppSettings {
|
|||||||
'map_cache_bounds': mapCacheBounds,
|
'map_cache_bounds': mapCacheBounds,
|
||||||
'map_cache_min_zoom': mapCacheMinZoom,
|
'map_cache_min_zoom': mapCacheMinZoom,
|
||||||
'map_cache_max_zoom': mapCacheMaxZoom,
|
'map_cache_max_zoom': mapCacheMaxZoom,
|
||||||
|
'map_raster_source_id': mapRasterSourceId,
|
||||||
|
'map_tile_endpoint_id': mapTileEndpointId,
|
||||||
|
'map_tile_api_key': mapTileApiKey,
|
||||||
'notifications_enabled': notificationsEnabled,
|
'notifications_enabled': notificationsEnabled,
|
||||||
'notify_on_new_message': notifyOnNewMessage,
|
'notify_on_new_message': notifyOnNewMessage,
|
||||||
'notify_on_new_channel_message': notifyOnNewChannelMessage,
|
'notify_on_new_channel_message': notifyOnNewChannelMessage,
|
||||||
'notify_on_new_advert': notifyOnNewAdvert,
|
'notify_on_new_advert': notifyOnNewAdvert,
|
||||||
|
'auto_send_zero_hop_advert_on_gps_update':
|
||||||
|
autoSendZeroHopAdvertOnGpsUpdate,
|
||||||
|
'gps_interval_seconds': gpsIntervalSeconds,
|
||||||
'auto_route_rotation_enabled': autoRouteRotationEnabled,
|
'auto_route_rotation_enabled': autoRouteRotationEnabled,
|
||||||
'max_route_weight': maxRouteWeight,
|
'max_route_weight': maxRouteWeight,
|
||||||
'initial_route_weight': initialRouteWeight,
|
'initial_route_weight': initialRouteWeight,
|
||||||
@@ -270,11 +299,18 @@ class AppSettings {
|
|||||||
),
|
),
|
||||||
mapCacheMinZoom: json['map_cache_min_zoom'] as int? ?? 10,
|
mapCacheMinZoom: json['map_cache_min_zoom'] as int? ?? 10,
|
||||||
mapCacheMaxZoom: json['map_cache_max_zoom'] as int? ?? 15,
|
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,
|
notificationsEnabled: json['notifications_enabled'] as bool? ?? true,
|
||||||
notifyOnNewMessage: json['notify_on_new_message'] as bool? ?? true,
|
notifyOnNewMessage: json['notify_on_new_message'] as bool? ?? true,
|
||||||
notifyOnNewChannelMessage:
|
notifyOnNewChannelMessage:
|
||||||
json['notify_on_new_channel_message'] as bool? ?? true,
|
json['notify_on_new_channel_message'] as bool? ?? true,
|
||||||
notifyOnNewAdvert: json['notify_on_new_advert'] as bool? ?? true,
|
notifyOnNewAdvert: json['notify_on_new_advert'] as bool? ?? true,
|
||||||
|
autoSendZeroHopAdvertOnGpsUpdate:
|
||||||
|
json['auto_send_zero_hop_advert_on_gps_update'] as bool? ?? false,
|
||||||
|
gpsIntervalSeconds:
|
||||||
|
(json['gps_interval_seconds'] as num?)?.toInt() ?? 900,
|
||||||
autoRouteRotationEnabled:
|
autoRouteRotationEnabled:
|
||||||
json['auto_route_rotation_enabled'] as bool? ?? true,
|
json['auto_route_rotation_enabled'] as bool? ?? true,
|
||||||
maxRouteWeight: (json['max_route_weight'] as num?)?.toDouble() ?? 5.0,
|
maxRouteWeight: (json['max_route_weight'] as num?)?.toDouble() ?? 5.0,
|
||||||
@@ -379,10 +415,15 @@ class AppSettings {
|
|||||||
Object? mapCacheBounds = _unset,
|
Object? mapCacheBounds = _unset,
|
||||||
int? mapCacheMinZoom,
|
int? mapCacheMinZoom,
|
||||||
int? mapCacheMaxZoom,
|
int? mapCacheMaxZoom,
|
||||||
|
String? mapRasterSourceId,
|
||||||
|
String? mapTileEndpointId,
|
||||||
|
Object? mapTileApiKey = _unset,
|
||||||
bool? notificationsEnabled,
|
bool? notificationsEnabled,
|
||||||
bool? notifyOnNewMessage,
|
bool? notifyOnNewMessage,
|
||||||
bool? notifyOnNewChannelMessage,
|
bool? notifyOnNewChannelMessage,
|
||||||
bool? notifyOnNewAdvert,
|
bool? notifyOnNewAdvert,
|
||||||
|
bool? autoSendZeroHopAdvertOnGpsUpdate,
|
||||||
|
int? gpsIntervalSeconds,
|
||||||
bool? autoRouteRotationEnabled,
|
bool? autoRouteRotationEnabled,
|
||||||
double? maxRouteWeight,
|
double? maxRouteWeight,
|
||||||
double? initialRouteWeight,
|
double? initialRouteWeight,
|
||||||
@@ -428,11 +469,20 @@ class AppSettings {
|
|||||||
: mapCacheBounds as Map<String, double>?,
|
: mapCacheBounds as Map<String, double>?,
|
||||||
mapCacheMinZoom: mapCacheMinZoom ?? this.mapCacheMinZoom,
|
mapCacheMinZoom: mapCacheMinZoom ?? this.mapCacheMinZoom,
|
||||||
mapCacheMaxZoom: mapCacheMaxZoom ?? this.mapCacheMaxZoom,
|
mapCacheMaxZoom: mapCacheMaxZoom ?? this.mapCacheMaxZoom,
|
||||||
|
mapRasterSourceId: mapRasterSourceId ?? this.mapRasterSourceId,
|
||||||
|
mapTileEndpointId: mapTileEndpointId ?? this.mapTileEndpointId,
|
||||||
|
mapTileApiKey: mapTileApiKey == _unset
|
||||||
|
? this.mapTileApiKey
|
||||||
|
: mapTileApiKey as String?,
|
||||||
notificationsEnabled: notificationsEnabled ?? this.notificationsEnabled,
|
notificationsEnabled: notificationsEnabled ?? this.notificationsEnabled,
|
||||||
notifyOnNewMessage: notifyOnNewMessage ?? this.notifyOnNewMessage,
|
notifyOnNewMessage: notifyOnNewMessage ?? this.notifyOnNewMessage,
|
||||||
notifyOnNewChannelMessage:
|
notifyOnNewChannelMessage:
|
||||||
notifyOnNewChannelMessage ?? this.notifyOnNewChannelMessage,
|
notifyOnNewChannelMessage ?? this.notifyOnNewChannelMessage,
|
||||||
notifyOnNewAdvert: notifyOnNewAdvert ?? this.notifyOnNewAdvert,
|
notifyOnNewAdvert: notifyOnNewAdvert ?? this.notifyOnNewAdvert,
|
||||||
|
autoSendZeroHopAdvertOnGpsUpdate:
|
||||||
|
autoSendZeroHopAdvertOnGpsUpdate ??
|
||||||
|
this.autoSendZeroHopAdvertOnGpsUpdate,
|
||||||
|
gpsIntervalSeconds: gpsIntervalSeconds ?? this.gpsIntervalSeconds,
|
||||||
autoRouteRotationEnabled:
|
autoRouteRotationEnabled:
|
||||||
autoRouteRotationEnabled ?? this.autoRouteRotationEnabled,
|
autoRouteRotationEnabled ?? this.autoRouteRotationEnabled,
|
||||||
maxRouteWeight: maxRouteWeight ?? this.maxRouteWeight,
|
maxRouteWeight: maxRouteWeight ?? this.maxRouteWeight,
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:math';
|
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:crypto/crypto.dart' as crypto;
|
import 'package:crypto/crypto.dart' as crypto;
|
||||||
|
import 'package:meshcore_open/utils/keys.dart';
|
||||||
|
|
||||||
import 'channel.dart';
|
import 'channel.dart';
|
||||||
|
|
||||||
@@ -37,11 +37,7 @@ class Community {
|
|||||||
|
|
||||||
/// Generate a new community with a random 32-byte secret
|
/// Generate a new community with a random 32-byte secret
|
||||||
factory Community.create({required String id, required String name}) {
|
factory Community.create({required String id, required String name}) {
|
||||||
final random = Random.secure();
|
final secret = randomBytes(32);
|
||||||
final secret = Uint8List(32);
|
|
||||||
for (int i = 0; i < 32; i++) {
|
|
||||||
secret[i] = random.nextInt(256);
|
|
||||||
}
|
|
||||||
return Community(
|
return Community(
|
||||||
id: id,
|
id: id,
|
||||||
name: name,
|
name: name,
|
||||||
@@ -210,12 +206,7 @@ class Community {
|
|||||||
|
|
||||||
/// Create a copy of this community with a regenerated random secret
|
/// Create a copy of this community with a regenerated random secret
|
||||||
Community withRegeneratedSecret() {
|
Community withRegeneratedSecret() {
|
||||||
final random = Random.secure();
|
return withNewSecret(randomBytes(32));
|
||||||
final newSecret = Uint8List(32);
|
|
||||||
for (int i = 0; i < 32; i++) {
|
|
||||||
newSecret[i] = random.nextInt(256);
|
|
||||||
}
|
|
||||||
return withNewSecret(newSecret);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extract secret from QR data (for updating existing community)
|
/// Extract secret from QR data (for updating existing community)
|
||||||
|
|||||||
@@ -81,6 +81,16 @@ class RadioSettings {
|
|||||||
txPowerDbm: 20,
|
txPowerDbm: 20,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
'Australia (Mid)',
|
||||||
|
RadioSettings(
|
||||||
|
frequencyMHz: 915.075,
|
||||||
|
bandwidth: LoRaBandwidth.bw125,
|
||||||
|
spreadingFactor: LoRaSpreadingFactor.sf9,
|
||||||
|
codingRate: LoRaCodingRate.cr4_5,
|
||||||
|
txPowerDbm: 20,
|
||||||
|
),
|
||||||
|
),
|
||||||
(
|
(
|
||||||
'Australia SA, WA, QLD',
|
'Australia SA, WA, QLD',
|
||||||
RadioSettings(
|
RadioSettings(
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import '../l10n/l10n.dart';
|
|||||||
import '../models/app_settings.dart';
|
import '../models/app_settings.dart';
|
||||||
import '../models/translation_support.dart';
|
import '../models/translation_support.dart';
|
||||||
import '../services/app_settings_service.dart';
|
import '../services/app_settings_service.dart';
|
||||||
|
import '../services/map_tile_cache_service.dart';
|
||||||
import '../services/notification_service.dart';
|
import '../services/notification_service.dart';
|
||||||
import '../services/translation_service.dart';
|
import '../services/translation_service.dart';
|
||||||
import '../theme/mesh_theme.dart';
|
import '../theme/mesh_theme.dart';
|
||||||
@@ -699,52 +700,229 @@ class AppSettingsScreen extends StatelessWidget {
|
|||||||
) {
|
) {
|
||||||
final scheme = Theme.of(context).colorScheme;
|
final scheme = Theme.of(context).colorScheme;
|
||||||
final textTheme = Theme.of(context).textTheme;
|
final textTheme = Theme.of(context).textTheme;
|
||||||
return Column(
|
final children = <Widget>[
|
||||||
children: [
|
SwitchListTile(
|
||||||
SwitchListTile(
|
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
secondary: const Icon(Icons.router_outlined, size: 20),
|
||||||
horizontal: 16,
|
title: Text(context.l10n.appSettings_showRepeaters),
|
||||||
vertical: 4,
|
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(
|
const Divider(height: 1, indent: 16),
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
InkWell(
|
||||||
horizontal: 16,
|
onTap: () => _showUnitsSheet(context, settingsService),
|
||||||
vertical: 4,
|
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(
|
const Divider(height: 1, indent: 16),
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
InkWell(
|
||||||
horizontal: 16,
|
onTap: () {
|
||||||
vertical: 4,
|
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),
|
const Divider(height: 1, indent: 16),
|
||||||
InkWell(
|
InkWell(
|
||||||
onTap: () => _showTimeFilterSheet(context, settingsService),
|
onTap: () => _showMapRasterEndpointDialog(context, settingsService),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(
|
||||||
Icons.timer_outlined,
|
Icons.public_outlined,
|
||||||
size: 20,
|
size: 20,
|
||||||
color: scheme.onSurfaceVariant,
|
color: scheme.onSurfaceVariant,
|
||||||
),
|
),
|
||||||
@@ -754,19 +932,14 @@ class AppSettingsScreen extends StatelessWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
context.l10n.appSettings_timeFilter,
|
context.l10n.appSettings_stadiaEndpoint,
|
||||||
style: textTheme.bodyMedium?.copyWith(
|
style: textTheme.bodyMedium?.copyWith(
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 2),
|
const SizedBox(height: 2),
|
||||||
Text(
|
Text(
|
||||||
settingsService.settings.mapTimeFilterHours == 0
|
_mapRasterEndpointSummary(settingsService.settings),
|
||||||
? context.l10n.appSettings_timeFilterShowAll
|
|
||||||
: context.l10n.appSettings_timeFilterShowLast(
|
|
||||||
settingsService.settings.mapTimeFilterHours
|
|
||||||
.toInt(),
|
|
||||||
),
|
|
||||||
style: textTheme.bodySmall?.copyWith(
|
style: textTheme.bodySmall?.copyWith(
|
||||||
color: scheme.onSurfaceVariant,
|
color: scheme.onSurfaceVariant,
|
||||||
),
|
),
|
||||||
@@ -785,13 +958,13 @@ class AppSettingsScreen extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
const Divider(height: 1, indent: 16),
|
const Divider(height: 1, indent: 16),
|
||||||
InkWell(
|
InkWell(
|
||||||
onTap: () => _showUnitsSheet(context, settingsService),
|
onTap: () => _showMapApiKeyDialog(context, settingsService),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(
|
||||||
Icons.straighten,
|
Icons.key_outlined,
|
||||||
size: 20,
|
size: 20,
|
||||||
color: scheme.onSurfaceVariant,
|
color: scheme.onSurfaceVariant,
|
||||||
),
|
),
|
||||||
@@ -801,17 +974,14 @@ class AppSettingsScreen extends StatelessWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
context.l10n.appSettings_unitsTitle,
|
context.l10n.appSettings_stadiaApiKey,
|
||||||
style: textTheme.bodyMedium?.copyWith(
|
style: textTheme.bodyMedium?.copyWith(
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 2),
|
const SizedBox(height: 2),
|
||||||
Text(
|
Text(
|
||||||
settingsService.settings.unitSystem ==
|
_mapApiKeySummary(context, settingsService.settings),
|
||||||
UnitSystem.imperial
|
|
||||||
? context.l10n.appSettings_unitsImperial
|
|
||||||
: context.l10n.appSettings_unitsMetric,
|
|
||||||
style: textTheme.bodySmall?.copyWith(
|
style: textTheme.bodySmall?.copyWith(
|
||||||
color: scheme.onSurfaceVariant,
|
color: scheme.onSurfaceVariant,
|
||||||
),
|
),
|
||||||
@@ -828,59 +998,210 @@ class AppSettingsScreen extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Divider(height: 1, indent: 16),
|
]);
|
||||||
InkWell(
|
}
|
||||||
onTap: () {
|
|
||||||
Navigator.push(
|
return Column(children: children);
|
||||||
context,
|
}
|
||||||
MaterialPageRoute(builder: (context) => const MapCacheScreen()),
|
|
||||||
);
|
String _mapRasterSourceSummary(AppSettings settings) {
|
||||||
},
|
final source = MapRasterSourceCatalog.fromSettings(settings);
|
||||||
child: Padding(
|
return '${source.label} - ${source.description}';
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
}
|
||||||
child: Row(
|
|
||||||
children: [
|
bool _isStadiaSource(AppSettings settings) {
|
||||||
Icon(
|
return MapRasterSourceCatalog.fromSettings(settings).isStadia;
|
||||||
Icons.download_outlined,
|
}
|
||||||
size: 20,
|
|
||||||
color: scheme.onSurfaceVariant,
|
String _mapRasterEndpointSummary(AppSettings settings) {
|
||||||
),
|
final endpoint = MapRasterEndpointCatalog.fromSettings(settings);
|
||||||
const SizedBox(width: 12),
|
return '${endpoint.label} - ${endpoint.description}';
|
||||||
Expanded(
|
}
|
||||||
|
|
||||||
|
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(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
for (final preset in MapRasterSourcePreset.values)
|
||||||
context.l10n.appSettings_offlineMapCache,
|
Builder(
|
||||||
style: textTheme.bodyMedium?.copyWith(
|
builder: (context) {
|
||||||
fontWeight: FontWeight.w600,
|
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),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -578,10 +578,10 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
|
|||||||
mainAxisAlignment: isOutgoing
|
mainAxisAlignment: isOutgoing
|
||||||
? MainAxisAlignment.end
|
? MainAxisAlignment.end
|
||||||
: MainAxisAlignment.start,
|
: MainAxisAlignment.start,
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
if (!isOutgoing) ...[
|
if (!isOutgoing) ...[
|
||||||
_buildAvatar(message.senderName),
|
_buildAvatar(message.senderName, textScale),
|
||||||
const SizedBox(width: 6),
|
const SizedBox(width: 6),
|
||||||
],
|
],
|
||||||
Flexible(
|
Flexible(
|
||||||
@@ -619,10 +619,10 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
|
|||||||
: EdgeInsets.zero,
|
: EdgeInsets.zero,
|
||||||
child: Text(
|
child: Text(
|
||||||
message.senderName,
|
message.senderName,
|
||||||
style: MeshTheme.mono(
|
style: TextStyle(
|
||||||
fontSize: 11,
|
fontSize: 13 * textScale,
|
||||||
fontWeight: FontWeight.w700,
|
fontWeight: FontWeight.w700,
|
||||||
color: _colorForName(message.senderName),
|
color: textColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -1047,8 +1047,11 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildAvatar(String senderName) {
|
Widget _buildAvatar(String senderName, double textScale) {
|
||||||
return AvatarCircle(name: senderName, size: 32);
|
return AvatarCircle(
|
||||||
|
name: senderName,
|
||||||
|
size: (32 * textScale).clamp(28.0, 56.0),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildReplyBanner(double textScale) {
|
Widget _buildReplyBanner(double textScale) {
|
||||||
@@ -1212,7 +1215,7 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
|
|||||||
hintText: context.l10n.chat_typeMessage,
|
hintText: context.l10n.chat_typeMessage,
|
||||||
border: OutlineInputBorder(
|
border: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(
|
borderRadius: BorderRadius.circular(
|
||||||
MeshRadii.pill,
|
MeshRadii.md,
|
||||||
),
|
),
|
||||||
borderSide: BorderSide(
|
borderSide: BorderSide(
|
||||||
color: scheme.outlineVariant,
|
color: scheme.outlineVariant,
|
||||||
@@ -1220,7 +1223,7 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
|
|||||||
),
|
),
|
||||||
enabledBorder: OutlineInputBorder(
|
enabledBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(
|
borderRadius: BorderRadius.circular(
|
||||||
MeshRadii.pill,
|
MeshRadii.md,
|
||||||
),
|
),
|
||||||
borderSide: BorderSide(
|
borderSide: BorderSide(
|
||||||
color: scheme.outlineVariant,
|
color: scheme.outlineVariant,
|
||||||
@@ -1228,7 +1231,7 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
|
|||||||
),
|
),
|
||||||
focusedBorder: OutlineInputBorder(
|
focusedBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(
|
borderRadius: BorderRadius.circular(
|
||||||
MeshRadii.pill,
|
MeshRadii.md,
|
||||||
),
|
),
|
||||||
borderSide: BorderSide(
|
borderSide: BorderSide(
|
||||||
color: scheme.primary,
|
color: scheme.primary,
|
||||||
@@ -1602,23 +1605,6 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
|
|||||||
return PathHelper.splitPathBytes(pathBytes, pathHashByteWidth).length;
|
return PathHelper.splitPathBytes(pathBytes, pathHashByteWidth).length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deterministic name-to-hue mapping consistent with [AvatarCircle].
|
|
||||||
Color _colorForName(String name) {
|
|
||||||
const hues = [
|
|
||||||
MeshPalette.blue,
|
|
||||||
MeshPalette.magenta,
|
|
||||||
MeshPalette.signal,
|
|
||||||
MeshPalette.warn,
|
|
||||||
Color(0xFF8FA8F0),
|
|
||||||
Color(0xFF6FD9CE),
|
|
||||||
];
|
|
||||||
var h = 0;
|
|
||||||
for (final c in name.codeUnits) {
|
|
||||||
h = (h * 31 + c) & 0x7fffffff;
|
|
||||||
}
|
|
||||||
return hues[h % hues.length];
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> openRegionSelectDialog(Channel channel) async {
|
Future<void> openRegionSelectDialog(Channel channel) async {
|
||||||
// The AppBar subtitle reads the region from the connector inside a
|
// The AppBar subtitle reads the region from the connector inside a
|
||||||
// Consumer, so setChannelRegion's notifyListeners refreshes it directly —
|
// Consumer, so setChannelRegion's notifyListeners refreshes it directly —
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import '../theme/mesh_theme.dart';
|
|||||||
import '../widgets/adaptive_app_bar_title.dart';
|
import '../widgets/adaptive_app_bar_title.dart';
|
||||||
import '../widgets/mesh_ui.dart';
|
import '../widgets/mesh_ui.dart';
|
||||||
import '../widgets/path_map_ui.dart';
|
import '../widgets/path_map_ui.dart';
|
||||||
import '../widgets/themed_map_tile_layer.dart';
|
|
||||||
|
|
||||||
class ChannelMessagePathScreen extends StatelessWidget {
|
class ChannelMessagePathScreen extends StatelessWidget {
|
||||||
final ChannelMessage message;
|
final ChannelMessage message;
|
||||||
@@ -1014,7 +1013,7 @@ class _ChannelMessagePathMapScreenState
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
children: [
|
children: [
|
||||||
ThemedMapTileLayer(tileCache: tileCache),
|
tileCache.buildTileLayer(context),
|
||||||
AnimatedBuilder(
|
AnimatedBuilder(
|
||||||
animation: _playback,
|
animation: _playback,
|
||||||
builder: (context, _) {
|
builder: (context, _) {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:math';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:meshcore_open/storage/channel_message_store.dart';
|
import 'package:meshcore_open/storage/channel_message_store.dart';
|
||||||
|
import 'package:meshcore_open/utils/keys.dart';
|
||||||
import 'package:meshcore_open/utils/platform_info.dart';
|
import 'package:meshcore_open/utils/platform_info.dart';
|
||||||
import 'package:meshcore_open/widgets/app_bar.dart';
|
import 'package:meshcore_open/widgets/app_bar.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
@@ -27,6 +27,7 @@ import '../widgets/qr_code_display.dart';
|
|||||||
import '../widgets/quick_switch_bar.dart';
|
import '../widgets/quick_switch_bar.dart';
|
||||||
import '../widgets/sync_progress_overlay.dart';
|
import '../widgets/sync_progress_overlay.dart';
|
||||||
import '../widgets/unread_badge.dart';
|
import '../widgets/unread_badge.dart';
|
||||||
|
import '../helpers/gif_helper.dart';
|
||||||
import '../helpers/snack_bar_builder.dart';
|
import '../helpers/snack_bar_builder.dart';
|
||||||
import 'channel_chat_screen.dart';
|
import 'channel_chat_screen.dart';
|
||||||
import 'community_qr_scanner_screen.dart';
|
import 'community_qr_scanner_screen.dart';
|
||||||
@@ -410,7 +411,11 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
|||||||
// Last message preview
|
// Last message preview
|
||||||
final messages = connector.getChannelMessages(channel);
|
final messages = connector.getChannelMessages(channel);
|
||||||
final lastMessage = messages.isNotEmpty ? messages.last : null;
|
final lastMessage = messages.isNotEmpty ? messages.last : null;
|
||||||
final lastPreview = lastMessage?.text ?? '';
|
final lastMessageText = lastMessage?.text ?? '';
|
||||||
|
final lastPreview = lastMessageText.isNotEmpty &&
|
||||||
|
GifHelper.parseGif(lastMessageText) != null
|
||||||
|
? context.l10n.chat_receivedGif
|
||||||
|
: lastMessageText;
|
||||||
final lastTime = lastMessage?.timestamp;
|
final lastTime = lastMessage?.timestamp;
|
||||||
|
|
||||||
final channelLabel = channel.name.isEmpty
|
final channelLabel = channel.name.isEmpty
|
||||||
@@ -453,7 +458,7 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
|||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
// Leading avatar with optional community badge
|
// Leading avatar with optional community badge
|
||||||
Stack(
|
Stack(
|
||||||
@@ -511,10 +516,12 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 6),
|
const SizedBox(width: 6),
|
||||||
StatusChip(
|
Text(
|
||||||
label: 'CH ${channel.index}',
|
'CH ${channel.index}',
|
||||||
color: MeshPalette.blue,
|
style: MeshTheme.mono(
|
||||||
fontSize: 10,
|
fontSize: 11,
|
||||||
|
color: scheme.onSurfaceVariant.withValues(alpha: 0.7),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -579,10 +586,13 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
|||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
ReorderableDragStartListener(
|
ReorderableDragStartListener(
|
||||||
index: dragIndex,
|
index: dragIndex,
|
||||||
|
// Top-aligned with the "CH n" / time line. Bottom padding keeps
|
||||||
|
// a comfortable drag target without pushing the icon down.
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.only(left: 8, right: 8, bottom: 16),
|
||||||
child: Icon(
|
child: Icon(
|
||||||
Icons.drag_handle,
|
Icons.drag_handle,
|
||||||
|
size: 18,
|
||||||
color: scheme.onSurfaceVariant,
|
color: scheme.onSurfaceVariant,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -936,11 +946,7 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final random = Random.secure();
|
final psk = randomBytes(16);
|
||||||
final psk = Uint8List(16);
|
|
||||||
for (int i = 0; i < 16; i++) {
|
|
||||||
psk[i] = random.nextInt(256);
|
|
||||||
}
|
|
||||||
Navigator.pop(sheetContext);
|
Navigator.pop(sheetContext);
|
||||||
await connector.setChannel(
|
await connector.setChannel(
|
||||||
nextIndex,
|
nextIndex,
|
||||||
@@ -1569,11 +1575,7 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
|||||||
icon: const Icon(Icons.casino),
|
icon: const Icon(Icons.casino),
|
||||||
tooltip: sheetContext.l10n.channels_generateRandomPsk,
|
tooltip: sheetContext.l10n.channels_generateRandomPsk,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
final random = Random.secure();
|
final bytes = randomBytes(16);
|
||||||
final bytes = Uint8List(16);
|
|
||||||
for (int i = 0; i < 16; i++) {
|
|
||||||
bytes[i] = random.nextInt(256);
|
|
||||||
}
|
|
||||||
pskController.text = Channel.formatPskHex(bytes);
|
pskController.text = Channel.formatPskHex(bytes);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -547,15 +547,15 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: context.l10n.chat_typeMessage,
|
hintText: context.l10n.chat_typeMessage,
|
||||||
border: OutlineInputBorder(
|
border: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(MeshRadii.pill),
|
borderRadius: BorderRadius.circular(MeshRadii.md),
|
||||||
borderSide: BorderSide(color: scheme.outlineVariant),
|
borderSide: BorderSide(color: scheme.outlineVariant),
|
||||||
),
|
),
|
||||||
enabledBorder: OutlineInputBorder(
|
enabledBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(MeshRadii.pill),
|
borderRadius: BorderRadius.circular(MeshRadii.md),
|
||||||
borderSide: BorderSide(color: scheme.outlineVariant),
|
borderSide: BorderSide(color: scheme.outlineVariant),
|
||||||
),
|
),
|
||||||
focusedBorder: OutlineInputBorder(
|
focusedBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(MeshRadii.pill),
|
borderRadius: BorderRadius.circular(MeshRadii.md),
|
||||||
borderSide: BorderSide(
|
borderSide: BorderSide(
|
||||||
color: scheme.primary,
|
color: scheme.primary,
|
||||||
width: 1.5,
|
width: 1.5,
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import '../widgets/app_bar.dart';
|
|||||||
import '../widgets/quick_switch_bar.dart';
|
import '../widgets/quick_switch_bar.dart';
|
||||||
import '../icons/los_icon.dart';
|
import '../icons/los_icon.dart';
|
||||||
import '../theme/mesh_theme.dart';
|
import '../theme/mesh_theme.dart';
|
||||||
import '../widgets/themed_map_tile_layer.dart';
|
|
||||||
|
|
||||||
class LineOfSightEndpoint {
|
class LineOfSightEndpoint {
|
||||||
final String label;
|
final String label;
|
||||||
@@ -357,7 +356,7 @@ class _LineOfSightMapScreenState extends State<LineOfSightMapScreen> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final settings = context.watch<AppSettingsService>().settings;
|
final settings = context.watch<AppSettingsService>().settings;
|
||||||
final isImperial = settings.unitSystem == UnitSystem.imperial;
|
final isImperial = settings.unitSystem == UnitSystem.imperial;
|
||||||
final tileCache = context.read<MapTileCacheService>();
|
final tileCache = context.watch<MapTileCacheService>();
|
||||||
final endpoints = _visibleEndpoints();
|
final endpoints = _visibleEndpoints();
|
||||||
final mapPoints = [
|
final mapPoints = [
|
||||||
if (_start != null) _start!.point,
|
if (_start != null) _start!.point,
|
||||||
@@ -443,8 +442,8 @@ class _LineOfSightMapScreenState extends State<LineOfSightMapScreen> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
children: [
|
children: [
|
||||||
ThemedMapTileLayer(
|
tileCache.buildTileLayer(
|
||||||
tileCache: tileCache,
|
context,
|
||||||
opacity: _showTerrainLayer ? 1 : 0.72,
|
opacity: _showTerrainLayer ? 1 : 0.72,
|
||||||
),
|
),
|
||||||
if (_result != null && _result!.segments.isNotEmpty)
|
if (_result != null && _result!.segments.isNotEmpty)
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import '../widgets/adaptive_app_bar_title.dart';
|
|||||||
import '../helpers/snack_bar_builder.dart';
|
import '../helpers/snack_bar_builder.dart';
|
||||||
import '../theme/mesh_theme.dart';
|
import '../theme/mesh_theme.dart';
|
||||||
import '../widgets/mesh_ui.dart';
|
import '../widgets/mesh_ui.dart';
|
||||||
import '../widgets/themed_map_tile_layer.dart';
|
|
||||||
|
|
||||||
class MapCacheScreen extends StatefulWidget {
|
class MapCacheScreen extends StatefulWidget {
|
||||||
const MapCacheScreen({super.key});
|
const MapCacheScreen({super.key});
|
||||||
@@ -34,6 +33,11 @@ class _MapCacheScreenState extends State<MapCacheScreen> {
|
|||||||
bool _isDownloading = false;
|
bool _isDownloading = false;
|
||||||
int _completedTiles = 0;
|
int _completedTiles = 0;
|
||||||
int _failedTiles = 0;
|
int _failedTiles = 0;
|
||||||
|
List<CachedTileInfo> _cachedTiles = const [];
|
||||||
|
int _cachedTileBytes = 0;
|
||||||
|
bool _isLoadingCachedTiles = false;
|
||||||
|
double _overlayZoom = 2.0;
|
||||||
|
LatLngBounds? _visibleBounds;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -123,8 +127,10 @@ class _MapCacheScreenState extends State<MapCacheScreen> {
|
|||||||
_minZoom = safeMin;
|
_minZoom = safeMin;
|
||||||
_maxZoom = safeMax;
|
_maxZoom = safeMax;
|
||||||
_selectedBounds = bounds;
|
_selectedBounds = bounds;
|
||||||
|
_visibleBounds = bounds;
|
||||||
});
|
});
|
||||||
_updateEstimate();
|
_updateEstimate();
|
||||||
|
_refreshCachedTiles();
|
||||||
if (bounds != null) {
|
if (bounds != null) {
|
||||||
_mapController.fitCamera(
|
_mapController.fitCamera(
|
||||||
CameraFit.bounds(bounds: bounds, padding: const EdgeInsets.all(48)),
|
CameraFit.bounds(bounds: bounds, padding: const EdgeInsets.all(48)),
|
||||||
@@ -181,6 +187,7 @@ class _MapCacheScreenState extends State<MapCacheScreen> {
|
|||||||
|
|
||||||
Future<void> _startDownload() async {
|
Future<void> _startDownload() async {
|
||||||
final bounds = _selectedBounds;
|
final bounds = _selectedBounds;
|
||||||
|
final cacheService = context.read<MapTileCacheService>();
|
||||||
if (bounds == null) {
|
if (bounds == null) {
|
||||||
showDismissibleSnackBar(
|
showDismissibleSnackBar(
|
||||||
context,
|
context,
|
||||||
@@ -197,6 +204,18 @@ class _MapCacheScreenState extends State<MapCacheScreen> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!cacheService.source.allowsBulkDownload) {
|
||||||
|
showDismissibleSnackBar(
|
||||||
|
context,
|
||||||
|
content: Text(
|
||||||
|
context.l10n.mapCache_bulkDownloadDisabledInConfig(
|
||||||
|
cacheService.source.label,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final confirmed = await showDialog<bool>(
|
final confirmed = await showDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (dialogContext) => AlertDialog(
|
builder: (dialogContext) => AlertDialog(
|
||||||
@@ -219,8 +238,6 @@ class _MapCacheScreenState extends State<MapCacheScreen> {
|
|||||||
|
|
||||||
if (confirmed != true || !mounted) return;
|
if (confirmed != true || !mounted) return;
|
||||||
|
|
||||||
final cacheService = context.read<MapTileCacheService>();
|
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_isDownloading = true;
|
_isDownloading = true;
|
||||||
_completedTiles = 0;
|
_completedTiles = 0;
|
||||||
@@ -254,6 +271,8 @@ class _MapCacheScreenState extends State<MapCacheScreen> {
|
|||||||
result.failed,
|
result.failed,
|
||||||
)
|
)
|
||||||
: context.l10n.mapCache_cachedTiles(result.downloaded);
|
: context.l10n.mapCache_cachedTiles(result.downloaded);
|
||||||
|
await _refreshCachedTiles();
|
||||||
|
if (!mounted) return;
|
||||||
showDismissibleSnackBar(context, content: Text(message));
|
showDismissibleSnackBar(context, content: Text(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -280,16 +299,61 @@ class _MapCacheScreenState extends State<MapCacheScreen> {
|
|||||||
final cacheService = context.read<MapTileCacheService>();
|
final cacheService = context.read<MapTileCacheService>();
|
||||||
await cacheService.clearCache();
|
await cacheService.clearCache();
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
|
await _refreshCachedTiles();
|
||||||
|
if (!mounted) return;
|
||||||
showDismissibleSnackBar(
|
showDismissibleSnackBar(
|
||||||
context,
|
context,
|
||||||
content: Text(context.l10n.mapCache_offlineCacheCleared),
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final tileCache = context.read<MapTileCacheService>();
|
final tileCache = context.watch<MapTileCacheService>();
|
||||||
|
final source = tileCache.source;
|
||||||
final selectedBounds = _selectedBounds;
|
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 l10n = context.l10n;
|
||||||
final scheme = Theme.of(context).colorScheme;
|
final scheme = Theme.of(context).colorScheme;
|
||||||
final isDesktop = _isDesktopPlatform(defaultTargetPlatform);
|
final isDesktop = _isDesktopPlatform(defaultTargetPlatform);
|
||||||
@@ -327,9 +391,23 @@ class _MapCacheScreenState extends State<MapCacheScreen> {
|
|||||||
)
|
)
|
||||||
: const KeyboardOptions.disabled(),
|
: 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: [
|
children: [
|
||||||
ThemedMapTileLayer(tileCache: tileCache),
|
tileCache.buildTileLayer(context),
|
||||||
if (selectedBounds != null)
|
if (selectedBounds != null)
|
||||||
PolygonLayer(
|
PolygonLayer(
|
||||||
polygons: [
|
polygons: [
|
||||||
@@ -341,9 +419,24 @@ class _MapCacheScreenState extends State<MapCacheScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
if (overlayPolygons.isNotEmpty)
|
||||||
|
PolygonLayer(polygons: overlayPolygons),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (isDesktop) _buildDesktopMapControls(),
|
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(
|
Positioned(
|
||||||
top: 12,
|
top: 12,
|
||||||
right: 12,
|
right: 12,
|
||||||
@@ -445,6 +538,34 @@ class _MapCacheScreenState extends State<MapCacheScreen> {
|
|||||||
color: scheme.onSurfaceVariant,
|
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) ...[
|
if (_isDownloading) ...[
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
LinearProgressIndicator(
|
LinearProgressIndicator(
|
||||||
@@ -471,7 +592,10 @@ class _MapCacheScreenState extends State<MapCacheScreen> {
|
|||||||
child: ElevatedButton.icon(
|
child: ElevatedButton.icon(
|
||||||
icon: const Icon(Icons.download),
|
icon: const Icon(Icons.download),
|
||||||
label: Text(l10n.mapCache_downloadTilesButton),
|
label: Text(l10n.mapCache_downloadTilesButton),
|
||||||
onPressed: _isDownloading || selectedBounds == null
|
onPressed:
|
||||||
|
_isDownloading ||
|
||||||
|
selectedBounds == null ||
|
||||||
|
!source.allowsBulkDownload
|
||||||
? null
|
? null
|
||||||
: _startDownload,
|
: _startDownload,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import '../utils/battery_utils.dart';
|
|||||||
import '../utils/route_transitions.dart';
|
import '../utils/route_transitions.dart';
|
||||||
import '../widgets/quick_switch_bar.dart';
|
import '../widgets/quick_switch_bar.dart';
|
||||||
import '../widgets/sync_progress_overlay.dart';
|
import '../widgets/sync_progress_overlay.dart';
|
||||||
import '../widgets/themed_map_tile_layer.dart';
|
|
||||||
import '../icons/los_icon.dart';
|
import '../icons/los_icon.dart';
|
||||||
import 'channels_screen.dart';
|
import 'channels_screen.dart';
|
||||||
import 'chat_screen.dart';
|
import 'chat_screen.dart';
|
||||||
@@ -109,6 +108,31 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
super.dispose();
|
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) {
|
_NodeAge _ageOf(Contact contact) {
|
||||||
final d = DateTime.now().difference(contact.lastSeen);
|
final d = DateTime.now().difference(contact.lastSeen);
|
||||||
if (d.inMinutes <= 60) return _NodeAge.online;
|
if (d.inMinutes <= 60) return _NodeAge.online;
|
||||||
@@ -235,12 +259,12 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
bottom: 96,
|
bottom: 96,
|
||||||
child: DecoratedBox(
|
child: DecoratedBox(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: MapPalette.panelDark,
|
color: _overlayPanelColor,
|
||||||
borderRadius: BorderRadius.circular(MeshRadii.md),
|
borderRadius: BorderRadius.circular(MeshRadii.md),
|
||||||
border: Border.all(color: MapPalette.border),
|
border: Border.all(color: _overlayBorderColor),
|
||||||
boxShadow: const [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: MapPalette.markerShadow,
|
color: _overlayShadowColor,
|
||||||
blurRadius: 8,
|
blurRadius: 8,
|
||||||
offset: Offset(0, 3),
|
offset: Offset(0, 3),
|
||||||
),
|
),
|
||||||
@@ -252,20 +276,20 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
IconButton(
|
IconButton(
|
||||||
color: MapPalette.textPrimary,
|
color: _overlayPrimaryTextColor,
|
||||||
icon: const Icon(Icons.add),
|
icon: const Icon(Icons.add),
|
||||||
visualDensity: VisualDensity.standard,
|
visualDensity: VisualDensity.standard,
|
||||||
tooltip: context.l10n.map_zoomIn,
|
tooltip: context.l10n.map_zoomIn,
|
||||||
onPressed: () => _zoomMapBy(1),
|
onPressed: () => _zoomMapBy(1),
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
color: MapPalette.textPrimary,
|
color: _overlayPrimaryTextColor,
|
||||||
icon: const Icon(Icons.remove),
|
icon: const Icon(Icons.remove),
|
||||||
tooltip: context.l10n.map_zoomOut,
|
tooltip: context.l10n.map_zoomOut,
|
||||||
onPressed: () => _zoomMapBy(-1),
|
onPressed: () => _zoomMapBy(-1),
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
color: MapPalette.textPrimary,
|
color: _overlayPrimaryTextColor,
|
||||||
icon: const Icon(Icons.crop_free),
|
icon: const Icon(Icons.crop_free),
|
||||||
tooltip: context.l10n.map_centerMap,
|
tooltip: context.l10n.map_centerMap,
|
||||||
onPressed: () => _mapController.move(center, zoom),
|
onPressed: () => _mapController.move(center, zoom),
|
||||||
@@ -330,6 +354,7 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
final settingsService = context.read<AppSettingsService>();
|
final settingsService = context.read<AppSettingsService>();
|
||||||
final pathHistory = context.read<PathHistoryService>();
|
final pathHistory = context.read<PathHistoryService>();
|
||||||
final tileCache = context.read<MapTileCacheService>();
|
final tileCache = context.read<MapTileCacheService>();
|
||||||
|
final scheme = Theme.of(context).colorScheme;
|
||||||
final isDesktop = _isDesktopPlatform(defaultTargetPlatform);
|
final isDesktop = _isDesktopPlatform(defaultTargetPlatform);
|
||||||
final allContacts = connector.allContacts;
|
final allContacts = connector.allContacts;
|
||||||
|
|
||||||
@@ -585,8 +610,8 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
canPop: allowBack,
|
canPop: allowBack,
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
backgroundColor: MapPalette.panelDark,
|
backgroundColor: scheme.surface,
|
||||||
foregroundColor: MapPalette.textPrimary,
|
foregroundColor: scheme.onSurface,
|
||||||
title: AppBarTitle(context.l10n.map_title),
|
title: AppBarTitle(context.l10n.map_title),
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
automaticallyImplyLeading: false,
|
automaticallyImplyLeading: false,
|
||||||
@@ -759,7 +784,7 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
children: [
|
children: [
|
||||||
ThemedMapTileLayer(tileCache: tileCache),
|
tileCache.buildTileLayer(context),
|
||||||
if (_polylines.isNotEmpty && _isBuildingPathTrace)
|
if (_polylines.isNotEmpty && _isBuildingPathTrace)
|
||||||
PolylineLayer(polylines: _polylines),
|
PolylineLayer(polylines: _polylines),
|
||||||
if (sharedMarkerPolylines.isNotEmpty)
|
if (sharedMarkerPolylines.isNotEmpty)
|
||||||
@@ -912,7 +937,7 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
_handleQuickSwitch(index, context),
|
_handleQuickSwitch(index, context),
|
||||||
contactsUnreadCount: connector.getTotalContactsUnreadCount(),
|
contactsUnreadCount: connector.getTotalContactsUnreadCount(),
|
||||||
channelsUnreadCount: connector.getTotalChannelsUnreadCount(),
|
channelsUnreadCount: connector.getTotalChannelsUnreadCount(),
|
||||||
highContrast: true,
|
highContrast: _useDarkOverlay,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
floatingActionButton:
|
floatingActionButton:
|
||||||
@@ -1211,14 +1236,14 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
height: 36,
|
height: 36,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
color: MapPalette.panelDark,
|
color: _overlayPanelColor,
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: guess.highConfidence ? color : MapPalette.textMuted,
|
color: guess.highConfidence ? color : _overlayMutedTextColor,
|
||||||
width: guess.highConfidence ? 2.5 : 2,
|
width: guess.highConfidence ? 2.5 : 2,
|
||||||
),
|
),
|
||||||
boxShadow: const [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: MapPalette.markerShadow,
|
color: _overlayShadowColor,
|
||||||
blurRadius: 7,
|
blurRadius: 7,
|
||||||
offset: Offset(0, 2),
|
offset: Offset(0, 2),
|
||||||
),
|
),
|
||||||
@@ -1227,7 +1252,7 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: Icon(
|
child: Icon(
|
||||||
Icons.not_listed_location,
|
Icons.not_listed_location,
|
||||||
color: MapPalette.textPrimary,
|
color: _overlayPrimaryTextColor,
|
||||||
size: 19,
|
size: 19,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -1563,12 +1588,12 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: MapPalette.panelDark,
|
color: _overlayPanelColor,
|
||||||
borderRadius: BorderRadius.circular(MeshRadii.xs),
|
borderRadius: BorderRadius.circular(MeshRadii.xs),
|
||||||
border: Border.all(color: MapPalette.border),
|
border: Border.all(color: _overlayBorderColor),
|
||||||
boxShadow: const [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: MapPalette.markerShadow,
|
color: _overlayShadowColor,
|
||||||
blurRadius: 4,
|
blurRadius: 4,
|
||||||
offset: Offset(0, 1),
|
offset: Offset(0, 1),
|
||||||
),
|
),
|
||||||
@@ -1582,7 +1607,7 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
style: MeshTheme.mono(
|
style: MeshTheme.mono(
|
||||||
fontSize: 10,
|
fontSize: 10,
|
||||||
fontWeight: FontWeight.w700,
|
fontWeight: FontWeight.w700,
|
||||||
color: MapPalette.textPrimary,
|
color: _overlayPrimaryTextColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -1703,7 +1728,7 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
color: statusColor,
|
color: statusColor,
|
||||||
border: Border.all(color: MapPalette.panelDark, width: 2),
|
border: Border.all(color: _overlayPanelColor, width: 2),
|
||||||
),
|
),
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: batteryLow
|
child: batteryLow
|
||||||
@@ -1726,10 +1751,10 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
label,
|
label,
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
color: MapPalette.textSecondary,
|
color: _overlaySecondaryTextColor,
|
||||||
),
|
),
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
@@ -1787,9 +1812,9 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Material(
|
child: Material(
|
||||||
color: MapPalette.panelDark,
|
color: _overlayPanelColor,
|
||||||
shape: StadiumBorder(
|
shape: StadiumBorder(
|
||||||
side: const BorderSide(color: MapPalette.border),
|
side: BorderSide(color: _overlayBorderColor),
|
||||||
),
|
),
|
||||||
clipBehavior: Clip.antiAlias,
|
clipBehavior: Clip.antiAlias,
|
||||||
child: TextField(
|
child: TextField(
|
||||||
@@ -1797,17 +1822,15 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
focusNode: _searchFocus,
|
focusNode: _searchFocus,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: context.l10n.map_searchHint,
|
hintText: context.l10n.map_searchHint,
|
||||||
hintStyle: const TextStyle(
|
hintStyle: TextStyle(color: _overlaySecondaryTextColor),
|
||||||
color: MapPalette.textSecondary,
|
prefixIcon: Icon(
|
||||||
),
|
|
||||||
prefixIcon: const Icon(
|
|
||||||
Icons.search,
|
Icons.search,
|
||||||
size: 20,
|
size: 20,
|
||||||
color: MapPalette.textPrimary,
|
color: _overlayPrimaryTextColor,
|
||||||
),
|
),
|
||||||
suffixIcon: hasQuery
|
suffixIcon: hasQuery
|
||||||
? IconButton(
|
? IconButton(
|
||||||
color: MapPalette.textPrimary,
|
color: _overlayPrimaryTextColor,
|
||||||
icon: const Icon(Icons.close, size: 18),
|
icon: const Icon(Icons.close, size: 18),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
@@ -1827,9 +1850,9 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
vertical: 12,
|
vertical: 12,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
color: MapPalette.textPrimary,
|
color: _overlayPrimaryTextColor,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
),
|
),
|
||||||
cursorColor: MapPalette.selected,
|
cursorColor: MapPalette.selected,
|
||||||
@@ -1841,9 +1864,9 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Material(
|
Material(
|
||||||
color: MapPalette.panelDark,
|
color: _overlayPanelColor,
|
||||||
shape: StadiumBorder(
|
shape: StadiumBorder(
|
||||||
side: const BorderSide(color: MapPalette.border),
|
side: BorderSide(color: _overlayBorderColor),
|
||||||
),
|
),
|
||||||
clipBehavior: Clip.antiAlias,
|
clipBehavior: Clip.antiAlias,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
@@ -1867,17 +1890,17 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
style: MeshTheme.mono(
|
style: MeshTheme.mono(
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
fontWeight: FontWeight.w700,
|
fontWeight: FontWeight.w700,
|
||||||
color: MapPalette.textPrimary,
|
color: _overlayPrimaryTextColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 2),
|
const SizedBox(width: 2),
|
||||||
AnimatedRotation(
|
AnimatedRotation(
|
||||||
turns: _statsExpanded ? 0.5 : 0,
|
turns: _statsExpanded ? 0.5 : 0,
|
||||||
duration: const Duration(milliseconds: 200),
|
duration: const Duration(milliseconds: 200),
|
||||||
child: const Icon(
|
child: Icon(
|
||||||
Icons.expand_more,
|
Icons.expand_more,
|
||||||
size: 16,
|
size: 16,
|
||||||
color: MapPalette.textPrimary,
|
color: _overlayPrimaryTextColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -1976,12 +1999,12 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
color: selected
|
color: selected
|
||||||
? Color.alphaBlend(
|
? Color.alphaBlend(
|
||||||
accent.withValues(alpha: 0.34),
|
accent.withValues(alpha: 0.34),
|
||||||
MapPalette.panelDark,
|
_overlayPanelColor,
|
||||||
)
|
)
|
||||||
: MapPalette.panelDark,
|
: _overlayPanelColor,
|
||||||
shape: StadiumBorder(
|
shape: StadiumBorder(
|
||||||
side: BorderSide(
|
side: BorderSide(
|
||||||
color: selected ? accent : MapPalette.border,
|
color: selected ? accent : _overlayBorderColor,
|
||||||
width: selected ? 1.5 : 1,
|
width: selected ? 1.5 : 1,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -1997,11 +2020,7 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
if (selected) ...[
|
if (selected) ...[
|
||||||
const Icon(
|
Icon(Icons.check, size: 13, color: _overlayPrimaryTextColor),
|
||||||
Icons.check,
|
|
||||||
size: 13,
|
|
||||||
color: MapPalette.textPrimary,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
],
|
],
|
||||||
Text(
|
Text(
|
||||||
@@ -2010,8 +2029,8 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
fontSize: 12.5,
|
fontSize: 12.5,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
color: selected
|
color: selected
|
||||||
? MapPalette.textPrimary
|
? _overlayPrimaryTextColor
|
||||||
: MapPalette.textSecondary,
|
: _overlaySecondaryTextColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -2041,14 +2060,14 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
margin: const EdgeInsets.only(top: 6),
|
margin: const EdgeInsets.only(top: 6),
|
||||||
constraints: const BoxConstraints(maxHeight: 300),
|
constraints: const BoxConstraints(maxHeight: 300),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: MapPalette.panelDark,
|
color: _overlayPanelColor,
|
||||||
borderRadius: BorderRadius.circular(MeshRadii.md),
|
borderRadius: BorderRadius.circular(MeshRadii.md),
|
||||||
border: Border.all(color: MapPalette.border),
|
border: Border.all(color: _overlayBorderColor),
|
||||||
boxShadow: const [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: MapPalette.markerShadow,
|
color: _overlayShadowColor,
|
||||||
blurRadius: 10,
|
blurRadius: 10,
|
||||||
offset: Offset(0, 4),
|
offset: const Offset(0, 4),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -2057,8 +2076,8 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
child: Text(
|
child: Text(
|
||||||
context.l10n.map_noResults,
|
context.l10n.map_noResults,
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
color: MapPalette.textSecondary,
|
color: _overlaySecondaryTextColor,
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -2068,7 +2087,7 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||||
itemCount: results.length,
|
itemCount: results.length,
|
||||||
separatorBuilder: (_, _) =>
|
separatorBuilder: (_, _) =>
|
||||||
const Divider(height: 1, color: MapPalette.border),
|
Divider(height: 1, color: _overlayBorderColor),
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final c = results[index];
|
final c = results[index];
|
||||||
final color = _getNodeColor(c.type);
|
final color = _getNodeColor(c.type);
|
||||||
@@ -2089,10 +2108,10 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
c.name,
|
c.name,
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 13.5,
|
fontSize: 13.5,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
color: MapPalette.textPrimary,
|
color: _overlayPrimaryTextColor,
|
||||||
),
|
),
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
@@ -2100,7 +2119,7 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
c.publicKeyHex.substring(0, 12),
|
c.publicKeyHex.substring(0, 12),
|
||||||
style: MeshTheme.mono(
|
style: MeshTheme.mono(
|
||||||
fontSize: 10.5,
|
fontSize: 10.5,
|
||||||
color: MapPalette.textSecondary,
|
color: _overlaySecondaryTextColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -2110,13 +2129,13 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
Icon(
|
Icon(
|
||||||
Icons.chevron_right,
|
Icons.chevron_right,
|
||||||
size: 18,
|
size: 18,
|
||||||
color: MapPalette.textSecondary,
|
color: _overlaySecondaryTextColor,
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
Text(
|
Text(
|
||||||
context.l10n.map_noGps.toUpperCase(),
|
context.l10n.map_noGps.toUpperCase(),
|
||||||
style: MeshTheme.accentLabel(
|
style: MeshTheme.accentLabel(
|
||||||
color: MapPalette.textMuted,
|
color: _overlayMutedTextColor,
|
||||||
fontSize: 8.5,
|
fontSize: 8.5,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -2176,14 +2195,14 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
width: 230,
|
width: 230,
|
||||||
padding: const EdgeInsets.fromLTRB(14, 12, 14, 12),
|
padding: const EdgeInsets.fromLTRB(14, 12, 14, 12),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: MapPalette.panelDark,
|
color: _overlayPanelColor,
|
||||||
borderRadius: BorderRadius.circular(MeshRadii.md),
|
borderRadius: BorderRadius.circular(MeshRadii.md),
|
||||||
border: Border.all(color: MapPalette.border),
|
border: Border.all(color: _overlayBorderColor),
|
||||||
boxShadow: const [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: MapPalette.markerShadow,
|
color: _overlayShadowColor,
|
||||||
blurRadius: 10,
|
blurRadius: 10,
|
||||||
offset: Offset(0, 4),
|
offset: const Offset(0, 4),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -2200,7 +2219,7 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
),
|
),
|
||||||
_statRow(context.l10n.map_hidden, hiddenCount, MapPalette.offline),
|
_statRow(context.l10n.map_hidden, hiddenCount, MapPalette.offline),
|
||||||
_statRow(context.l10n.map_markers, pinCount, MapPalette.shared),
|
_statRow(context.l10n.map_markers, pinCount, MapPalette.shared),
|
||||||
const Divider(height: 16, color: MapPalette.border),
|
Divider(height: 16, color: _overlayBorderColor),
|
||||||
_buildLegendItem(
|
_buildLegendItem(
|
||||||
Icons.person,
|
Icons.person,
|
||||||
context.l10n.map_chat,
|
context.l10n.map_chat,
|
||||||
@@ -2230,7 +2249,7 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
_buildLegendItem(
|
_buildLegendItem(
|
||||||
Icons.not_listed_location,
|
Icons.not_listed_location,
|
||||||
context.l10n.map_guessedLocation,
|
context.l10n.map_guessedLocation,
|
||||||
MapPalette.textMuted,
|
_overlayMutedTextColor,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -2251,7 +2270,10 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
label,
|
label,
|
||||||
style: TextStyle(fontSize: 12.5, color: MapPalette.textSecondary),
|
style: TextStyle(
|
||||||
|
fontSize: 12.5,
|
||||||
|
color: _overlaySecondaryTextColor,
|
||||||
|
),
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -2260,7 +2282,7 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
style: MeshTheme.mono(
|
style: MeshTheme.mono(
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
fontWeight: FontWeight.w700,
|
fontWeight: FontWeight.w700,
|
||||||
color: MapPalette.textPrimary,
|
color: _overlayPrimaryTextColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -2293,8 +2315,8 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
child: MeshCard(
|
child: MeshCard(
|
||||||
margin: EdgeInsets.zero,
|
margin: EdgeInsets.zero,
|
||||||
padding: const EdgeInsets.fromLTRB(14, 12, 8, 12),
|
padding: const EdgeInsets.fromLTRB(14, 12, 8, 12),
|
||||||
color: MapPalette.panelDark,
|
color: _overlayPanelColor,
|
||||||
borderColor: MapPalette.border,
|
borderColor: _overlayBorderColor,
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@@ -2317,10 +2339,10 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
Flexible(
|
Flexible(
|
||||||
child: Text(
|
child: Text(
|
||||||
contact.name,
|
contact.name,
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 15,
|
fontSize: 15,
|
||||||
fontWeight: FontWeight.w700,
|
fontWeight: FontWeight.w700,
|
||||||
color: MapPalette.textPrimary,
|
color: _overlayPrimaryTextColor,
|
||||||
),
|
),
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
@@ -2348,9 +2370,9 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
Flexible(
|
Flexible(
|
||||||
child: Text(
|
child: Text(
|
||||||
contact.typeLabel(context.l10n),
|
contact.typeLabel(context.l10n),
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 11.5,
|
fontSize: 11.5,
|
||||||
color: MapPalette.textSecondary,
|
color: _overlaySecondaryTextColor,
|
||||||
),
|
),
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
@@ -2362,13 +2384,13 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
),
|
),
|
||||||
if (pos != null)
|
if (pos != null)
|
||||||
IconButton(
|
IconButton(
|
||||||
color: MapPalette.textPrimary,
|
color: _overlayPrimaryTextColor,
|
||||||
icon: const Icon(Icons.center_focus_strong, size: 20),
|
icon: const Icon(Icons.center_focus_strong, size: 20),
|
||||||
tooltip: context.l10n.map_centerOnNode,
|
tooltip: context.l10n.map_centerOnNode,
|
||||||
onPressed: () => _mapController.move(pos, max(_zoom, 15)),
|
onPressed: () => _mapController.move(pos, max(_zoom, 15)),
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
color: MapPalette.textPrimary,
|
color: _overlayPrimaryTextColor,
|
||||||
icon: const Icon(Icons.close, size: 20),
|
icon: const Icon(Icons.close, size: 20),
|
||||||
onPressed: _clearSelection,
|
onPressed: _clearSelection,
|
||||||
),
|
),
|
||||||
@@ -2432,14 +2454,17 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
Text(
|
Text(
|
||||||
label.toUpperCase(),
|
label.toUpperCase(),
|
||||||
style: MeshTheme.accentLabel(
|
style: MeshTheme.accentLabel(
|
||||||
color: MapPalette.textMuted,
|
color: _overlayMutedTextColor,
|
||||||
fontSize: 8,
|
fontSize: 8,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 1),
|
const SizedBox(height: 1),
|
||||||
Text(
|
Text(
|
||||||
value,
|
value,
|
||||||
style: MeshTheme.mono(fontSize: 11.5, color: MapPalette.textPrimary),
|
style: MeshTheme.mono(
|
||||||
|
fontSize: 11.5,
|
||||||
|
color: _overlayPrimaryTextColor,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -3608,14 +3633,14 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
right: 16,
|
right: 16,
|
||||||
child: DecoratedBox(
|
child: DecoratedBox(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: MapPalette.panelDark,
|
color: _overlayPanelColor,
|
||||||
borderRadius: BorderRadius.circular(MeshRadii.md),
|
borderRadius: BorderRadius.circular(MeshRadii.md),
|
||||||
border: Border.all(color: MapPalette.border),
|
border: Border.all(color: _overlayBorderColor),
|
||||||
boxShadow: const [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: MapPalette.markerShadow,
|
color: _overlayShadowColor,
|
||||||
blurRadius: 10,
|
blurRadius: 10,
|
||||||
offset: Offset(0, 4),
|
offset: const Offset(0, 4),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -3628,18 +3653,27 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
l10n.contacts_pathTrace,
|
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) const SizedBox(height: 8),
|
||||||
if (_pathTrace.isEmpty)
|
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),
|
const SizedBox(height: 6),
|
||||||
if (_pathTrace.isNotEmpty)
|
if (_pathTrace.isNotEmpty)
|
||||||
Text(
|
Text(
|
||||||
"${l10n.path_currentPathLabel} ${formatDistance(getPathDistanceMeters(_points), isImperial: isImperial)}",
|
"${l10n.path_currentPathLabel} ${formatDistance(getPathDistanceMeters(_points), isImperial: isImperial)}",
|
||||||
style: MeshTheme.mono(
|
style: MeshTheme.mono(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
color: MapPalette.textSecondary,
|
color: _overlaySecondaryTextColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SelectableText(
|
SelectableText(
|
||||||
|
|||||||
@@ -276,7 +276,7 @@ class _NeighborsScreenState extends State<NeighborsScreen> {
|
|||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Column(
|
title: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
@@ -296,7 +296,7 @@ class _NeighborsScreenState extends State<NeighborsScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
centerTitle: false,
|
centerTitle: true,
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(isFloodMode ? Icons.waves : Icons.route),
|
icon: Icon(isFloodMode ? Icons.waves : Icons.route),
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import 'package:meshcore_open/services/path_history_service.dart';
|
|||||||
import 'package:meshcore_open/utils/app_logger.dart';
|
import 'package:meshcore_open/utils/app_logger.dart';
|
||||||
import 'package:meshcore_open/widgets/path_map_ui.dart';
|
import 'package:meshcore_open/widgets/path_map_ui.dart';
|
||||||
import 'package:meshcore_open/widgets/snr_indicator.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 'package:provider/provider.dart';
|
||||||
import '../theme/mesh_theme.dart';
|
import '../theme/mesh_theme.dart';
|
||||||
|
|
||||||
@@ -1576,7 +1575,7 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen>
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
children: [
|
children: [
|
||||||
ThemedMapTileLayer(tileCache: tileCache),
|
tileCache.buildTileLayer(context),
|
||||||
AnimatedBuilder(
|
AnimatedBuilder(
|
||||||
animation: _playback,
|
animation: _playback,
|
||||||
builder: (context, _) {
|
builder: (context, _) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:typed_data';
|
import 'dart:math';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import '../l10n/l10n.dart';
|
import '../l10n/l10n.dart';
|
||||||
import '../models/contact.dart';
|
import '../models/contact.dart';
|
||||||
@@ -11,6 +12,7 @@ import '../services/storage_service.dart';
|
|||||||
import '../theme/mesh_theme.dart';
|
import '../theme/mesh_theme.dart';
|
||||||
import '../widgets/mesh_ui.dart';
|
import '../widgets/mesh_ui.dart';
|
||||||
import '../widgets/routing_sheet.dart';
|
import '../widgets/routing_sheet.dart';
|
||||||
|
import '../utils/keys.dart';
|
||||||
import '../helpers/snack_bar_builder.dart';
|
import '../helpers/snack_bar_builder.dart';
|
||||||
|
|
||||||
class RepeaterSettingsScreen extends StatefulWidget {
|
class RepeaterSettingsScreen extends StatefulWidget {
|
||||||
@@ -44,6 +46,7 @@ enum _SettingField {
|
|||||||
advertInterval,
|
advertInterval,
|
||||||
floodAdvertInterval,
|
floodAdvertInterval,
|
||||||
pathHashMode,
|
pathHashMode,
|
||||||
|
prvKey,
|
||||||
txDelay,
|
txDelay,
|
||||||
directTxDelay,
|
directTxDelay,
|
||||||
intThresh,
|
intThresh,
|
||||||
@@ -109,6 +112,8 @@ class _RepeaterSettingsScreenState extends State<RepeaterSettingsScreen> {
|
|||||||
bool _refreshingIntThresh = false;
|
bool _refreshingIntThresh = false;
|
||||||
bool _refreshingAgcResetInterval = false;
|
bool _refreshingAgcResetInterval = false;
|
||||||
bool _runningAction = false;
|
bool _runningAction = false;
|
||||||
|
bool _searchingForKeyPair = false;
|
||||||
|
bool _stopSearchingForKeyPair = false;
|
||||||
StreamSubscription<Uint8List>? _frameSubscription;
|
StreamSubscription<Uint8List>? _frameSubscription;
|
||||||
RepeaterCommandService? _commandService;
|
RepeaterCommandService? _commandService;
|
||||||
|
|
||||||
@@ -154,6 +159,11 @@ class _RepeaterSettingsScreenState extends State<RepeaterSettingsScreen> {
|
|||||||
|
|
||||||
// Advanced
|
// Advanced
|
||||||
int _pathHashMode = 0; // 0-2
|
int _pathHashMode = 0; // 0-2
|
||||||
|
final TextEditingController _prvKeyController = TextEditingController();
|
||||||
|
final TextEditingController _pubKeyController = TextEditingController();
|
||||||
|
final TextEditingController _prvKeyPrefixController = TextEditingController();
|
||||||
|
final KeyPairSearcher _keyPairSearcher = KeyPairSearcher();
|
||||||
|
final int _searchChunkTime = 30; // time in ms to search before yielding
|
||||||
final TextEditingController _txDelayController = TextEditingController();
|
final TextEditingController _txDelayController = TextEditingController();
|
||||||
final TextEditingController _directTxDelayController =
|
final TextEditingController _directTxDelayController =
|
||||||
TextEditingController();
|
TextEditingController();
|
||||||
@@ -206,6 +216,10 @@ class _RepeaterSettingsScreenState extends State<RepeaterSettingsScreen> {
|
|||||||
_txDelayController.dispose();
|
_txDelayController.dispose();
|
||||||
_directTxDelayController.dispose();
|
_directTxDelayController.dispose();
|
||||||
_intThreshController.dispose();
|
_intThreshController.dispose();
|
||||||
|
_prvKeyController.dispose();
|
||||||
|
_pubKeyController.dispose();
|
||||||
|
_prvKeyPrefixController.dispose();
|
||||||
|
_stopSearchingForKeyPair = true;
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -860,6 +874,12 @@ class _RepeaterSettingsScreenState extends State<RepeaterSettingsScreen> {
|
|||||||
command: 'set path.hash.mode $_pathHashMode',
|
command: 'set path.hash.mode $_pathHashMode',
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
if (_dirtyFields.contains(_SettingField.prvKey)) {
|
||||||
|
final v = _prvKeyController.text.trim();
|
||||||
|
if (v != "") {
|
||||||
|
pending.add((field: _SettingField.prvKey, command: 'set prv.key $v'));
|
||||||
|
}
|
||||||
|
}
|
||||||
if (_dirtyFields.contains(_SettingField.txDelay) &&
|
if (_dirtyFields.contains(_SettingField.txDelay) &&
|
||||||
_txDelayController.text.isNotEmpty) {
|
_txDelayController.text.isNotEmpty) {
|
||||||
final v = double.tryParse(_txDelayController.text.trim());
|
final v = double.tryParse(_txDelayController.text.trim());
|
||||||
@@ -1005,6 +1025,36 @@ class _RepeaterSettingsScreenState extends State<RepeaterSettingsScreen> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _startSearchingForKeyPair() async {
|
||||||
|
if (_searchingForKeyPair) return;
|
||||||
|
setState(() {
|
||||||
|
_searchingForKeyPair = true;
|
||||||
|
_stopSearchingForKeyPair = false;
|
||||||
|
});
|
||||||
|
_searchForKeyPair();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _searchForKeyPair() async {
|
||||||
|
if (_stopSearchingForKeyPair) {
|
||||||
|
if (mounted) setState(() => _searchingForKeyPair = false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final String prefix = _prvKeyPrefixController.text.trim();
|
||||||
|
MCKeyPair? keys = await _keyPairSearcher.findMatchingKeyPair(
|
||||||
|
prefix,
|
||||||
|
_searchChunkTime,
|
||||||
|
);
|
||||||
|
if (keys == null) {
|
||||||
|
// Ran out of time; schedule another attempt.
|
||||||
|
Timer.run(_searchForKeyPair);
|
||||||
|
} else {
|
||||||
|
setState(() => _searchingForKeyPair = false);
|
||||||
|
_prvKeyController.text = pubKeyToHex(keys.private);
|
||||||
|
_pubKeyController.text = pubKeyToHex(keys.public);
|
||||||
|
_markChanged(_SettingField.prvKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildInlineRefreshButton({
|
Widget _buildInlineRefreshButton({
|
||||||
required bool isRefreshing,
|
required bool isRefreshing,
|
||||||
required VoidCallback onRefresh,
|
required VoidCallback onRefresh,
|
||||||
@@ -1069,6 +1119,7 @@ class _RepeaterSettingsScreenState extends State<RepeaterSettingsScreen> {
|
|||||||
_buildOwnerInfoCard(),
|
_buildOwnerInfoCard(),
|
||||||
_buildActionsCard(),
|
_buildActionsCard(),
|
||||||
_buildAdvancedCard(),
|
_buildAdvancedCard(),
|
||||||
|
_buildKeysCard(),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
_buildDangerZoneCard(),
|
_buildDangerZoneCard(),
|
||||||
],
|
],
|
||||||
@@ -1984,6 +2035,127 @@ class _RepeaterSettingsScreenState extends State<RepeaterSettingsScreen> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildKeysCard() {
|
||||||
|
final l10n = context.l10n;
|
||||||
|
return MeshCard(
|
||||||
|
child: ExpansionTile(
|
||||||
|
leading: const Icon(Icons.vpn_key),
|
||||||
|
title: Text(
|
||||||
|
l10n.repeater_keySettings,
|
||||||
|
style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w600),
|
||||||
|
),
|
||||||
|
subtitle: Text(l10n.repeater_keySettingsSubtitle),
|
||||||
|
childrenPadding: const EdgeInsets.fromLTRB(0, 8, 0, 4),
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: TextField(
|
||||||
|
controller: _prvKeyController,
|
||||||
|
inputFormatters: [
|
||||||
|
FilteringTextInputFormatter.allow(RegExp("[0-9a-fA-F]")),
|
||||||
|
],
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: l10n.repeater_prvKey,
|
||||||
|
helperText: l10n.repeater_prvKeyHelper,
|
||||||
|
helperMaxLines: 3,
|
||||||
|
border: const OutlineInputBorder(),
|
||||||
|
),
|
||||||
|
onChanged: (_) {
|
||||||
|
_pubKeyController.text = "";
|
||||||
|
if (_prvKeyController.text.isNotEmpty) {
|
||||||
|
_markChanged(_SettingField.prvKey);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 8),
|
||||||
|
child: _searchingForKeyPair
|
||||||
|
? IconButton(
|
||||||
|
icon: const Icon(Icons.cancel, size: 24),
|
||||||
|
onPressed: (() => _stopSearchingForKeyPair = true),
|
||||||
|
tooltip: l10n.repeater_stopGeneratingPrvKey,
|
||||||
|
visualDensity: VisualDensity.compact,
|
||||||
|
)
|
||||||
|
: IconButton(
|
||||||
|
icon: const Icon(Icons.casino_rounded, size: 24),
|
||||||
|
onPressed: () {
|
||||||
|
_startSearchingForKeyPair();
|
||||||
|
},
|
||||||
|
tooltip: l10n.repeater_generatePrvKey,
|
||||||
|
visualDensity: VisualDensity.compact,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: TextField(
|
||||||
|
readOnly: true,
|
||||||
|
controller: _pubKeyController,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(
|
||||||
|
context,
|
||||||
|
).textTheme.headlineSmall?.color?.withValues(alpha: 0.6),
|
||||||
|
),
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: l10n.repeater_pubKey,
|
||||||
|
helperText: l10n.repeater_pubKeyHelper,
|
||||||
|
helperMaxLines: 3,
|
||||||
|
border: const OutlineInputBorder(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 12),
|
||||||
|
child: SizedBox(
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
child: _searchingForKeyPair
|
||||||
|
? CircularProgressIndicator(strokeWidth: 2)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: TextField(
|
||||||
|
controller: _prvKeyPrefixController,
|
||||||
|
maxLength: 4,
|
||||||
|
inputFormatters: [
|
||||||
|
FilteringTextInputFormatter.allow(RegExp("[0-9a-fA-F]")),
|
||||||
|
],
|
||||||
|
onChanged: (_) => setState(() => {}), // updates helper text
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: l10n.repeater_pubKeyPrefix,
|
||||||
|
helperText: l10n.repeater_pubKeyPrefixHelper(
|
||||||
|
pow(16, _prvKeyPrefixController.text.length).toInt(),
|
||||||
|
),
|
||||||
|
helperMaxLines: 3,
|
||||||
|
border: const OutlineInputBorder(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 48),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildDangerZoneCard() {
|
Widget _buildDangerZoneCard() {
|
||||||
final l10n = context.l10n;
|
final l10n = context.l10n;
|
||||||
return MeshCard(
|
return MeshCard(
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import '../connector/meshcore_connector.dart';
|
|||||||
import '../connector/meshcore_protocol.dart';
|
import '../connector/meshcore_protocol.dart';
|
||||||
import '../l10n/l10n.dart';
|
import '../l10n/l10n.dart';
|
||||||
import '../models/radio_settings.dart';
|
import '../models/radio_settings.dart';
|
||||||
|
import '../services/app_settings_service.dart';
|
||||||
import '../services/app_debug_log_service.dart';
|
import '../services/app_debug_log_service.dart';
|
||||||
import '../theme/mesh_theme.dart';
|
import '../theme/mesh_theme.dart';
|
||||||
import '../widgets/app_bar.dart';
|
import '../widgets/app_bar.dart';
|
||||||
@@ -862,6 +863,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
|
|
||||||
void _editLocation(BuildContext context, MeshCoreConnector connector) {
|
void _editLocation(BuildContext context, MeshCoreConnector connector) {
|
||||||
final l10n = context.l10n;
|
final l10n = context.l10n;
|
||||||
|
final settingsService = context.read<AppSettingsService>();
|
||||||
final latController = TextEditingController();
|
final latController = TextEditingController();
|
||||||
final lonController = TextEditingController();
|
final lonController = TextEditingController();
|
||||||
final intervalController = TextEditingController();
|
final intervalController = TextEditingController();
|
||||||
@@ -873,9 +875,9 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
final bool hasGPS = customVars.containsKey("gps");
|
final bool hasGPS = customVars.containsKey("gps");
|
||||||
bool isGPSEnabled = customVars["gps"] == "1";
|
bool isGPSEnabled = customVars["gps"] == "1";
|
||||||
|
|
||||||
// Read current interval or default to 900 (15 minutes)
|
final currentInterval = settingsService.resolvedGpsIntervalSeconds(
|
||||||
final currentInterval =
|
customVars,
|
||||||
int.tryParse(customVars["gps_interval"] ?? "") ?? 900;
|
);
|
||||||
intervalController.text = currentInterval.toString();
|
intervalController.text = currentInterval.toString();
|
||||||
|
|
||||||
String? intervalError;
|
String? intervalError;
|
||||||
@@ -973,7 +975,11 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
|
|
||||||
if (interval != null) {
|
if (interval != null) {
|
||||||
await connector.setCustomVar("gps_interval:$interval");
|
await settingsService.setGpsIntervalSeconds(
|
||||||
|
interval,
|
||||||
|
writeToDevice: (value) =>
|
||||||
|
connector.setCustomVar("gps_interval:$value"),
|
||||||
|
);
|
||||||
await connector.refreshDeviceInfo();
|
await connector.refreshDeviceInfo();
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) return;
|
||||||
showDismissibleSnackBar(
|
showDismissibleSnackBar(
|
||||||
@@ -1273,12 +1279,15 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
|
|
||||||
void _privacySettings(BuildContext context, MeshCoreConnector connector) {
|
void _privacySettings(BuildContext context, MeshCoreConnector connector) {
|
||||||
final l10n = context.l10n;
|
final l10n = context.l10n;
|
||||||
|
final settingsService = context.read<AppSettingsService>();
|
||||||
|
|
||||||
int telemetryMode = connector.telemetryModeBase;
|
int telemetryMode = connector.telemetryModeBase;
|
||||||
int telemetryLocMode = connector.telemetryModeLoc;
|
int telemetryLocMode = connector.telemetryModeLoc;
|
||||||
int telemetryEnvMode = connector.telemetryModeEnv;
|
int telemetryEnvMode = connector.telemetryModeEnv;
|
||||||
bool advertLocPolicy = connector.advertLocationPolicy == 0 ? false : true;
|
bool advertLocPolicy = connector.advertLocationPolicy == 0 ? false : true;
|
||||||
int multiAcks = connector.multiAcks;
|
int multiAcks = connector.multiAcks;
|
||||||
|
bool autoZeroHopAdvertOnGpsUpdate =
|
||||||
|
settingsService.settings.autoSendZeroHopAdvertOnGpsUpdate;
|
||||||
|
|
||||||
final telemModeBase = [
|
final telemModeBase = [
|
||||||
DropdownMenuItem(value: teleModeDeny, child: Text(l10n.settings_denyAll)),
|
DropdownMenuItem(value: teleModeDeny, child: Text(l10n.settings_denyAll)),
|
||||||
@@ -1313,6 +1322,20 @@ void _privacySettings(BuildContext context, MeshCoreConnector connector) {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
|
FeatureToggleRow(
|
||||||
|
title: l10n.settings_autoZeroHopAdvertOnGpsUpdate,
|
||||||
|
subtitle: l10n.settings_autoZeroHopAdvertOnGpsUpdateSubtitle,
|
||||||
|
value: autoZeroHopAdvertOnGpsUpdate,
|
||||||
|
enabled: advertLocPolicy,
|
||||||
|
onChanged: advertLocPolicy
|
||||||
|
? (value) {
|
||||||
|
setDialogState(
|
||||||
|
() => autoZeroHopAdvertOnGpsUpdate = value,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
SwitchListTile(
|
SwitchListTile(
|
||||||
title: Text(l10n.settings_multiAck),
|
title: Text(l10n.settings_multiAck),
|
||||||
value: multiAcks == 1,
|
value: multiAcks == 1,
|
||||||
@@ -1381,6 +1404,9 @@ void _privacySettings(BuildContext context, MeshCoreConnector connector) {
|
|||||||
advertLocPolicy ? 1 : 0,
|
advertLocPolicy ? 1 : 0,
|
||||||
multiAcks,
|
multiAcks,
|
||||||
);
|
);
|
||||||
|
await settingsService.setAutoSendZeroHopAdvertOnGpsUpdate(
|
||||||
|
autoZeroHopAdvertOnGpsUpdate,
|
||||||
|
);
|
||||||
await connector.refreshDeviceInfo();
|
await connector.refreshDeviceInfo();
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) return;
|
||||||
showDismissibleSnackBar(
|
showDismissibleSnackBar(
|
||||||
|
|||||||
@@ -334,7 +334,7 @@ class _TelemetryScreenState extends State<TelemetryScreen> {
|
|||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Column(
|
title: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
@@ -350,7 +350,7 @@ class _TelemetryScreenState extends State<TelemetryScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
centerTitle: false,
|
centerTitle: true,
|
||||||
bottom: const SyncProgressAppBarBottom(),
|
bottom: const SyncProgressAppBarBottom(),
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
|
|||||||
@@ -13,6 +13,14 @@ class AppSettingsService extends ChangeNotifier {
|
|||||||
|
|
||||||
AppSettings get settings => _settings;
|
AppSettings get settings => _settings;
|
||||||
|
|
||||||
|
int resolvedGpsIntervalSeconds(Map<String, String>? deviceCustomVars) {
|
||||||
|
final deviceValue = int.tryParse(deviceCustomVars?['gps_interval'] ?? '');
|
||||||
|
if (deviceValue != null && deviceValue >= 0) {
|
||||||
|
return deviceValue;
|
||||||
|
}
|
||||||
|
return _settings.gpsIntervalSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
String batteryChemistryForDevice(String deviceId) {
|
String batteryChemistryForDevice(String deviceId) {
|
||||||
final stored = _settings.batteryChemistryByDeviceId[deviceId];
|
final stored = _settings.batteryChemistryByDeviceId[deviceId];
|
||||||
if (stored == 'liion') return 'nmc';
|
if (stored == 'liion') return 'nmc';
|
||||||
@@ -112,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 {
|
Future<void> setNotificationsEnabled(bool value) async {
|
||||||
await updateSettings(_settings.copyWith(notificationsEnabled: value));
|
await updateSettings(_settings.copyWith(notificationsEnabled: value));
|
||||||
}
|
}
|
||||||
@@ -128,6 +155,28 @@ class AppSettingsService extends ChangeNotifier {
|
|||||||
await updateSettings(_settings.copyWith(notifyOnNewAdvert: value));
|
await updateSettings(_settings.copyWith(notifyOnNewAdvert: value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> setAutoSendZeroHopAdvertOnGpsUpdate(bool value) async {
|
||||||
|
await updateSettings(
|
||||||
|
_settings.copyWith(autoSendZeroHopAdvertOnGpsUpdate: value),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> setGpsIntervalSeconds(
|
||||||
|
int value, {
|
||||||
|
Future<void> Function(int value)? writeToDevice,
|
||||||
|
}) async {
|
||||||
|
await updateSettings(_settings.copyWith(gpsIntervalSeconds: value));
|
||||||
|
if (writeToDevice == null) return;
|
||||||
|
try {
|
||||||
|
await writeToDevice(value);
|
||||||
|
} catch (e) {
|
||||||
|
appLogger.warn(
|
||||||
|
'Failed to write GPS interval to device: $e',
|
||||||
|
tag: 'AppSettings',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> setAutoRouteRotationEnabled(bool value) async {
|
Future<void> setAutoRouteRotationEnabled(bool value) async {
|
||||||
await updateSettings(_settings.copyWith(autoRouteRotationEnabled: value));
|
await updateSettings(_settings.copyWith(autoRouteRotationEnabled: value));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,232 @@
|
|||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
|
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_map/flutter_map.dart';
|
|
||||||
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
||||||
|
import 'package:flutter_map/flutter_map.dart';
|
||||||
|
|
||||||
const String kMapTileUrlTemplate =
|
import '../models/app_settings.dart';
|
||||||
'https://tile.openstreetmap.org/{z}/{x}/{y}.png';
|
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 {
|
class MapTileCacheProgress {
|
||||||
final int completed;
|
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 cacheKey = 'map_tile_cache';
|
||||||
static const String userAgentPackageName = 'com.meshcore.open';
|
static const String userAgentPackageName = 'com.meshcore.open';
|
||||||
static const int defaultMinZoom = 10;
|
static const int defaultMinZoom = 10;
|
||||||
static const int defaultMaxZoom = 15;
|
static const int defaultMaxZoom = 15;
|
||||||
|
|
||||||
|
final AppSettingsService appSettingsService;
|
||||||
final BaseCacheManager cacheManager;
|
final BaseCacheManager cacheManager;
|
||||||
late final TileProvider tileProvider;
|
late final TileProvider tileProvider;
|
||||||
|
|
||||||
MapTileCacheService({BaseCacheManager? cacheManager})
|
MapTileCacheService({
|
||||||
: cacheManager =
|
required this.appSettingsService,
|
||||||
cacheManager ??
|
BaseCacheManager? cacheManager,
|
||||||
CacheManager(
|
}) : cacheManager =
|
||||||
Config(
|
cacheManager ??
|
||||||
cacheKey,
|
CacheManager(
|
||||||
stalePeriod: const Duration(days: 365),
|
Config(
|
||||||
maxNrOfCacheObjects: 200000,
|
cacheKey,
|
||||||
),
|
stalePeriod: const Duration(days: 365),
|
||||||
) {
|
maxNrOfCacheObjects: 200000,
|
||||||
|
),
|
||||||
|
) {
|
||||||
tileProvider = CachedNetworkTileProvider(cacheManager: this.cacheManager);
|
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 => {
|
Map<String, String> get defaultHeaders => {
|
||||||
'User-Agent': 'flutter_map ($userAgentPackageName)',
|
'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 {
|
Future<void> clearCache() async {
|
||||||
await cacheManager.emptyCache();
|
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) {
|
int estimateTileCount(LatLngBounds bounds, int minZoom, int maxZoom) {
|
||||||
final safeMin = math.min(minZoom, maxZoom);
|
final safeMin = math.min(minZoom, maxZoom);
|
||||||
final safeMax = math.max(minZoom, maxZoom);
|
final safeMax = math.max(minZoom, maxZoom);
|
||||||
@@ -89,6 +526,7 @@ class MapTileCacheService {
|
|||||||
final total = estimateTileCount(bounds, safeMin, safeMax);
|
final total = estimateTileCount(bounds, safeMin, safeMax);
|
||||||
final authHeaders = headers ?? defaultHeaders;
|
final authHeaders = headers ?? defaultHeaders;
|
||||||
final safeConcurrency = math.max(1, concurrentDownloads);
|
final safeConcurrency = math.max(1, concurrentDownloads);
|
||||||
|
final currentTemplate = urlTemplate;
|
||||||
int completed = 0;
|
int completed = 0;
|
||||||
int failed = 0;
|
int failed = 0;
|
||||||
|
|
||||||
@@ -124,7 +562,7 @@ class MapTileCacheService {
|
|||||||
final tileBounds = _tileBoundsForBounds(bounds, zoom);
|
final tileBounds = _tileBoundsForBounds(bounds, zoom);
|
||||||
for (int x = tileBounds.minX; x <= tileBounds.maxX; x++) {
|
for (int x = tileBounds.minX; x <= tileBounds.maxX; x++) {
|
||||||
for (int y = tileBounds.minY; y <= tileBounds.maxY; y++) {
|
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);
|
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) {
|
_TileBounds _tileBoundsForBounds(LatLngBounds bounds, int zoom) {
|
||||||
final north = _clampLatitude(bounds.north);
|
final north = _clampLatitude(bounds.north);
|
||||||
final south = _clampLatitude(bounds.south);
|
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) {
|
int _lonToTileX(double lon, int zoom, int maxIndex) {
|
||||||
final n = 1 << zoom;
|
final n = 1 << zoom;
|
||||||
final value = ((lon + 180.0) / 360.0 * n).floor();
|
final value = ((lon + 180.0) / 360.0 * n).floor();
|
||||||
@@ -205,8 +703,34 @@ class MapTileCacheService {
|
|||||||
return lat.clamp(-maxLat, maxLat);
|
return lat.clamp(-maxLat, maxLat);
|
||||||
}
|
}
|
||||||
|
|
||||||
String _buildTileUrl(int x, int y, int zoom) {
|
LatLngBounds _tileBoundsForTile(int x, int y, int zoom) {
|
||||||
return kMapTileUrlTemplate
|
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('{z}', zoom.toString())
|
||||||
.replaceAll('{x}', x.toString())
|
.replaceAll('{x}', x.toString())
|
||||||
.replaceAll('{y}', y.toString());
|
.replaceAll('{y}', y.toString());
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import 'package:flutter/cupertino.dart' show CupertinoPageTransitionsBuilder;
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
/// MeshCore palette — high-contrast slate surfaces with sky-blue accents.
|
/// MeshCore palette — high-contrast slate surfaces with sky-blue accents.
|
||||||
@@ -12,16 +11,16 @@ class MeshPalette {
|
|||||||
static const bg3 = Color(0xFF1E293B);
|
static const bg3 = Color(0xFF1E293B);
|
||||||
static const bg4 = Color(0xFF334155);
|
static const bg4 = Color(0xFF334155);
|
||||||
|
|
||||||
// Lines
|
// Lines — lifted for clearer element separation on the near-black surface
|
||||||
static const line = Color(0xFF1E293B);
|
static const line = Color(0xFF2A3850);
|
||||||
static const line2 = Color(0xFF334155);
|
static const line2 = Color(0xFF3B4A61);
|
||||||
static const line3 = Color(0xFF475569);
|
static const line3 = Color(0xFF546376);
|
||||||
|
|
||||||
// Ink
|
// Ink — muted tones brightened for readable secondary/tertiary text in dark
|
||||||
static const ink = Color(0xFFF8FAFC);
|
static const ink = Color(0xFFF8FAFC);
|
||||||
static const ink2 = Color(0xFFCBD5E1);
|
static const ink2 = Color(0xFFD5DEE9);
|
||||||
static const ink3 = Color(0xFF94A3B8);
|
static const ink3 = Color(0xFFAAB6C6);
|
||||||
static const ink4 = Color(0xFF64748B);
|
static const ink4 = Color(0xFF828FA3);
|
||||||
|
|
||||||
// Signal-quality green (used only for SNR coloring, not UI chrome)
|
// Signal-quality green (used only for SNR coloring, not UI chrome)
|
||||||
static const signal = Color(0xFF22C55E);
|
static const signal = Color(0xFF22C55E);
|
||||||
@@ -395,9 +394,9 @@ class MeshTheme {
|
|||||||
labelTextStyle: WidgetStateProperty.resolveWith((states) {
|
labelTextStyle: WidgetStateProperty.resolveWith((states) {
|
||||||
final selected = states.contains(WidgetState.selected);
|
final selected = states.contains(WidgetState.selected);
|
||||||
return TextStyle(
|
return TextStyle(
|
||||||
fontFamily: MeshFonts.mono,
|
fontFamily: MeshFonts.sans,
|
||||||
fontFamilyFallback: MeshFonts.monoFallback,
|
fontFamilyFallback: MeshFonts.sansFallback,
|
||||||
fontSize: 10,
|
fontSize: 11.5,
|
||||||
fontWeight: selected ? FontWeight.w700 : FontWeight.w500,
|
fontWeight: selected ? FontWeight.w700 : FontWeight.w500,
|
||||||
letterSpacing: 0.1,
|
letterSpacing: 0.1,
|
||||||
color: selected ? scheme.onPrimary : scheme.onSurfaceVariant,
|
color: selected ? scheme.onPrimary : scheme.onSurfaceVariant,
|
||||||
@@ -448,7 +447,7 @@ class MeshTheme {
|
|||||||
pageTransitionsTheme: const PageTransitionsTheme(
|
pageTransitionsTheme: const PageTransitionsTheme(
|
||||||
builders: {
|
builders: {
|
||||||
TargetPlatform.android: FadeForwardsPageTransitionsBuilder(),
|
TargetPlatform.android: FadeForwardsPageTransitionsBuilder(),
|
||||||
TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
|
TargetPlatform.iOS: FadeForwardsPageTransitionsBuilder(),
|
||||||
TargetPlatform.linux: FadeForwardsPageTransitionsBuilder(),
|
TargetPlatform.linux: FadeForwardsPageTransitionsBuilder(),
|
||||||
TargetPlatform.macOS: FadeForwardsPageTransitionsBuilder(),
|
TargetPlatform.macOS: FadeForwardsPageTransitionsBuilder(),
|
||||||
TargetPlatform.windows: FadeForwardsPageTransitionsBuilder(),
|
TargetPlatform.windows: FadeForwardsPageTransitionsBuilder(),
|
||||||
@@ -583,14 +582,15 @@ class MeshTheme {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Small-caps mono label — used for section accents and chip labels.
|
/// Section-accent / chip label — sans for legibility, with light tracking
|
||||||
|
/// to keep the "label" feel that section headers rely on.
|
||||||
static TextStyle accentLabel({Color? color, double? fontSize}) {
|
static TextStyle accentLabel({Color? color, double? fontSize}) {
|
||||||
return TextStyle(
|
return TextStyle(
|
||||||
fontFamily: MeshFonts.mono,
|
fontFamily: MeshFonts.sans,
|
||||||
fontFamilyFallback: MeshFonts.monoFallback,
|
fontFamilyFallback: MeshFonts.sansFallback,
|
||||||
fontSize: fontSize ?? 9.5,
|
fontSize: fontSize ?? 11,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w700,
|
||||||
letterSpacing: 1.8,
|
letterSpacing: 0.6,
|
||||||
color: color,
|
color: color,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:math';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
import 'package:convert/convert.dart';
|
||||||
|
import 'package:cryptography/cryptography.dart';
|
||||||
|
|
||||||
|
Uint8List randomBytes(int length) {
|
||||||
|
final random = Random.secure();
|
||||||
|
final bytes = Uint8List(length);
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
bytes[i] = random.nextInt(256);
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
class MCKeyPair {
|
||||||
|
final Uint8List public;
|
||||||
|
final Uint8List private;
|
||||||
|
MCKeyPair(this.public, this.private);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<MCKeyPair> generateKeyPair() async {
|
||||||
|
final algo = Ed25519();
|
||||||
|
final seed = randomBytes(32);
|
||||||
|
final SimpleKeyPair keys = await algo.newKeyPairFromSeed(seed);
|
||||||
|
final Uint8List pubKeyBytes = Uint8List.fromList(
|
||||||
|
(await keys.extractPublicKey()).bytes,
|
||||||
|
);
|
||||||
|
// Ed25519 gives 32-byte public and private keys, but MeshCore
|
||||||
|
// uses an expanded 64-byte private key, so we have to make it
|
||||||
|
// ourselves. Luckily, we can do it such that it has the same public
|
||||||
|
// key, which we'd otherwise be unable to compute, since although the
|
||||||
|
// crypto library implements the elliptic curve computations we need,
|
||||||
|
// it doesn't make them available.
|
||||||
|
final Uint8List hash = Uint8List.fromList((await Sha512().hash(seed)).bytes);
|
||||||
|
hash[0] &= 248; // Clamp the scalar. This keeps the chosen point in the
|
||||||
|
hash[31] &= 63; // large elliptic curve subgroup, and is demanded both by
|
||||||
|
hash[31] |= 64; // Ed25519 and by the MeshCore repeater key validation code.
|
||||||
|
return MCKeyPair(pubKeyBytes, hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
class _KeyHashPrefix {
|
||||||
|
Uint8List _bytes = Uint8List(0);
|
||||||
|
Uint8List _mask = Uint8List(0);
|
||||||
|
_KeyHashPrefix(String prefix) {
|
||||||
|
final bool lengthIsOdd = (prefix.length % 2 == 1) ? true : false;
|
||||||
|
final String decodableString = lengthIsOdd ? "${prefix}0" : prefix;
|
||||||
|
_bytes = hex.decoder.convert(decodableString);
|
||||||
|
_mask = Uint8List(_bytes.length);
|
||||||
|
for (var i = 0; i < _mask.length; i++) {
|
||||||
|
_mask[i] = 0xff;
|
||||||
|
}
|
||||||
|
if (lengthIsOdd) {
|
||||||
|
_mask[_mask.length - 1] = 0xf0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool matches(List<int> keyBytes) {
|
||||||
|
if (keyBytes.length < _bytes.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (var i = 0; i < _bytes.length; i++) {
|
||||||
|
if (keyBytes[i] & _mask[i] != _bytes[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class KeyPairSearcher {
|
||||||
|
Future<MCKeyPair?> findMatchingKeyPair(
|
||||||
|
String prefixStr,
|
||||||
|
int maxMilliseconds,
|
||||||
|
) async {
|
||||||
|
final int now = DateTime.now().millisecondsSinceEpoch;
|
||||||
|
final int endTime = now + maxMilliseconds;
|
||||||
|
final _KeyHashPrefix keyHashPrefix = _KeyHashPrefix(prefixStr);
|
||||||
|
while (DateTime.now().millisecondsSinceEpoch < endTime) {
|
||||||
|
final MCKeyPair keys = await generateKeyPair();
|
||||||
|
if (keyHashPrefix.matches(keys.public)) {
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,20 +6,15 @@ Route<T> buildQuickSwitchRoute<T>(Widget page) {
|
|||||||
transitionDuration: const Duration(milliseconds: 220),
|
transitionDuration: const Duration(milliseconds: 220),
|
||||||
reverseTransitionDuration: const Duration(milliseconds: 200),
|
reverseTransitionDuration: const Duration(milliseconds: 200),
|
||||||
transitionsBuilder: (context, animation, secondaryAnimation, child) {
|
transitionsBuilder: (context, animation, secondaryAnimation, child) {
|
||||||
final curved = CurvedAnimation(
|
// Pure crossfade — no positional slide, so the shared top chrome
|
||||||
parent: animation,
|
// (app bar + Contacts/Channels toggle) stays rock-steady while switching.
|
||||||
curve: Curves.easeOutCubic,
|
|
||||||
reverseCurve: Curves.easeInCubic,
|
|
||||||
);
|
|
||||||
return FadeTransition(
|
return FadeTransition(
|
||||||
opacity: curved,
|
opacity: CurvedAnimation(
|
||||||
child: SlideTransition(
|
parent: animation,
|
||||||
position: Tween<Offset>(
|
curve: Curves.easeOut,
|
||||||
begin: const Offset(0.02, 0),
|
reverseCurve: Curves.easeIn,
|
||||||
end: Offset.zero,
|
|
||||||
).animate(curved),
|
|
||||||
child: child,
|
|
||||||
),
|
),
|
||||||
|
child: child,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ class FeatureToggleRow extends StatefulWidget {
|
|||||||
final String title;
|
final String title;
|
||||||
final String subtitle;
|
final String subtitle;
|
||||||
final bool value;
|
final bool value;
|
||||||
|
final bool enabled;
|
||||||
final bool hasRefreshing;
|
final bool hasRefreshing;
|
||||||
final bool isRefreshing;
|
final bool isRefreshing;
|
||||||
final ValueChanged<bool>? onChanged;
|
final ValueChanged<bool>? onChanged;
|
||||||
@@ -15,6 +16,7 @@ class FeatureToggleRow extends StatefulWidget {
|
|||||||
required this.title,
|
required this.title,
|
||||||
required this.subtitle,
|
required this.subtitle,
|
||||||
required this.value,
|
required this.value,
|
||||||
|
this.enabled = true,
|
||||||
this.hasRefreshing = false,
|
this.hasRefreshing = false,
|
||||||
this.isRefreshing = false,
|
this.isRefreshing = false,
|
||||||
this.onChanged,
|
this.onChanged,
|
||||||
@@ -31,6 +33,13 @@ class _FeatureToggleRow extends State<FeatureToggleRow> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final scheme = Theme.of(context).colorScheme;
|
final scheme = Theme.of(context).colorScheme;
|
||||||
final textTheme = Theme.of(context).textTheme;
|
final textTheme = Theme.of(context).textTheme;
|
||||||
|
final isEnabled = widget.enabled;
|
||||||
|
final titleColor = isEnabled
|
||||||
|
? scheme.onSurface
|
||||||
|
: scheme.onSurfaceVariant.withValues(alpha: 0.7);
|
||||||
|
final subtitleColor = isEnabled
|
||||||
|
? scheme.onSurfaceVariant
|
||||||
|
: scheme.onSurfaceVariant.withValues(alpha: 0.55);
|
||||||
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
|
||||||
@@ -44,14 +53,13 @@ class _FeatureToggleRow extends State<FeatureToggleRow> {
|
|||||||
widget.title,
|
widget.title,
|
||||||
style: textTheme.bodyMedium?.copyWith(
|
style: textTheme.bodyMedium?.copyWith(
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
|
color: titleColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 2),
|
const SizedBox(height: 2),
|
||||||
Text(
|
Text(
|
||||||
widget.subtitle,
|
widget.subtitle,
|
||||||
style: textTheme.bodySmall?.copyWith(
|
style: textTheme.bodySmall?.copyWith(color: subtitleColor),
|
||||||
color: scheme.onSurfaceVariant,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -60,7 +68,18 @@ class _FeatureToggleRow extends State<FeatureToggleRow> {
|
|||||||
Row(
|
Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Switch(value: widget.value, onChanged: widget.onChanged),
|
Switch(
|
||||||
|
value: widget.value,
|
||||||
|
onChanged: isEnabled ? widget.onChanged : null,
|
||||||
|
thumbColor: !isEnabled
|
||||||
|
? WidgetStatePropertyAll<Color?>(scheme.onSurfaceVariant)
|
||||||
|
: null,
|
||||||
|
trackColor: !isEnabled
|
||||||
|
? WidgetStatePropertyAll<Color?>(
|
||||||
|
scheme.onSurfaceVariant.withValues(alpha: 0.35),
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
if (widget.hasRefreshing) ...[
|
if (widget.hasRefreshing) ...[
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
widget.isRefreshing
|
widget.isRefreshing
|
||||||
@@ -74,7 +93,7 @@ class _FeatureToggleRow extends State<FeatureToggleRow> {
|
|||||||
)
|
)
|
||||||
: IconButton(
|
: IconButton(
|
||||||
icon: const Icon(Icons.refresh, size: 18),
|
icon: const Icon(Icons.refresh, size: 18),
|
||||||
onPressed: widget.onRefresh,
|
onPressed: isEnabled ? widget.onRefresh : null,
|
||||||
tooltip: widget.refreshTooltip,
|
tooltip: widget.refreshTooltip,
|
||||||
visualDensity: VisualDensity.compact,
|
visualDensity: VisualDensity.compact,
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
|
|||||||
@@ -26,9 +26,16 @@ class QuickSwitchBar extends StatelessWidget {
|
|||||||
final colorScheme = theme.colorScheme;
|
final colorScheme = theme.colorScheme;
|
||||||
final labelStyle = theme.textTheme.labelMedium ?? const TextStyle();
|
final labelStyle = theme.textTheme.labelMedium ?? const TextStyle();
|
||||||
final background = highContrast ? MapPalette.panelDark : Colors.transparent;
|
final background = highContrast ? MapPalette.panelDark : Colors.transparent;
|
||||||
final selectedColor = highContrast
|
// The selected icon sits on the primary indicator pill, so it uses
|
||||||
|
// onPrimary. The label sits below on the bar background, so it must use a
|
||||||
|
// foreground color that contrasts with the surface (not onPrimary, which
|
||||||
|
// is white-on-white in the light theme).
|
||||||
|
final selectedIconColor = highContrast
|
||||||
? MapPalette.textPrimary
|
? MapPalette.textPrimary
|
||||||
: colorScheme.onPrimary;
|
: colorScheme.onPrimary;
|
||||||
|
final selectedLabelColor = highContrast
|
||||||
|
? MapPalette.textPrimary
|
||||||
|
: colorScheme.onSurface;
|
||||||
final unselectedColor = highContrast
|
final unselectedColor = highContrast
|
||||||
? MapPalette.textSecondary
|
? MapPalette.textSecondary
|
||||||
: colorScheme.onSurfaceVariant;
|
: colorScheme.onSurfaceVariant;
|
||||||
@@ -59,13 +66,13 @@ class QuickSwitchBar extends StatelessWidget {
|
|||||||
final isSelected = states.contains(WidgetState.selected);
|
final isSelected = states.contains(WidgetState.selected);
|
||||||
return labelStyle.copyWith(
|
return labelStyle.copyWith(
|
||||||
fontWeight: isSelected ? FontWeight.w700 : FontWeight.w500,
|
fontWeight: isSelected ? FontWeight.w700 : FontWeight.w500,
|
||||||
color: isSelected ? selectedColor : unselectedColor,
|
color: isSelected ? selectedLabelColor : unselectedColor,
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
iconTheme: WidgetStateProperty.resolveWith((states) {
|
iconTheme: WidgetStateProperty.resolveWith((states) {
|
||||||
final isSelected = states.contains(WidgetState.selected);
|
final isSelected = states.contains(WidgetState.selected);
|
||||||
return IconThemeData(
|
return IconThemeData(
|
||||||
color: isSelected ? selectedColor : unselectedColor,
|
color: isSelected ? selectedIconColor : unselectedColor,
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import '../models/app_settings.dart';
|
|||||||
import '../models/contact.dart';
|
import '../models/contact.dart';
|
||||||
import '../services/app_settings_service.dart';
|
import '../services/app_settings_service.dart';
|
||||||
import '../services/map_tile_cache_service.dart';
|
import '../services/map_tile_cache_service.dart';
|
||||||
import 'themed_map_tile_layer.dart';
|
|
||||||
|
|
||||||
class TelemetryLocationMap extends StatefulWidget {
|
class TelemetryLocationMap extends StatefulWidget {
|
||||||
final double latitude;
|
final double latitude;
|
||||||
@@ -115,7 +114,7 @@ class _TelemetryLocationMapState extends State<TelemetryLocationMap> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
children: [
|
children: [
|
||||||
ThemedMapTileLayer(tileCache: tileCache),
|
tileCache.buildTileLayer(context),
|
||||||
MarkerLayer(
|
MarkerLayer(
|
||||||
markers: [
|
markers: [
|
||||||
...contacts.map(_buildContactMarker),
|
...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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -11,18 +11,19 @@ class UnreadBadge extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final display = count > 9999 ? '9999+' : count.toString();
|
final display = count > 9999 ? '9999+' : count.toString();
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 2),
|
constraints: const BoxConstraints(minWidth: 20),
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||||
|
alignment: Alignment.center,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: MeshPalette.blue.withValues(alpha: 0.18),
|
color: MeshPalette.alert,
|
||||||
borderRadius: BorderRadius.circular(MeshRadii.pill),
|
borderRadius: BorderRadius.circular(MeshRadii.pill),
|
||||||
border: Border.all(color: MeshPalette.blue.withValues(alpha: 0.45)),
|
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
display,
|
display,
|
||||||
style: MeshTheme.mono(
|
style: MeshTheme.mono(
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
fontWeight: FontWeight.w700,
|
fontWeight: FontWeight.w700,
|
||||||
color: MeshPalette.blue,
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
import FlutterMacOS
|
import FlutterMacOS
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
import cryptography_flutter
|
||||||
import flutter_blue_plus_darwin
|
import flutter_blue_plus_darwin
|
||||||
import flutter_local_notifications
|
import flutter_local_notifications
|
||||||
import mobile_scanner
|
import mobile_scanner
|
||||||
@@ -15,6 +16,7 @@ import sqflite_darwin
|
|||||||
import url_launcher_macos
|
import url_launcher_macos
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
|
CryptographyFlutterPlugin.register(with: registry.registrar(forPlugin: "CryptographyFlutterPlugin"))
|
||||||
FlutterBluePlusPlugin.register(with: registry.registrar(forPlugin: "FlutterBluePlusPlugin"))
|
FlutterBluePlusPlugin.register(with: registry.registrar(forPlugin: "FlutterBluePlusPlugin"))
|
||||||
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
|
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
|
||||||
MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin"))
|
MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin"))
|
||||||
|
|||||||
@@ -73,6 +73,9 @@ dependencies:
|
|||||||
ml_dataframe: ^1.0.0
|
ml_dataframe: ^1.0.0
|
||||||
llamadart: ^0.8.0
|
llamadart: ^0.8.0
|
||||||
flutter_langdetect: ^0.0.1
|
flutter_langdetect: ^0.0.1
|
||||||
|
cryptography: ^2.9.0
|
||||||
|
cryptography_flutter: ^2.3.4
|
||||||
|
convert: ^3.1.2
|
||||||
|
|
||||||
hooks:
|
hooks:
|
||||||
user_defines:
|
user_defines:
|
||||||
@@ -111,12 +114,25 @@ flutter:
|
|||||||
assets:
|
assets:
|
||||||
- assets/images/
|
- assets/images/
|
||||||
- assets/icons/
|
- assets/icons/
|
||||||
|
- assets/fonts/
|
||||||
|
|
||||||
|
fonts:
|
||||||
|
- family: Inter
|
||||||
|
fonts:
|
||||||
|
- asset: assets/fonts/Inter.ttf
|
||||||
|
- asset: assets/fonts/InterItalic.ttf
|
||||||
|
style: italic
|
||||||
|
- family: JetBrains Mono
|
||||||
|
fonts:
|
||||||
|
- asset: assets/fonts/JetBrainsMono.ttf
|
||||||
|
|
||||||
flutter_launcher_icons:
|
flutter_launcher_icons:
|
||||||
android: true
|
android: true
|
||||||
ios: true
|
ios: true
|
||||||
image_path: "mesh-icon.png"
|
image_path: "mesh-icon.png"
|
||||||
remove_alpha_ios: true
|
remove_alpha_ios: true
|
||||||
|
adaptive_icon_background: "#9F9D95"
|
||||||
|
adaptive_icon_foreground: "assets/icons/ic_foreground.png"
|
||||||
|
|
||||||
# To add assets to your application, add an assets section, like this:
|
# To add assets to your application, add an assets section, like this:
|
||||||
# assets:
|
# assets:
|
||||||
|
|||||||
@@ -2,17 +2,18 @@ import 'dart:convert';
|
|||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
|
||||||
import 'package:meshcore_open/connector/meshcore_protocol.dart';
|
import 'package:meshcore_open/connector/meshcore_protocol.dart';
|
||||||
import 'package:meshcore_open/models/channel_message.dart';
|
import 'package:meshcore_open/models/channel_message.dart';
|
||||||
import 'package:meshcore_open/models/contact.dart';
|
import 'package:meshcore_open/models/contact.dart';
|
||||||
import 'package:meshcore_open/models/path_history.dart';
|
import 'package:meshcore_open/models/path_history.dart';
|
||||||
import 'package:meshcore_open/models/app_settings.dart';
|
import 'package:meshcore_open/models/app_settings.dart';
|
||||||
import 'package:meshcore_open/storage/prefs_manager.dart';
|
|
||||||
import 'package:meshcore_open/storage/contact_store.dart';
|
import 'package:meshcore_open/storage/contact_store.dart';
|
||||||
import 'package:meshcore_open/storage/message_store.dart';
|
|
||||||
import 'package:meshcore_open/storage/channel_message_store.dart';
|
import 'package:meshcore_open/storage/channel_message_store.dart';
|
||||||
import 'package:meshcore_open/storage/contact_discovery_store.dart';
|
import 'package:meshcore_open/storage/contact_discovery_store.dart';
|
||||||
|
import 'package:meshcore_open/services/app_settings_service.dart';
|
||||||
|
import 'package:meshcore_open/storage/prefs_manager.dart';
|
||||||
|
import 'package:meshcore_open/storage/message_store.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
// Builds a valid contact frame with the given pathLen and optional overrides.
|
// Builds a valid contact frame with the given pathLen and optional overrides.
|
||||||
// Frame layout: [respCode(1)][pubKey(32)][type(1)][flags(1)][pathLen(1)][path(64)][name(32)][timestamp(4)][lat(4)][lon(4)]
|
// Frame layout: [respCode(1)][pubKey(32)][type(1)][flags(1)][pathLen(1)][path(64)][name(32)][timestamp(4)][lat(4)][lon(4)]
|
||||||
@@ -51,6 +52,7 @@ Uint8List _buildChannelMessageFrameV3({
|
|||||||
required bool hasPath,
|
required bool hasPath,
|
||||||
int channelIndex = 7,
|
int channelIndex = 7,
|
||||||
String senderName = 'Alice',
|
String senderName = 'Alice',
|
||||||
|
|
||||||
String text = 'Hello world',
|
String text = 'Hello world',
|
||||||
int txtType = txtTypePlain,
|
int txtType = txtTypePlain,
|
||||||
}) {
|
}) {
|
||||||
@@ -77,6 +79,14 @@ Uint8List _buildChannelMessageFrameV3({
|
|||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
TestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
|
setUp(() async {
|
||||||
|
SharedPreferences.setMockInitialValues({});
|
||||||
|
PrefsManager.reset();
|
||||||
|
await PrefsManager.initialize();
|
||||||
|
});
|
||||||
|
|
||||||
group('ChannelMessage.fromFrame — V3 packed path decoding', () {
|
group('ChannelMessage.fromFrame — V3 packed path decoding', () {
|
||||||
test(
|
test(
|
||||||
'hasPath reads width, hop count, path bytes, and txtType correctly',
|
'hasPath reads width, hop count, path bytes, and txtType correctly',
|
||||||
@@ -467,11 +477,6 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
group('Storage migration — multi-byte paths compatibility', () {
|
group('Storage migration — multi-byte paths compatibility', () {
|
||||||
setUp(() async {
|
|
||||||
SharedPreferences.setMockInitialValues({});
|
|
||||||
await PrefsManager.initialize();
|
|
||||||
});
|
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'ContactStore decodes and migrates legacy mode-encoded paths',
|
'ContactStore decodes and migrates legacy mode-encoded paths',
|
||||||
() async {
|
() async {
|
||||||
@@ -638,5 +643,46 @@ void main() {
|
|||||||
expect(contacts.first.path, equals(Uint8List.fromList([0x11, 0x22])));
|
expect(contacts.first.path, equals(Uint8List.fromList([0x11, 0x22])));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
group('AppSettingsService — gps interval fallback', () {
|
||||||
|
test('resolvedGpsIntervalSeconds prefers device custom var', () {
|
||||||
|
final service = AppSettingsService();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
service.resolvedGpsIntervalSeconds(const {'gps_interval': '120'}),
|
||||||
|
equals(120),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('resolvedGpsIntervalSeconds falls back to stored value', () async {
|
||||||
|
final service = AppSettingsService();
|
||||||
|
await service.updateSettings(AppSettings(gpsIntervalSeconds: 900));
|
||||||
|
|
||||||
|
expect(service.resolvedGpsIntervalSeconds(null), equals(900));
|
||||||
|
expect(
|
||||||
|
service.resolvedGpsIntervalSeconds(const {'gps_interval': 'bad'}),
|
||||||
|
equals(900),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('resolvedGpsIntervalSeconds keeps an explicit device zero', () async {
|
||||||
|
final service = AppSettingsService();
|
||||||
|
await service.updateSettings(AppSettings(gpsIntervalSeconds: 900));
|
||||||
|
|
||||||
|
expect(
|
||||||
|
service.resolvedGpsIntervalSeconds(const {'gps_interval': '0'}),
|
||||||
|
equals(0),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('toJson/fromJson preserves gpsIntervalSeconds', () {
|
||||||
|
final settings = AppSettings(gpsIntervalSeconds: 321);
|
||||||
|
|
||||||
|
final restored = AppSettings.fromJson(settings.toJson());
|
||||||
|
|
||||||
|
expect(restored.gpsIntervalSeconds, equals(321));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,18 @@
|
|||||||
"channels_regionSetTo",
|
"channels_regionSetTo",
|
||||||
"channels_regionNotSet",
|
"channels_regionNotSet",
|
||||||
"channels_regionSelect_Title",
|
"channels_regionSelect_Title",
|
||||||
"channels_clearRegion"
|
"channels_clearRegion",
|
||||||
|
"chat_receivedGif",
|
||||||
|
"repeater_keySettings",
|
||||||
|
"repeater_keySettingsSubtitle",
|
||||||
|
"repeater_prvKey",
|
||||||
|
"repeater_prvKeyHelper",
|
||||||
|
"repeater_generatePrvKey",
|
||||||
|
"repeater_stopGeneratingPrvKey",
|
||||||
|
"repeater_pubKey",
|
||||||
|
"repeater_pubKeyHelper",
|
||||||
|
"repeater_pubKeyPrefix",
|
||||||
|
"repeater_pubKeyPrefixHelper"
|
||||||
],
|
],
|
||||||
|
|
||||||
"de": [
|
"de": [
|
||||||
@@ -29,7 +40,18 @@
|
|||||||
"repeater_pathHashModeOption0",
|
"repeater_pathHashModeOption0",
|
||||||
"repeater_pathHashModeOption1",
|
"repeater_pathHashModeOption1",
|
||||||
"repeater_pathHashModeOption2",
|
"repeater_pathHashModeOption2",
|
||||||
"repeater_pathHashModeOption3"
|
"repeater_pathHashModeOption3",
|
||||||
|
"chat_receivedGif",
|
||||||
|
"repeater_keySettings",
|
||||||
|
"repeater_keySettingsSubtitle",
|
||||||
|
"repeater_prvKey",
|
||||||
|
"repeater_prvKeyHelper",
|
||||||
|
"repeater_generatePrvKey",
|
||||||
|
"repeater_stopGeneratingPrvKey",
|
||||||
|
"repeater_pubKey",
|
||||||
|
"repeater_pubKeyHelper",
|
||||||
|
"repeater_pubKeyPrefix",
|
||||||
|
"repeater_pubKeyPrefixHelper"
|
||||||
],
|
],
|
||||||
|
|
||||||
"es": [
|
"es": [
|
||||||
@@ -52,7 +74,18 @@
|
|||||||
"channels_regionSetTo",
|
"channels_regionSetTo",
|
||||||
"channels_regionNotSet",
|
"channels_regionNotSet",
|
||||||
"channels_regionSelect_Title",
|
"channels_regionSelect_Title",
|
||||||
"channels_clearRegion"
|
"channels_clearRegion",
|
||||||
|
"chat_receivedGif",
|
||||||
|
"repeater_keySettings",
|
||||||
|
"repeater_keySettingsSubtitle",
|
||||||
|
"repeater_prvKey",
|
||||||
|
"repeater_prvKeyHelper",
|
||||||
|
"repeater_generatePrvKey",
|
||||||
|
"repeater_stopGeneratingPrvKey",
|
||||||
|
"repeater_pubKey",
|
||||||
|
"repeater_pubKeyHelper",
|
||||||
|
"repeater_pubKeyPrefix",
|
||||||
|
"repeater_pubKeyPrefixHelper"
|
||||||
],
|
],
|
||||||
|
|
||||||
"fr": [
|
"fr": [
|
||||||
@@ -75,7 +108,18 @@
|
|||||||
"channels_regionSetTo",
|
"channels_regionSetTo",
|
||||||
"channels_regionNotSet",
|
"channels_regionNotSet",
|
||||||
"channels_regionSelect_Title",
|
"channels_regionSelect_Title",
|
||||||
"channels_clearRegion"
|
"channels_clearRegion",
|
||||||
|
"chat_receivedGif",
|
||||||
|
"repeater_keySettings",
|
||||||
|
"repeater_keySettingsSubtitle",
|
||||||
|
"repeater_prvKey",
|
||||||
|
"repeater_prvKeyHelper",
|
||||||
|
"repeater_generatePrvKey",
|
||||||
|
"repeater_stopGeneratingPrvKey",
|
||||||
|
"repeater_pubKey",
|
||||||
|
"repeater_pubKeyHelper",
|
||||||
|
"repeater_pubKeyPrefix",
|
||||||
|
"repeater_pubKeyPrefixHelper"
|
||||||
],
|
],
|
||||||
|
|
||||||
"hu": [
|
"hu": [
|
||||||
@@ -98,7 +142,18 @@
|
|||||||
"channels_regionSetTo",
|
"channels_regionSetTo",
|
||||||
"channels_regionNotSet",
|
"channels_regionNotSet",
|
||||||
"channels_regionSelect_Title",
|
"channels_regionSelect_Title",
|
||||||
"channels_clearRegion"
|
"channels_clearRegion",
|
||||||
|
"chat_receivedGif",
|
||||||
|
"repeater_keySettings",
|
||||||
|
"repeater_keySettingsSubtitle",
|
||||||
|
"repeater_prvKey",
|
||||||
|
"repeater_prvKeyHelper",
|
||||||
|
"repeater_generatePrvKey",
|
||||||
|
"repeater_stopGeneratingPrvKey",
|
||||||
|
"repeater_pubKey",
|
||||||
|
"repeater_pubKeyHelper",
|
||||||
|
"repeater_pubKeyPrefix",
|
||||||
|
"repeater_pubKeyPrefixHelper"
|
||||||
],
|
],
|
||||||
|
|
||||||
"it": [
|
"it": [
|
||||||
@@ -121,7 +176,18 @@
|
|||||||
"channels_regionSetTo",
|
"channels_regionSetTo",
|
||||||
"channels_regionNotSet",
|
"channels_regionNotSet",
|
||||||
"channels_regionSelect_Title",
|
"channels_regionSelect_Title",
|
||||||
"channels_clearRegion"
|
"channels_clearRegion",
|
||||||
|
"chat_receivedGif",
|
||||||
|
"repeater_keySettings",
|
||||||
|
"repeater_keySettingsSubtitle",
|
||||||
|
"repeater_prvKey",
|
||||||
|
"repeater_prvKeyHelper",
|
||||||
|
"repeater_generatePrvKey",
|
||||||
|
"repeater_stopGeneratingPrvKey",
|
||||||
|
"repeater_pubKey",
|
||||||
|
"repeater_pubKeyHelper",
|
||||||
|
"repeater_pubKeyPrefix",
|
||||||
|
"repeater_pubKeyPrefixHelper"
|
||||||
],
|
],
|
||||||
|
|
||||||
"ja": [
|
"ja": [
|
||||||
@@ -144,7 +210,18 @@
|
|||||||
"channels_regionSetTo",
|
"channels_regionSetTo",
|
||||||
"channels_regionNotSet",
|
"channels_regionNotSet",
|
||||||
"channels_regionSelect_Title",
|
"channels_regionSelect_Title",
|
||||||
"channels_clearRegion"
|
"channels_clearRegion",
|
||||||
|
"chat_receivedGif",
|
||||||
|
"repeater_keySettings",
|
||||||
|
"repeater_keySettingsSubtitle",
|
||||||
|
"repeater_prvKey",
|
||||||
|
"repeater_prvKeyHelper",
|
||||||
|
"repeater_generatePrvKey",
|
||||||
|
"repeater_stopGeneratingPrvKey",
|
||||||
|
"repeater_pubKey",
|
||||||
|
"repeater_pubKeyHelper",
|
||||||
|
"repeater_pubKeyPrefix",
|
||||||
|
"repeater_pubKeyPrefixHelper"
|
||||||
],
|
],
|
||||||
|
|
||||||
"ko": [
|
"ko": [
|
||||||
@@ -167,7 +244,18 @@
|
|||||||
"channels_regionSetTo",
|
"channels_regionSetTo",
|
||||||
"channels_regionNotSet",
|
"channels_regionNotSet",
|
||||||
"channels_regionSelect_Title",
|
"channels_regionSelect_Title",
|
||||||
"channels_clearRegion"
|
"channels_clearRegion",
|
||||||
|
"chat_receivedGif",
|
||||||
|
"repeater_keySettings",
|
||||||
|
"repeater_keySettingsSubtitle",
|
||||||
|
"repeater_prvKey",
|
||||||
|
"repeater_prvKeyHelper",
|
||||||
|
"repeater_generatePrvKey",
|
||||||
|
"repeater_stopGeneratingPrvKey",
|
||||||
|
"repeater_pubKey",
|
||||||
|
"repeater_pubKeyHelper",
|
||||||
|
"repeater_pubKeyPrefix",
|
||||||
|
"repeater_pubKeyPrefixHelper"
|
||||||
],
|
],
|
||||||
|
|
||||||
"nl": [
|
"nl": [
|
||||||
@@ -190,7 +278,18 @@
|
|||||||
"channels_regionSetTo",
|
"channels_regionSetTo",
|
||||||
"channels_regionNotSet",
|
"channels_regionNotSet",
|
||||||
"channels_regionSelect_Title",
|
"channels_regionSelect_Title",
|
||||||
"channels_clearRegion"
|
"channels_clearRegion",
|
||||||
|
"chat_receivedGif",
|
||||||
|
"repeater_keySettings",
|
||||||
|
"repeater_keySettingsSubtitle",
|
||||||
|
"repeater_prvKey",
|
||||||
|
"repeater_prvKeyHelper",
|
||||||
|
"repeater_generatePrvKey",
|
||||||
|
"repeater_stopGeneratingPrvKey",
|
||||||
|
"repeater_pubKey",
|
||||||
|
"repeater_pubKeyHelper",
|
||||||
|
"repeater_pubKeyPrefix",
|
||||||
|
"repeater_pubKeyPrefixHelper"
|
||||||
],
|
],
|
||||||
|
|
||||||
"pl": [
|
"pl": [
|
||||||
@@ -213,7 +312,18 @@
|
|||||||
"channels_regionSetTo",
|
"channels_regionSetTo",
|
||||||
"channels_regionNotSet",
|
"channels_regionNotSet",
|
||||||
"channels_regionSelect_Title",
|
"channels_regionSelect_Title",
|
||||||
"channels_clearRegion"
|
"channels_clearRegion",
|
||||||
|
"chat_receivedGif",
|
||||||
|
"repeater_keySettings",
|
||||||
|
"repeater_keySettingsSubtitle",
|
||||||
|
"repeater_prvKey",
|
||||||
|
"repeater_prvKeyHelper",
|
||||||
|
"repeater_generatePrvKey",
|
||||||
|
"repeater_stopGeneratingPrvKey",
|
||||||
|
"repeater_pubKey",
|
||||||
|
"repeater_pubKeyHelper",
|
||||||
|
"repeater_pubKeyPrefix",
|
||||||
|
"repeater_pubKeyPrefixHelper"
|
||||||
],
|
],
|
||||||
|
|
||||||
"pt": [
|
"pt": [
|
||||||
@@ -236,7 +346,18 @@
|
|||||||
"channels_regionSetTo",
|
"channels_regionSetTo",
|
||||||
"channels_regionNotSet",
|
"channels_regionNotSet",
|
||||||
"channels_regionSelect_Title",
|
"channels_regionSelect_Title",
|
||||||
"channels_clearRegion"
|
"channels_clearRegion",
|
||||||
|
"chat_receivedGif",
|
||||||
|
"repeater_keySettings",
|
||||||
|
"repeater_keySettingsSubtitle",
|
||||||
|
"repeater_prvKey",
|
||||||
|
"repeater_prvKeyHelper",
|
||||||
|
"repeater_generatePrvKey",
|
||||||
|
"repeater_stopGeneratingPrvKey",
|
||||||
|
"repeater_pubKey",
|
||||||
|
"repeater_pubKeyHelper",
|
||||||
|
"repeater_pubKeyPrefix",
|
||||||
|
"repeater_pubKeyPrefixHelper"
|
||||||
],
|
],
|
||||||
|
|
||||||
"ru": [
|
"ru": [
|
||||||
@@ -259,7 +380,18 @@
|
|||||||
"channels_regionSetTo",
|
"channels_regionSetTo",
|
||||||
"channels_regionNotSet",
|
"channels_regionNotSet",
|
||||||
"channels_regionSelect_Title",
|
"channels_regionSelect_Title",
|
||||||
"channels_clearRegion"
|
"channels_clearRegion",
|
||||||
|
"chat_receivedGif",
|
||||||
|
"repeater_keySettings",
|
||||||
|
"repeater_keySettingsSubtitle",
|
||||||
|
"repeater_prvKey",
|
||||||
|
"repeater_prvKeyHelper",
|
||||||
|
"repeater_generatePrvKey",
|
||||||
|
"repeater_stopGeneratingPrvKey",
|
||||||
|
"repeater_pubKey",
|
||||||
|
"repeater_pubKeyHelper",
|
||||||
|
"repeater_pubKeyPrefix",
|
||||||
|
"repeater_pubKeyPrefixHelper"
|
||||||
],
|
],
|
||||||
|
|
||||||
"sk": [
|
"sk": [
|
||||||
@@ -282,7 +414,18 @@
|
|||||||
"channels_regionSetTo",
|
"channels_regionSetTo",
|
||||||
"channels_regionNotSet",
|
"channels_regionNotSet",
|
||||||
"channels_regionSelect_Title",
|
"channels_regionSelect_Title",
|
||||||
"channels_clearRegion"
|
"channels_clearRegion",
|
||||||
|
"chat_receivedGif",
|
||||||
|
"repeater_keySettings",
|
||||||
|
"repeater_keySettingsSubtitle",
|
||||||
|
"repeater_prvKey",
|
||||||
|
"repeater_prvKeyHelper",
|
||||||
|
"repeater_generatePrvKey",
|
||||||
|
"repeater_stopGeneratingPrvKey",
|
||||||
|
"repeater_pubKey",
|
||||||
|
"repeater_pubKeyHelper",
|
||||||
|
"repeater_pubKeyPrefix",
|
||||||
|
"repeater_pubKeyPrefixHelper"
|
||||||
],
|
],
|
||||||
|
|
||||||
"sl": [
|
"sl": [
|
||||||
@@ -305,7 +448,18 @@
|
|||||||
"channels_regionSetTo",
|
"channels_regionSetTo",
|
||||||
"channels_regionNotSet",
|
"channels_regionNotSet",
|
||||||
"channels_regionSelect_Title",
|
"channels_regionSelect_Title",
|
||||||
"channels_clearRegion"
|
"channels_clearRegion",
|
||||||
|
"chat_receivedGif",
|
||||||
|
"repeater_keySettings",
|
||||||
|
"repeater_keySettingsSubtitle",
|
||||||
|
"repeater_prvKey",
|
||||||
|
"repeater_prvKeyHelper",
|
||||||
|
"repeater_generatePrvKey",
|
||||||
|
"repeater_stopGeneratingPrvKey",
|
||||||
|
"repeater_pubKey",
|
||||||
|
"repeater_pubKeyHelper",
|
||||||
|
"repeater_pubKeyPrefix",
|
||||||
|
"repeater_pubKeyPrefixHelper"
|
||||||
],
|
],
|
||||||
|
|
||||||
"sv": [
|
"sv": [
|
||||||
@@ -328,7 +482,18 @@
|
|||||||
"channels_regionSetTo",
|
"channels_regionSetTo",
|
||||||
"channels_regionNotSet",
|
"channels_regionNotSet",
|
||||||
"channels_regionSelect_Title",
|
"channels_regionSelect_Title",
|
||||||
"channels_clearRegion"
|
"channels_clearRegion",
|
||||||
|
"chat_receivedGif",
|
||||||
|
"repeater_keySettings",
|
||||||
|
"repeater_keySettingsSubtitle",
|
||||||
|
"repeater_prvKey",
|
||||||
|
"repeater_prvKeyHelper",
|
||||||
|
"repeater_generatePrvKey",
|
||||||
|
"repeater_stopGeneratingPrvKey",
|
||||||
|
"repeater_pubKey",
|
||||||
|
"repeater_pubKeyHelper",
|
||||||
|
"repeater_pubKeyPrefix",
|
||||||
|
"repeater_pubKeyPrefixHelper"
|
||||||
],
|
],
|
||||||
|
|
||||||
"uk": [
|
"uk": [
|
||||||
@@ -351,7 +516,18 @@
|
|||||||
"channels_regionSetTo",
|
"channels_regionSetTo",
|
||||||
"channels_regionNotSet",
|
"channels_regionNotSet",
|
||||||
"channels_regionSelect_Title",
|
"channels_regionSelect_Title",
|
||||||
"channels_clearRegion"
|
"channels_clearRegion",
|
||||||
|
"chat_receivedGif",
|
||||||
|
"repeater_keySettings",
|
||||||
|
"repeater_keySettingsSubtitle",
|
||||||
|
"repeater_prvKey",
|
||||||
|
"repeater_prvKeyHelper",
|
||||||
|
"repeater_generatePrvKey",
|
||||||
|
"repeater_stopGeneratingPrvKey",
|
||||||
|
"repeater_pubKey",
|
||||||
|
"repeater_pubKeyHelper",
|
||||||
|
"repeater_pubKeyPrefix",
|
||||||
|
"repeater_pubKeyPrefixHelper"
|
||||||
],
|
],
|
||||||
|
|
||||||
"zh": [
|
"zh": [
|
||||||
@@ -374,6 +550,17 @@
|
|||||||
"channels_regionSetTo",
|
"channels_regionSetTo",
|
||||||
"channels_regionNotSet",
|
"channels_regionNotSet",
|
||||||
"channels_regionSelect_Title",
|
"channels_regionSelect_Title",
|
||||||
"channels_clearRegion"
|
"channels_clearRegion",
|
||||||
|
"chat_receivedGif",
|
||||||
|
"repeater_keySettings",
|
||||||
|
"repeater_keySettingsSubtitle",
|
||||||
|
"repeater_prvKey",
|
||||||
|
"repeater_prvKeyHelper",
|
||||||
|
"repeater_generatePrvKey",
|
||||||
|
"repeater_stopGeneratingPrvKey",
|
||||||
|
"repeater_pubKey",
|
||||||
|
"repeater_pubKeyHelper",
|
||||||
|
"repeater_pubKeyPrefix",
|
||||||
|
"repeater_pubKeyPrefixHelper"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||