mirror of
https://github.com/zjs81/meshcore-open.git
synced 2026-06-15 07:04:26 +10:00
Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 49665fd563 | |||
| 1603adf5dd | |||
| cedbe1dd6c | |||
| fac062a100 | |||
| c52b19b09f | |||
| 6a666839b6 | |||
| 4995f5f380 | |||
| 4e6e7b6061 | |||
| aa350aa4ae | |||
| dfd38b19e9 | |||
| 4afab3f629 | |||
| 67816130ac | |||
| d573f0c312 | |||
| 5b699cd624 | |||
| a4d3d248a5 | |||
| 2a3f2b3a24 | |||
| 675083fa01 | |||
| 5fc4b80b16 | |||
| 84a32c1e67 | |||
| 607583060a | |||
| 71cf556b61 | |||
| c26174ad18 | |||
| 04021a39a1 | |||
| fe23e9f7a0 | |||
| d7ec1876af |
@@ -65,6 +65,7 @@ secrets.dart
|
|||||||
**/ios/Flutter/Flutter.podspec
|
**/ios/Flutter/Flutter.podspec
|
||||||
|
|
||||||
# Android
|
# Android
|
||||||
|
.gradle/
|
||||||
**/android/.gradle/
|
**/android/.gradle/
|
||||||
**/android/captures/
|
**/android/captures/
|
||||||
**/android/local.properties
|
**/android/local.properties
|
||||||
|
|||||||
Generated
+61
@@ -0,0 +1,61 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"flake-utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1731533236,
|
||||||
|
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1770562336,
|
||||||
|
"narHash": "sha256-ub1gpAONMFsT/GU2hV6ZWJjur8rJ6kKxdm9IlCT0j84=",
|
||||||
|
"owner": "nixos",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "d6c71932130818840fc8fe9509cf50be8c64634f",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nixos",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
{
|
||||||
|
description = "MeshCore Flutter Application";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||||
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, flake-utils }:
|
||||||
|
flake-utils.lib.eachDefaultSystem (system:
|
||||||
|
let
|
||||||
|
pkgs = nixpkgs.legacyPackages.${system};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
devShells.default = pkgs.mkShell {
|
||||||
|
buildInputs = with pkgs; [
|
||||||
|
# Flutter and Dart
|
||||||
|
flutter
|
||||||
|
dart
|
||||||
|
|
||||||
|
# Java (required for Android development)
|
||||||
|
jdk17
|
||||||
|
|
||||||
|
# Android development tools
|
||||||
|
android-tools
|
||||||
|
gradle
|
||||||
|
|
||||||
|
# For the shell hook to set up the environment for Flutter development
|
||||||
|
gtk3
|
||||||
|
glib
|
||||||
|
sysprof
|
||||||
|
libclang
|
||||||
|
cmake
|
||||||
|
ninja
|
||||||
|
pkg-config
|
||||||
|
libdatrie
|
||||||
|
|
||||||
|
# Additional tools for installing Android SDK if not present
|
||||||
|
curl
|
||||||
|
unzip
|
||||||
|
];
|
||||||
|
|
||||||
|
shellHook = ''
|
||||||
|
echo "MeshCore Flutter Development Environment"
|
||||||
|
export PKG_CONFIG_PATH="${pkgs.gtk3}/lib/pkgconfig:${pkgs.glib}/lib/pkgconfig:${pkgs.sysprof}/lib/pkgconfig:$PKG_CONFIG_PATH"
|
||||||
|
export LD_LIBRARY_PATH="${pkgs.lib.makeLibraryPath [pkgs.gtk3 pkgs.glib pkgs.sysprof pkgs.libdatrie]}:$LD_LIBRARY_PATH"
|
||||||
|
export CMAKE_INSTALL_PREFIX="$PWD/build/bundle"
|
||||||
|
|
||||||
|
# Setup Android SDK in home directory (standard location)
|
||||||
|
export ANDROID_HOME="$HOME/Android/Sdk"
|
||||||
|
export ANDROID_SDK_ROOT="$ANDROID_HOME"
|
||||||
|
export PATH="$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$ANDROID_HOME/tools/bin:$PATH"
|
||||||
|
|
||||||
|
echo "Android SDK: $ANDROID_HOME"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check if Android SDK exists and offer to download if not
|
||||||
|
if [ ! -d "$ANDROID_HOME" ]; then
|
||||||
|
echo "WARNING: Android SDK not found at $ANDROID_HOME"
|
||||||
|
echo ""
|
||||||
|
echo "To download and set up the Android SDK, run this command:"
|
||||||
|
echo ""
|
||||||
|
cat << 'EOF'
|
||||||
|
mkdir -p ~/Android/Sdk && cd ~/Android/Sdk && \
|
||||||
|
curl -o cmdline-tools.zip ${if pkgs.stdenv.isDarwin then "https://dl.google.com/android/repository/commandlinetools-mac-10406996_latest.zip" else "https://dl.google.com/android/repository/commandlinetools-linux-10406996_latest.zip"} && \
|
||||||
|
unzip -q cmdline-tools.zip && \
|
||||||
|
mkdir -p cmdline-tools/latest && \
|
||||||
|
mv cmdline-tools/* cmdline-tools/latest/ 2>/dev/null || echo "Warning: failed to move Android cmdline-tools into 'latest' directory; please check your SDK layout." >&2 && \
|
||||||
|
rm cmdline-tools.zip && \
|
||||||
|
cd cmdline-tools/latest/bin && \
|
||||||
|
yes | ./sdkmanager --sdk_root=~/Android/Sdk 'platform-tools' && \
|
||||||
|
echo "Android SDK setup complete!"
|
||||||
|
EOF
|
||||||
|
echo ""
|
||||||
|
echo "Then run 'flutter doctor' again to verify."
|
||||||
|
echo ""
|
||||||
|
else
|
||||||
|
echo "Android SDK found at $ANDROID_HOME"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "To check that everything is set up correctly, run 'flutter doctor' and ensure there are no issues."
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -2,10 +2,11 @@ import 'dart:async';
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:crypto/crypto.dart' as crypto;
|
import 'package:crypto/crypto.dart' as crypto;
|
||||||
|
import 'package:geolocator/geolocator.dart';
|
||||||
|
import 'package:meshcore_open/services/sparse_location_logger.dart';
|
||||||
import 'package:pointycastle/export.dart';
|
import 'package:pointycastle/export.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
|
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
|
||||||
import 'package:wakelock_plus/wakelock_plus.dart';
|
|
||||||
|
|
||||||
import '../models/channel.dart';
|
import '../models/channel.dart';
|
||||||
import '../models/channel_message.dart';
|
import '../models/channel_message.dart';
|
||||||
@@ -131,6 +132,7 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
PathHistoryService? _pathHistoryService;
|
PathHistoryService? _pathHistoryService;
|
||||||
AppSettingsService? _appSettingsService;
|
AppSettingsService? _appSettingsService;
|
||||||
BackgroundService? _backgroundService;
|
BackgroundService? _backgroundService;
|
||||||
|
SparseLocationLogger? _sparseLocationLogger;
|
||||||
final NotificationService _notificationService = NotificationService();
|
final NotificationService _notificationService = NotificationService();
|
||||||
BleDebugLogService? _bleDebugLogService;
|
BleDebugLogService? _bleDebugLogService;
|
||||||
AppDebugLogService? _appDebugLogService;
|
AppDebugLogService? _appDebugLogService;
|
||||||
@@ -503,6 +505,7 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
BleDebugLogService? bleDebugLogService,
|
BleDebugLogService? bleDebugLogService,
|
||||||
AppDebugLogService? appDebugLogService,
|
AppDebugLogService? appDebugLogService,
|
||||||
BackgroundService? backgroundService,
|
BackgroundService? backgroundService,
|
||||||
|
SparseLocationLogger? sparseLocationLogger,
|
||||||
}) {
|
}) {
|
||||||
_retryService = retryService;
|
_retryService = retryService;
|
||||||
_pathHistoryService = pathHistoryService;
|
_pathHistoryService = pathHistoryService;
|
||||||
@@ -510,11 +513,14 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
_bleDebugLogService = bleDebugLogService;
|
_bleDebugLogService = bleDebugLogService;
|
||||||
_appDebugLogService = appDebugLogService;
|
_appDebugLogService = appDebugLogService;
|
||||||
_backgroundService = backgroundService;
|
_backgroundService = backgroundService;
|
||||||
|
_sparseLocationLogger = sparseLocationLogger;
|
||||||
|
|
||||||
// Initialize notification service
|
// Initialize notification service
|
||||||
_notificationService.initialize();
|
_notificationService.initialize();
|
||||||
_loadChannelOrder();
|
_loadChannelOrder();
|
||||||
|
|
||||||
|
_sparseLocationLogger?.initialize(_updateLocationandAdvert);
|
||||||
|
|
||||||
// Initialize retry service callbacks
|
// Initialize retry service callbacks
|
||||||
_retryService?.initialize(
|
_retryService?.initialize(
|
||||||
sendMessageCallback: _sendMessageDirect,
|
sendMessageCallback: _sendMessageDirect,
|
||||||
@@ -658,7 +664,8 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
_scanResults.clear();
|
_scanResults.clear();
|
||||||
for (var result in results) {
|
for (var result in results) {
|
||||||
if (result.device.platformName.startsWith("MeshCore-") ||
|
if (result.device.platformName.startsWith("MeshCore-") ||
|
||||||
result.advertisementData.advName.startsWith("MeshCore-")) {
|
result.advertisementData.advName.startsWith("MeshCore-") ||
|
||||||
|
result.advertisementData.advName.startsWith("Whisper-")) {
|
||||||
_scanResults.add(result);
|
_scanResults.add(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -775,9 +782,6 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
|
|
||||||
_setState(MeshCoreConnectionState.connected);
|
_setState(MeshCoreConnectionState.connected);
|
||||||
|
|
||||||
// Enable wake lock to prevent BLE disconnection when screen turns off
|
|
||||||
await WakelockPlus.enable();
|
|
||||||
|
|
||||||
await _requestDeviceInfo();
|
await _requestDeviceInfo();
|
||||||
_startBatteryPolling();
|
_startBatteryPolling();
|
||||||
final gotSelfInfo = await _waitForSelfInfo(
|
final gotSelfInfo = await _waitForSelfInfo(
|
||||||
@@ -831,6 +835,8 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SparseLocationLogger? get sparseLocationLogger => _sparseLocationLogger;
|
||||||
|
|
||||||
bool get _shouldAutoReconnect => !_manualDisconnect && _lastDeviceId != null;
|
bool get _shouldAutoReconnect => !_manualDisconnect && _lastDeviceId != null;
|
||||||
|
|
||||||
void _cancelReconnectTimer() {
|
void _cancelReconnectTimer() {
|
||||||
@@ -886,9 +892,6 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
_setState(MeshCoreConnectionState.disconnecting);
|
_setState(MeshCoreConnectionState.disconnecting);
|
||||||
_stopBatteryPolling();
|
_stopBatteryPolling();
|
||||||
|
|
||||||
// Disable wake lock when disconnecting
|
|
||||||
await WakelockPlus.disable();
|
|
||||||
|
|
||||||
await _notifySubscription?.cancel();
|
await _notifySubscription?.cancel();
|
||||||
_notifySubscription = null;
|
_notifySubscription = null;
|
||||||
|
|
||||||
@@ -1696,6 +1699,11 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
_isLoadingContacts = true;
|
_isLoadingContacts = true;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
break;
|
break;
|
||||||
|
case pushCodeNewAdvert:
|
||||||
|
debugPrint('Got New CONTACT');
|
||||||
|
// It the same format as respCodeContact, so we can reuse the handler
|
||||||
|
_handleContact(frame);
|
||||||
|
break;
|
||||||
case respCodeContact:
|
case respCodeContact:
|
||||||
debugPrint('Got CONTACT');
|
debugPrint('Got CONTACT');
|
||||||
_handleContact(frame);
|
_handleContact(frame);
|
||||||
@@ -1740,6 +1748,7 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
case pushCodeStatusResponse:
|
case pushCodeStatusResponse:
|
||||||
break;
|
break;
|
||||||
case pushCodeLogRxData:
|
case pushCodeLogRxData:
|
||||||
|
_handleRxData(frame);
|
||||||
_handleLogRxData(frame);
|
_handleLogRxData(frame);
|
||||||
break;
|
break;
|
||||||
case respCodeChannelInfo:
|
case respCodeChannelInfo:
|
||||||
@@ -1753,6 +1762,7 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
break;
|
break;
|
||||||
case respCodeCustomVars:
|
case respCodeCustomVars:
|
||||||
_handleCustomVars(frame);
|
_handleCustomVars(frame);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
debugPrint('Unknown frame code: $code');
|
debugPrint('Unknown frame code: $code');
|
||||||
}
|
}
|
||||||
@@ -2008,6 +2018,76 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _handleContactAdvert(Contact contact) {
|
||||||
|
if (contact.type == advTypeRepeater) {
|
||||||
|
_contactUnreadCount.remove(contact.publicKeyHex);
|
||||||
|
_unreadStore.saveContactUnreadCount(
|
||||||
|
Map<String, int>.from(_contactUnreadCount),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Check if this is a new contact
|
||||||
|
final isNewContact = !_knownContactKeys.contains(contact.publicKeyHex);
|
||||||
|
final existingIndex = _contacts.indexWhere(
|
||||||
|
(c) => c.publicKeyHex == contact.publicKeyHex,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (existingIndex >= 0) {
|
||||||
|
final existing = _contacts[existingIndex];
|
||||||
|
final mergedLastMessageAt =
|
||||||
|
existing.lastMessageAt.isAfter(contact.lastMessageAt)
|
||||||
|
? existing.lastMessageAt
|
||||||
|
: contact.lastMessageAt;
|
||||||
|
|
||||||
|
appLogger.info(
|
||||||
|
'Refreshing contact ${contact.name}: devicePath=${contact.pathLength}, existingOverride=${existing.pathOverride}',
|
||||||
|
tag: 'Connector',
|
||||||
|
);
|
||||||
|
|
||||||
|
// CRITICAL: Preserve user's path override when contact is refreshed from device
|
||||||
|
_contacts[existingIndex] = contact.copyWith(
|
||||||
|
lastMessageAt: mergedLastMessageAt,
|
||||||
|
pathOverride: existing.pathOverride, // Preserve user's path choice
|
||||||
|
pathOverrideBytes: existing.pathOverrideBytes,
|
||||||
|
);
|
||||||
|
|
||||||
|
appLogger.info(
|
||||||
|
'After merge: pathOverride=${_contacts[existingIndex].pathOverride}, devicePath=${_contacts[existingIndex].pathLength}',
|
||||||
|
tag: 'Connector',
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
_contacts.add(contact);
|
||||||
|
appLogger.info(
|
||||||
|
'Added new contact ${contact.name}: pathLen=${contact.pathLength}',
|
||||||
|
tag: 'Connector',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_knownContactKeys.add(contact.publicKeyHex);
|
||||||
|
_loadMessagesForContact(contact.publicKeyHex);
|
||||||
|
|
||||||
|
// Add path to history if we have a valid path
|
||||||
|
if (_pathHistoryService != null && contact.pathLength >= 0) {
|
||||||
|
_pathHistoryService!.handlePathUpdated(contact);
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyListeners();
|
||||||
|
|
||||||
|
// Show notification for new contact (advertisement)
|
||||||
|
if (isNewContact && _appSettingsService != null) {
|
||||||
|
final settings = _appSettingsService!.settings;
|
||||||
|
if (settings.notificationsEnabled && settings.notifyOnNewAdvert) {
|
||||||
|
_notificationService.showAdvertNotification(
|
||||||
|
contactName: contact.name,
|
||||||
|
contactType: contact.typeLabel,
|
||||||
|
contactId: contact.publicKeyHex,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_isLoadingContacts) {
|
||||||
|
unawaited(_persistContacts());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _persistContacts() async {
|
Future<void> _persistContacts() async {
|
||||||
await _contactStore.saveContacts(_contacts);
|
await _contactStore.saveContacts(_contacts);
|
||||||
}
|
}
|
||||||
@@ -3214,8 +3294,6 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _handleDisconnection() {
|
void _handleDisconnection() {
|
||||||
// Disable wake lock when connection is lost
|
|
||||||
WakelockPlus.disable();
|
|
||||||
_stopBatteryPolling();
|
_stopBatteryPolling();
|
||||||
|
|
||||||
for (final entry in _pendingRepeaterAcks.values) {
|
for (final entry in _pendingRepeaterAcks.values) {
|
||||||
@@ -3293,6 +3371,136 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_updateLocationandAdvert(Position position) async {
|
||||||
|
final snapToGridCenter = _sparseLocationLogger?.snapToGridCenter(
|
||||||
|
position: position,
|
||||||
|
cellSizeMeters: 0.001,
|
||||||
|
);
|
||||||
|
double lat = snapToGridCenter?.latitude ?? 0.0;
|
||||||
|
double lon = snapToGridCenter?.longitude ?? 0.0;
|
||||||
|
|
||||||
|
if (lat == 0.0 && lon == 0.0) {
|
||||||
|
debugPrint('Invalid location (0,0), skipping advert');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await sendFrame(buildSetOtherParamsFrame(true, 1, 1, 0));
|
||||||
|
await setNodeLocation(lat: lat, lon: lon);
|
||||||
|
await sendSelfAdvert(flood: true);
|
||||||
|
_selfLatitude = lat;
|
||||||
|
_selfLongitude = lon;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleRxData(Uint8List frame) {
|
||||||
|
final packet = BufferReader(frame);
|
||||||
|
packet.skipBytes(3); // Skip frame type byte
|
||||||
|
//final snr = packet.readByte() / 4.0;
|
||||||
|
//final rssi = packet.readByte();
|
||||||
|
final header = packet.readByte();
|
||||||
|
//final routeType = header & 0x03;
|
||||||
|
final payloadType = (header >> 2) & 0x0F;
|
||||||
|
//final payloadVer = (header >> 6) & 0x03;
|
||||||
|
|
||||||
|
if (packet.remaining <= 0) return;
|
||||||
|
final pathLen = packet.readByte();
|
||||||
|
|
||||||
|
if (packet.remaining < pathLen) return;
|
||||||
|
final pathBytes = packet.readBytes(pathLen);
|
||||||
|
|
||||||
|
if (packet.remaining <= 0) return;
|
||||||
|
final payload = packet.readBytes(packet.remaining);
|
||||||
|
|
||||||
|
switch (payloadType) {
|
||||||
|
case payloadTypeADVERT:
|
||||||
|
_handlePayloadAdvertReceived(payload, pathBytes);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handlePayloadAdvertReceived(Uint8List frame, Uint8List path) {
|
||||||
|
final advert = BufferReader(frame);
|
||||||
|
if (advert.remaining <= 32) return;
|
||||||
|
final publicKey = advert.readBytes(32);
|
||||||
|
final contactKeyHex = publicKey
|
||||||
|
.map((b) => b.toRadixString(16).padLeft(2, '0'))
|
||||||
|
.join();
|
||||||
|
if (advert.remaining <= 4) return;
|
||||||
|
final timestamp = advert.readInt32LE();
|
||||||
|
if (advert.remaining <= 64) return;
|
||||||
|
advert.skipBytes(64); // Skip signature for now
|
||||||
|
if (advert.remaining <= 1) return;
|
||||||
|
final flags = advert.readByte();
|
||||||
|
final type = flags & 0x0F;
|
||||||
|
final hasLocation = (flags & 0x10) != 0;
|
||||||
|
//final hasFeature1 = (flags & 0x20) != 0;
|
||||||
|
//final hasFeature2 = (flags & 0x40) != 0;
|
||||||
|
final hasName = (flags & 0x80) != 0;
|
||||||
|
double latitude = 0.0;
|
||||||
|
double longitude = 0.0;
|
||||||
|
if (hasLocation && advert.remaining >= 8) {
|
||||||
|
latitude = advert.readInt32LE() / 1e6;
|
||||||
|
longitude = advert.readInt32LE() / 1e6;
|
||||||
|
}
|
||||||
|
String name = '';
|
||||||
|
if (hasName && advert.remaining > 0) {
|
||||||
|
name = advert.readString();
|
||||||
|
}
|
||||||
|
// Check if this is a new contact
|
||||||
|
final isNewContact = !_knownContactKeys.contains(contactKeyHex);
|
||||||
|
if (isNewContact) {
|
||||||
|
_handleContactAdvert(
|
||||||
|
Contact(
|
||||||
|
publicKey: publicKey,
|
||||||
|
name: name,
|
||||||
|
type: type,
|
||||||
|
pathLength: path.length,
|
||||||
|
path: path,
|
||||||
|
latitude: latitude,
|
||||||
|
longitude: longitude,
|
||||||
|
lastSeen: DateTime.fromMillisecondsSinceEpoch(timestamp * 1000),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final existingIndex = _contacts.indexWhere(
|
||||||
|
(c) => c.publicKeyHex == contactKeyHex,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (existingIndex >= 0) {
|
||||||
|
final existing = _contacts[existingIndex];
|
||||||
|
final mergedLastMessageAt = existing.lastMessageAt.isAfter(DateTime.now())
|
||||||
|
? DateTime.now()
|
||||||
|
: existing.lastMessageAt;
|
||||||
|
|
||||||
|
appLogger.info(
|
||||||
|
'Refreshing contact ${existing.name}: devicePath=${existing.pathLength}, existingOverride=${existing.pathOverride}',
|
||||||
|
tag: 'Connector',
|
||||||
|
);
|
||||||
|
|
||||||
|
// CRITICAL: Preserve user's path override when contact is refreshed from device
|
||||||
|
_contacts[existingIndex] = existing.copyWith(
|
||||||
|
latitude: hasLocation ? latitude : existing.latitude,
|
||||||
|
longitude: hasLocation ? longitude : existing.longitude,
|
||||||
|
name: hasName ? name : existing.name,
|
||||||
|
path: path,
|
||||||
|
pathLength: path.length,
|
||||||
|
lastMessageAt: mergedLastMessageAt,
|
||||||
|
lastSeen: DateTime.fromMillisecondsSinceEpoch(timestamp * 1000),
|
||||||
|
pathOverride: existing.pathOverride, // Preserve user's path choice
|
||||||
|
pathOverrideBytes: existing.pathOverrideBytes,
|
||||||
|
);
|
||||||
|
|
||||||
|
appLogger.info(
|
||||||
|
'After merge: pathOverride=${_contacts[existingIndex].pathOverride}, devicePath=${_contacts[existingIndex].pathLength}',
|
||||||
|
tag: 'Connector',
|
||||||
|
);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const int _phRouteMask = 0x03;
|
const int _phRouteMask = 0x03;
|
||||||
|
|||||||
@@ -151,6 +151,7 @@ const int cmdGetContactByKey = 30;
|
|||||||
const int cmdGetChannel = 31;
|
const int cmdGetChannel = 31;
|
||||||
const int cmdSetChannel = 32;
|
const int cmdSetChannel = 32;
|
||||||
const int cmdSendTracePath = 36;
|
const int cmdSendTracePath = 36;
|
||||||
|
const int cmdSetOtherParams = 38;
|
||||||
const int cmdGetRadioSettings = 57;
|
const int cmdGetRadioSettings = 57;
|
||||||
const int cmdGetTelemetryReq = 39;
|
const int cmdGetTelemetryReq = 39;
|
||||||
const int cmdGetCustomVar = 40;
|
const int cmdGetCustomVar = 40;
|
||||||
@@ -212,6 +213,30 @@ const int advTypeRepeater = 2;
|
|||||||
const int advTypeRoom = 3;
|
const int advTypeRoom = 3;
|
||||||
const int advTypeSensor = 4;
|
const int advTypeSensor = 4;
|
||||||
|
|
||||||
|
// Payload Types
|
||||||
|
const int payloadTypeREQ =
|
||||||
|
0x00; // request (prefixed with dest/src hashes, MAC) (enc data: timestamp, blob)
|
||||||
|
const int payloadTypeRESPONSE =
|
||||||
|
0x01; // response to REQ or ANON_REQ (prefixed with dest/src hashes, MAC) (enc data: timestamp, blob)
|
||||||
|
const int payloadTypeTXTMSG =
|
||||||
|
0x02; // a plain text message (prefixed with dest/src hashes, MAC) (enc data: timestamp, text)
|
||||||
|
const int payloadTypeACK = 0x03; // a simple ack
|
||||||
|
const int payloadTypeADVERT = 0x04; // a node advertising its Identity
|
||||||
|
const int payloadTypeGRPTXT =
|
||||||
|
0x05; // an (unverified) group text message (prefixed with channel hash, MAC) (enc data: timestamp, "name: msg")
|
||||||
|
const int payloadTypeGRPDATA =
|
||||||
|
0x06; // an (unverified) group datagram (prefixed with channel hash, MAC) (enc data: timestamp, blob)
|
||||||
|
const int payloadTypeANONREQ =
|
||||||
|
0x07; // generic request (prefixed with dest_hash, ephemeral pub_key, MAC) (enc data: ...)
|
||||||
|
const int payloadTypePATH =
|
||||||
|
0x08; // returned path (prefixed with dest/src hashes, MAC) (enc data: path, extra)
|
||||||
|
const int payloadTypeTRACE = 0x09; // trace a path, collecting SNI for each hop
|
||||||
|
const int payloadTypeMULTIPART = 0x0A; // packet is one of a set of packets
|
||||||
|
const int payloadTypeCONTROL = 0x0B; // a control/discovery packet
|
||||||
|
//...
|
||||||
|
const int payloadTypeRawCustom =
|
||||||
|
0x0F; // custom packet as raw bytes, for applications with custom encryption, payloads, etc
|
||||||
|
|
||||||
// Sizes
|
// Sizes
|
||||||
const int pubKeySize = 32;
|
const int pubKeySize = 32;
|
||||||
const int maxPathSize = 64;
|
const int maxPathSize = 64;
|
||||||
@@ -777,3 +802,22 @@ Uint8List buildZeroHopContact(Uint8List pubKey) {
|
|||||||
writer.writeBytes(pubKey);
|
writer.writeBytes(pubKey);
|
||||||
return writer.toBytes();
|
return writer.toBytes();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build CMD_SET_OTHER_PARAMS frame
|
||||||
|
// Format: [cmd][allowAutoAddContacts][allowTelemetryFlags][advertLocationPolicy][multiAcks]
|
||||||
|
Uint8List buildSetOtherParamsFrame(
|
||||||
|
bool allowAutoAddContacts,
|
||||||
|
int allowTelemetryFlags,
|
||||||
|
int advertLocationPolicy,
|
||||||
|
int multiAcks,
|
||||||
|
) {
|
||||||
|
final writer = BufferWriter();
|
||||||
|
writer.writeByte(cmdSetOtherParams);
|
||||||
|
writer.writeByte(
|
||||||
|
allowAutoAddContacts ? 0x00 : 0x01,
|
||||||
|
); // Allow Auto Add Contacts
|
||||||
|
writer.writeByte(allowTelemetryFlags); // Allow Telemetry Flags
|
||||||
|
writer.writeByte(advertLocationPolicy); // Advertisement Location Policy
|
||||||
|
writer.writeByte(multiAcks); // Multi Acknowledgements
|
||||||
|
return writer.toBytes();
|
||||||
|
}
|
||||||
|
|||||||
+39
-39
@@ -96,14 +96,14 @@
|
|||||||
"settings_privacyModeEnabled": "Datenschutzmodus aktiviert",
|
"settings_privacyModeEnabled": "Datenschutzmodus aktiviert",
|
||||||
"settings_privacyModeDisabled": "Datenschutzmodus deaktiviert",
|
"settings_privacyModeDisabled": "Datenschutzmodus deaktiviert",
|
||||||
"settings_actions": "Aktionen",
|
"settings_actions": "Aktionen",
|
||||||
"settings_sendAdvertisement": "Sende eine Ankündigung",
|
"settings_sendAdvertisement": "Sende Ankündigung",
|
||||||
"settings_sendAdvertisementSubtitle": "Sende Ankündigung",
|
"settings_sendAdvertisementSubtitle": "Sende eine Ankündigung",
|
||||||
"settings_advertisementSent": "Ankündigung gesendet",
|
"settings_advertisementSent": "Ankündigung gesendet",
|
||||||
"settings_syncTime": "Zeitsynchronisierung",
|
"settings_syncTime": "Zeitsynchronisierung",
|
||||||
"settings_syncTimeSubtitle": "Stelle die Gerätezeit auf die Uhrzeit des Telefons ein",
|
"settings_syncTimeSubtitle": "Stelle die Gerätezeit auf die Uhrzeit des Telefons ein",
|
||||||
"settings_timeSynchronized": "Zeit synchronisiert",
|
"settings_timeSynchronized": "Zeit synchronisiert",
|
||||||
"settings_refreshContacts": "Kontakte aktualisieren",
|
"settings_refreshContacts": "Kontakte aktualisieren",
|
||||||
"settings_refreshContactsSubtitle": "Kontakte-Liste vom Gerät neu laden",
|
"settings_refreshContactsSubtitle": "Kontakt-Liste vom Gerät neu laden",
|
||||||
"settings_rebootDevice": "Gerät neu starten",
|
"settings_rebootDevice": "Gerät neu starten",
|
||||||
"settings_rebootDeviceSubtitle": "MeshCore-Gerät neu starten",
|
"settings_rebootDeviceSubtitle": "MeshCore-Gerät neu starten",
|
||||||
"settings_rebootDeviceConfirm": "Sind Sie sicher, dass Sie das Gerät neu starten möchten? Sie werden getrennt.",
|
"settings_rebootDeviceConfirm": "Sind Sie sicher, dass Sie das Gerät neu starten möchten? Sie werden getrennt.",
|
||||||
@@ -540,7 +540,7 @@
|
|||||||
"chat_routingMode": "Routenmodus",
|
"chat_routingMode": "Routenmodus",
|
||||||
"chat_autoUseSavedPath": "Automatisch (gespeicherten Pfad verwenden)",
|
"chat_autoUseSavedPath": "Automatisch (gespeicherten Pfad verwenden)",
|
||||||
"chat_forceFloodMode": "Flut-Modus erzwingen",
|
"chat_forceFloodMode": "Flut-Modus erzwingen",
|
||||||
"chat_recentAckPaths": "Aktuelle ACK-Pfade (tasten, um zu verwenden):",
|
"chat_recentAckPaths": "Aktuelle ACK-Pfade (antippen, um zu verwenden):",
|
||||||
"chat_pathHistoryFull": "Die Pfadhistorie ist voll. Entferne Einträge, um neue hinzuzufügen.",
|
"chat_pathHistoryFull": "Die Pfadhistorie ist voll. Entferne Einträge, um neue hinzuzufügen.",
|
||||||
"chat_hopSingular": "Sprung",
|
"chat_hopSingular": "Sprung",
|
||||||
"chat_hopPlural": "Sprünge",
|
"chat_hopPlural": "Sprünge",
|
||||||
@@ -554,7 +554,7 @@
|
|||||||
},
|
},
|
||||||
"chat_successes": "Erfolgreich",
|
"chat_successes": "Erfolgreich",
|
||||||
"chat_removePath": "Pfad entfernen",
|
"chat_removePath": "Pfad entfernen",
|
||||||
"chat_noPathHistoryYet": "Keine eine Pfadhistorie vorhanden.\nSende eine Nachricht, um Pfade zu entdecken.",
|
"chat_noPathHistoryYet": "Keine Pfadhistorie vorhanden.\nSende eine Nachricht, um Pfade zu entdecken.",
|
||||||
"chat_pathActions": "Pfadaktionen:",
|
"chat_pathActions": "Pfadaktionen:",
|
||||||
"chat_setCustomPath": "Lege benutzerdefinierten Pfad fest",
|
"chat_setCustomPath": "Lege benutzerdefinierten Pfad fest",
|
||||||
"chat_setCustomPathSubtitle": "Manuellen Routenpfad festlegen",
|
"chat_setCustomPathSubtitle": "Manuellen Routenpfad festlegen",
|
||||||
@@ -717,7 +717,7 @@
|
|||||||
"mapCache_cacheArea": "Zwischenspeicherbereich",
|
"mapCache_cacheArea": "Zwischenspeicherbereich",
|
||||||
"mapCache_useCurrentView": "Aktuelle Ansicht verwenden",
|
"mapCache_useCurrentView": "Aktuelle Ansicht verwenden",
|
||||||
"mapCache_zoomRange": "Zoom Bereich",
|
"mapCache_zoomRange": "Zoom Bereich",
|
||||||
"mapCache_estimatedTiles": "Geschätzte Fliesen: {count}",
|
"mapCache_estimatedTiles": "Geschätzte Kacheln: {count}",
|
||||||
"@mapCache_estimatedTiles": {
|
"@mapCache_estimatedTiles": {
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
"count": {
|
"count": {
|
||||||
@@ -854,7 +854,7 @@
|
|||||||
},
|
},
|
||||||
"path_enterCustomPath": "Gebe Pfad ein",
|
"path_enterCustomPath": "Gebe Pfad ein",
|
||||||
"path_currentPathLabel": "Aktueller Pfad",
|
"path_currentPathLabel": "Aktueller Pfad",
|
||||||
"path_hexPrefixInstructions": "Gebe für jeden Hopfen 2-stellige Hex-Präfixe ein, getrennt durch Kommas.",
|
"path_hexPrefixInstructions": "Gebe für jeden Zwischen-Hop das 2-stellige Hex-Präfix ein, getrennt durch Kommas.",
|
||||||
"path_hexPrefixExample": "Beispiel: A1,F2,3C (jeder Knoten verwendet den ersten Byte seines öffentlichen Schlüssels)",
|
"path_hexPrefixExample": "Beispiel: A1,F2,3C (jeder Knoten verwendet den ersten Byte seines öffentlichen Schlüssels)",
|
||||||
"path_labelHexPrefixes": "Pfad (Hex-Präfixe)",
|
"path_labelHexPrefixes": "Pfad (Hex-Präfixe)",
|
||||||
"path_helperMaxHops": "Max 64 Sprünge. Jede Präfixe ist 2 Hexadezimalzeichen (1 Byte)",
|
"path_helperMaxHops": "Max 64 Sprünge. Jede Präfixe ist 2 Hexadezimalzeichen (1 Byte)",
|
||||||
@@ -887,7 +887,7 @@
|
|||||||
"repeater_forceFloodMode": "Flut-Modus erzwingen",
|
"repeater_forceFloodMode": "Flut-Modus erzwingen",
|
||||||
"repeater_pathManagement": "Pfadverwaltung",
|
"repeater_pathManagement": "Pfadverwaltung",
|
||||||
"repeater_refresh": "Aktualisieren",
|
"repeater_refresh": "Aktualisieren",
|
||||||
"repeater_statusRequestTimeout": "Statusanfrage zeitweise fehlgeschlagen.",
|
"repeater_statusRequestTimeout": "Statusanfrage durch Timeout fehlgeschlagen.",
|
||||||
"repeater_errorLoadingStatus": "Fehler beim Laden des Status: {error}",
|
"repeater_errorLoadingStatus": "Fehler beim Laden des Status: {error}",
|
||||||
"@repeater_errorLoadingStatus": {
|
"@repeater_errorLoadingStatus": {
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
@@ -957,7 +957,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"repeater_duplicatesFloodDirect": "Überflut: {flood}, Direkt: {direct}",
|
"repeater_duplicatesFloodDirect": "Flut: {flood}, Direkt: {direct}",
|
||||||
"@repeater_duplicatesFloodDirect": {
|
"@repeater_duplicatesFloodDirect": {
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
"flood": {
|
"flood": {
|
||||||
@@ -983,7 +983,7 @@
|
|||||||
"repeater_adminPassword": "Admin-Passwort",
|
"repeater_adminPassword": "Admin-Passwort",
|
||||||
"repeater_adminPasswordHelper": "Vollzugriffspasswort",
|
"repeater_adminPasswordHelper": "Vollzugriffspasswort",
|
||||||
"repeater_guestPassword": "Gast-Passwort",
|
"repeater_guestPassword": "Gast-Passwort",
|
||||||
"repeater_guestPasswordHelper": "Schreibgeschützter Zugriffspasswort",
|
"repeater_guestPasswordHelper": "Schreibgeschütztes Zugriffspasswort",
|
||||||
"repeater_radioSettings": "Funk Einstellungen",
|
"repeater_radioSettings": "Funk Einstellungen",
|
||||||
"repeater_frequencyMhz": "Frequenz (MHz)",
|
"repeater_frequencyMhz": "Frequenz (MHz)",
|
||||||
"repeater_frequencyHelper": "300-2500 MHz",
|
"repeater_frequencyHelper": "300-2500 MHz",
|
||||||
@@ -1086,11 +1086,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"repeater_cliTitle": "Repeater CLI",
|
"repeater_cliTitle": "Repeater CLI",
|
||||||
"repeater_debugNextCommand": "Fehlersuche Nächster Befehl",
|
"repeater_debugNextCommand": "Fehlersuche des nächsten Befehls",
|
||||||
"repeater_commandHelp": "Hilfe",
|
"repeater_commandHelp": "Hilfe",
|
||||||
"repeater_clearHistory": "Löschen der Historie",
|
"repeater_clearHistory": "Löschen der Historie",
|
||||||
"repeater_noCommandsSent": "Noch keine Befehle gesendet.",
|
"repeater_noCommandsSent": "Noch keine Befehle gesendet.",
|
||||||
"repeater_typeCommandOrUseQuick": "Geben Sie einen Befehl unten ein oder verwenden Sie Schnellbefehle",
|
"repeater_typeCommandOrUseQuick": "Geben Sie unten einen Befehl ein oder verwenden Sie die Schnellbefehle",
|
||||||
"repeater_enterCommandHint": "Geben Sie den Befehl ein...",
|
"repeater_enterCommandHint": "Geben Sie den Befehl ein...",
|
||||||
"repeater_previousCommand": "Vorhergehende Aktion",
|
"repeater_previousCommand": "Vorhergehende Aktion",
|
||||||
"repeater_nextCommand": "Nächste Aktion",
|
"repeater_nextCommand": "Nächste Aktion",
|
||||||
@@ -1132,7 +1132,7 @@
|
|||||||
"repeater_cliHelpSetLat": "Legt die Breitengrad der Ankündigung fest. (dezimale Grad)",
|
"repeater_cliHelpSetLat": "Legt die Breitengrad der Ankündigung fest. (dezimale Grad)",
|
||||||
"repeater_cliHelpSetLon": "Legt die Längengrade der Ankündigung fest. (dezimale Grad)",
|
"repeater_cliHelpSetLon": "Legt die Längengrade der Ankündigung fest. (dezimale Grad)",
|
||||||
"repeater_cliHelpSetRadio": "Legt komplett neue Radio-Parameter fest und speichert diese als Präferenzen. Benötigt einen \"Reboot\"-Befehl, um sie anzuwenden.",
|
"repeater_cliHelpSetRadio": "Legt komplett neue Radio-Parameter fest und speichert diese als Präferenzen. Benötigt einen \"Reboot\"-Befehl, um sie anzuwenden.",
|
||||||
"repeater_cliHelpSetRxDelay": "Sets (experimentell) als Basis (muss > 1 sein für den Effekt) zur Anwendung einer leichten Verzögerung bei empfangenen Paketen, basierend auf Signalstärke/Punktzahl. Auf 0 setzen, um die Funktion zu deaktivieren.",
|
"repeater_cliHelpSetRxDelay": "Fügt eine leichte Verzögerung bei empfangenen Paketen hinzu, basierend auf Signalstärke/Punktzahl. Auf 0 setzen, um die Funktion zu deaktivieren.",
|
||||||
"repeater_cliHelpSetTxDelay": "Legt einen Faktor fest, der mit der Zeit bei voller Zuluft für ein Flood-Mode-Paket und mit einem zufälligen Slot-System multipliziert wird, um dessen Weiterleitung zu verzögern (um Kollisionen zu vermeiden).",
|
"repeater_cliHelpSetTxDelay": "Legt einen Faktor fest, der mit der Zeit bei voller Zuluft für ein Flood-Mode-Paket und mit einem zufälligen Slot-System multipliziert wird, um dessen Weiterleitung zu verzögern (um Kollisionen zu vermeiden).",
|
||||||
"repeater_cliHelpSetDirectTxDelay": "Ähnlich wie txdelay, aber zum Anwenden einer zufälligen Verzögerung bei der Weiterleitung von Direktmodus-Paketen.",
|
"repeater_cliHelpSetDirectTxDelay": "Ähnlich wie txdelay, aber zum Anwenden einer zufälligen Verzögerung bei der Weiterleitung von Direktmodus-Paketen.",
|
||||||
"repeater_cliHelpSetBridgeEnabled": "Brücke aktivieren/deaktivieren.",
|
"repeater_cliHelpSetBridgeEnabled": "Brücke aktivieren/deaktivieren.",
|
||||||
@@ -1143,14 +1143,14 @@
|
|||||||
"repeater_cliHelpSetAdcMultiplier": "Legt einen benutzerdefinierten Faktor zur Anpassung der gemeldeten Batteriewirkspannung fest (nur auf ausgewählten Boards unterstützt).",
|
"repeater_cliHelpSetAdcMultiplier": "Legt einen benutzerdefinierten Faktor zur Anpassung der gemeldeten Batteriewirkspannung fest (nur auf ausgewählten Boards unterstützt).",
|
||||||
"repeater_cliHelpTempRadio": "Legt vorübergehende Funkparameter für die angegebene Anzahl von Minuten fest und kehrt anschließend zu den ursprünglichen Funkparametern zurück (wird nicht in den Einstellungen gespeichert).",
|
"repeater_cliHelpTempRadio": "Legt vorübergehende Funkparameter für die angegebene Anzahl von Minuten fest und kehrt anschließend zu den ursprünglichen Funkparametern zurück (wird nicht in den Einstellungen gespeichert).",
|
||||||
"repeater_cliHelpSetPerm": "Ändert die ACL. Entfernt das passende Eintragen (durch Pubkey-Präfix), wenn \"permissions\" auf 0 steht. Fügt ein neues Eintragen hinzu, wenn die Pubkey-Hex-Länge vollständig ist und nicht bereits in der ACL vorhanden ist. Aktualisiert das Eintragen anhand des übereinstimmenden Pubkey-Präfix. Berechtigungsbits variieren je nach Firmware-Rolle, aber die unteren 2 Bits sind: 0 (Gast), 1 (Nur Lesen), 2 (Lesen/Schreiben), 3 (Admin)",
|
"repeater_cliHelpSetPerm": "Ändert die ACL. Entfernt das passende Eintragen (durch Pubkey-Präfix), wenn \"permissions\" auf 0 steht. Fügt ein neues Eintragen hinzu, wenn die Pubkey-Hex-Länge vollständig ist und nicht bereits in der ACL vorhanden ist. Aktualisiert das Eintragen anhand des übereinstimmenden Pubkey-Präfix. Berechtigungsbits variieren je nach Firmware-Rolle, aber die unteren 2 Bits sind: 0 (Gast), 1 (Nur Lesen), 2 (Lesen/Schreiben), 3 (Admin)",
|
||||||
"repeater_cliHelpGetBridgeType": "Ruft Brückentyp none, rs232, espnow ab.",
|
"repeater_cliHelpGetBridgeType": "Ruft Brückentyp: none, rs232, espnow ab.",
|
||||||
"repeater_cliHelpLogStart": "Beginnt die Paketprotokollierung in das Dateisystem.",
|
"repeater_cliHelpLogStart": "Beginnt die Paketprotokollierung in das Dateisystem.",
|
||||||
"repeater_cliHelpLogStop": "Stoppt das Paketprotokollieren in das Dateisystem.",
|
"repeater_cliHelpLogStop": "Stoppt das Paketprotokollieren in das Dateisystem.",
|
||||||
"repeater_cliHelpLogErase": "Löscht die Paketprotokolle aus dem Dateisystem.",
|
"repeater_cliHelpLogErase": "Löscht die Paketprotokolle aus dem Dateisystem.",
|
||||||
"repeater_cliHelpNeighbors": "Zeigt eine Liste anderer Repeater-Knoten an, die über Zero-Hop-Ankündigung gehört wurden. Jede Zeile ist id-prefix-hex:timestamp:snr-times-4",
|
"repeater_cliHelpNeighbors": "Zeigt eine Liste anderer Repeater-Knoten an, die über Zero-Hop-Ankündigung gehört wurden. Jede Zeile ist id-prefix-hex:timestamp:snr-times-4",
|
||||||
"repeater_cliHelpNeighborRemove": "Entfernt das erste übereinstimmende Element (über Pubkey-Präfix (hex)) aus der Liste der Nachbarn.",
|
"repeater_cliHelpNeighborRemove": "Entfernt das erste übereinstimmende Element (über Pubkey-Präfix (hex)) aus der Liste der Nachbarn.",
|
||||||
"repeater_cliHelpRegion": "Listet alle definierten Regionen auf.",
|
"repeater_cliHelpRegion": "Listet alle definierten Regionen auf.",
|
||||||
"repeater_cliHelpRegionLoad": "Hinweis: Dies ist ein spezieller Mehrbefehl-Aufruf. Jeder nachfolgende Befehl ist ein Regionsname (eingedruckt mit Leerzeichen zur Angabe der übergeordneten Hierarchie, mit mindestens einem Leerzeichen). Beendet durch das Senden einer Leerzeile/des Befehls.",
|
"repeater_cliHelpRegionLoad": "Hinweis: Dies ist ein spezieller Mehrbefehl-Aufruf. Jeder nachfolgende Befehl ist ein Regionsname (eingerückt mit Leerzeichen zur Angabe der übergeordneten Hierarchie, mit mindestens einem Leerzeichen). Beendet durch das Senden einer Leerzeile.",
|
||||||
"repeater_cliHelpRegionGet": "Sucht die Region mit dem gegebenen Namenspräfix (oder \"\\\" für den globalen Scope) und antwortet mit \"-> region-name (parent-name) 'F'\".",
|
"repeater_cliHelpRegionGet": "Sucht die Region mit dem gegebenen Namenspräfix (oder \"\\\" für den globalen Scope) und antwortet mit \"-> region-name (parent-name) 'F'\".",
|
||||||
"repeater_cliHelpRegionPut": "Fügt eine Region-Definition mit dem angegebenen Namen hinzu oder aktualisiert diese.",
|
"repeater_cliHelpRegionPut": "Fügt eine Region-Definition mit dem angegebenen Namen hinzu oder aktualisiert diese.",
|
||||||
"repeater_cliHelpRegionRemove": "Löscht eine Regiondefinition mit dem angegebenen Namen. (muss genau übereinstimmen und keine Kindregionen haben)",
|
"repeater_cliHelpRegionRemove": "Löscht eine Regiondefinition mit dem angegebenen Namen. (muss genau übereinstimmen und keine Kindregionen haben)",
|
||||||
@@ -1243,7 +1243,7 @@
|
|||||||
"channelPath_otherObservedPaths": "Sonstige beobachtete Pfade",
|
"channelPath_otherObservedPaths": "Sonstige beobachtete Pfade",
|
||||||
"channelPath_repeaterHops": "Repeater-Sprünge",
|
"channelPath_repeaterHops": "Repeater-Sprünge",
|
||||||
"channelPath_noHopDetails": "Die Detailangaben für dieses Paket sind nicht verfügbar.",
|
"channelPath_noHopDetails": "Die Detailangaben für dieses Paket sind nicht verfügbar.",
|
||||||
"channelPath_messageDetails": "Nachrichtsdetails",
|
"channelPath_messageDetails": "Nachrichtendetails",
|
||||||
"channelPath_senderLabel": "Sender",
|
"channelPath_senderLabel": "Sender",
|
||||||
"channelPath_timeLabel": "Zeit",
|
"channelPath_timeLabel": "Zeit",
|
||||||
"channelPath_repeatsLabel": "Wiederholungen",
|
"channelPath_repeatsLabel": "Wiederholungen",
|
||||||
@@ -1347,7 +1347,7 @@
|
|||||||
"listFilter_users": "Benutzer",
|
"listFilter_users": "Benutzer",
|
||||||
"listFilter_repeaters": "Repeater",
|
"listFilter_repeaters": "Repeater",
|
||||||
"listFilter_roomServers": "Raumserver",
|
"listFilter_roomServers": "Raumserver",
|
||||||
"listFilter_unreadOnly": "Nur nicht gelesen",
|
"listFilter_unreadOnly": "Nicht gelesen",
|
||||||
"listFilter_newGroup": "Neue Gruppe",
|
"listFilter_newGroup": "Neue Gruppe",
|
||||||
"@neighbors_errorLoading": {
|
"@neighbors_errorLoading": {
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
@@ -1358,11 +1358,11 @@
|
|||||||
},
|
},
|
||||||
"repeater_neighbours": "Nachbarn",
|
"repeater_neighbours": "Nachbarn",
|
||||||
"repeater_neighboursSubtitle": "Anzahl der Hop-Nachbarn anzeigen.",
|
"repeater_neighboursSubtitle": "Anzahl der Hop-Nachbarn anzeigen.",
|
||||||
"neighbors_receivedData": "Empfangene Nachbarendaten",
|
"neighbors_receivedData": "Empfangene Nachbarsdaten",
|
||||||
"neighbors_requestTimedOut": "Nachbarn melden zeitweise Ausfall.",
|
"neighbors_requestTimedOut": "Anfrage durch Timeout fehlgeschlagen.",
|
||||||
"neighbors_errorLoading": "Fehler beim Laden der Nachbarn: {error}",
|
"neighbors_errorLoading": "Fehler beim Laden der Nachbarn: {error}",
|
||||||
"neighbors_repeatersNeighbours": "Nachbarn",
|
"neighbors_repeatersNeighbours": "Nachbarn",
|
||||||
"neighbors_noData": "Keine Nachbardaten verfügbar.",
|
"neighbors_noData": "Keine Nachbarsdaten verfügbar.",
|
||||||
"channels_joinPrivateChannel": "Treten Sie einem privaten Kanal bei",
|
"channels_joinPrivateChannel": "Treten Sie einem privaten Kanal bei",
|
||||||
"channels_joinPrivateChannelDesc": "Manuelle Eingabe eines geheimen Schlüssels.",
|
"channels_joinPrivateChannelDesc": "Manuelle Eingabe eines geheimen Schlüssels.",
|
||||||
"channels_createPrivateChannel": "Erstelle einen privaten Kanal",
|
"channels_createPrivateChannel": "Erstelle einen privaten Kanal",
|
||||||
@@ -1389,8 +1389,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"neighbors_heardAgo": "Hörte: {time} vor her.",
|
"neighbors_heardAgo": "Gehört vor: {time}",
|
||||||
"neighbors_unknownContact": "Unbekannte {pubkey}",
|
"neighbors_unknownContact": "Unbekannt {pubkey}",
|
||||||
"settings_locationGPSEnable": "GPS aktivieren",
|
"settings_locationGPSEnable": "GPS aktivieren",
|
||||||
"settings_locationGPSEnableSubtitle": "Aktiviert GPS zur automatischen Aktualisierung des Standorts.",
|
"settings_locationGPSEnableSubtitle": "Aktiviert GPS zur automatischen Aktualisierung des Standorts.",
|
||||||
"settings_locationIntervalSec": "Intervall für GPS (Sekunden)",
|
"settings_locationIntervalSec": "Intervall für GPS (Sekunden)",
|
||||||
@@ -1493,9 +1493,9 @@
|
|||||||
"community_deleted": "Community \"{name}\" verlassen",
|
"community_deleted": "Community \"{name}\" verlassen",
|
||||||
"community_addHashtagChannel": "Füge einen Community-Hashtag hinzu",
|
"community_addHashtagChannel": "Füge einen Community-Hashtag hinzu",
|
||||||
"community_addHashtagChannelDesc": "Füge einen Hashtag-Kanal für diese Community hinzu",
|
"community_addHashtagChannelDesc": "Füge einen Hashtag-Kanal für diese Community hinzu",
|
||||||
"community_selectCommunity": "Wählen Sie Community",
|
"community_selectCommunity": "Wählen Sie eine Community",
|
||||||
"community_regularHashtag": "Regulärer Hashtag",
|
"community_regularHashtag": "Regulärer Hashtag",
|
||||||
"community_regularHashtagDesc": "Öffentliches Hashtag (jeder kann teilnehmen)",
|
"community_regularHashtagDesc": "Öffentlicher Hashtag (jeder kann teilnehmen)",
|
||||||
"community_communityHashtagDesc": "Nur für Mitglieder der Community",
|
"community_communityHashtagDesc": "Nur für Mitglieder der Community",
|
||||||
"community_forCommunity": "Für {name}",
|
"community_forCommunity": "Für {name}",
|
||||||
"community_communityHashtag": "Community Hashtag",
|
"community_communityHashtag": "Community Hashtag",
|
||||||
@@ -1559,16 +1559,16 @@
|
|||||||
"appSettings_languageUk": "Ukrainisch",
|
"appSettings_languageUk": "Ukrainisch",
|
||||||
"contacts_contactImported": "Kontakt wurde importiert.",
|
"contacts_contactImported": "Kontakt wurde importiert.",
|
||||||
"contacts_contactImportFailed": "Kontakt konnte nicht importiert werden",
|
"contacts_contactImportFailed": "Kontakt konnte nicht importiert werden",
|
||||||
"contacts_zeroHopAdvert": "Zero-Hop-Anzeige",
|
"contacts_zeroHopAdvert": "Zero-Hop-Ankündigung",
|
||||||
"contacts_floodAdvert": "Überflutungsanzeige",
|
"contacts_floodAdvert": "Flut-Ankündigung",
|
||||||
"contacts_addContactFromClipboard": "Kontakt aus Zwischenablage hinzufügen",
|
"contacts_addContactFromClipboard": "Kontakt aus Zwischenablage hinzufügen",
|
||||||
"contacts_ShareContactZeroHop": "Kontakt über Anzeige teilen",
|
"contacts_ShareContactZeroHop": "Kontakt über Anzeige teilen",
|
||||||
"contacts_copyAdvertToClipboard": "Werbung in die Zwischenablage kopieren",
|
"contacts_copyAdvertToClipboard": "Ankündigung in die Zwischenablage kopieren",
|
||||||
"contacts_ShareContact": "Kontakt in die Zwischenablage kopieren",
|
"contacts_ShareContact": "Kontakt in die Zwischenablage kopieren",
|
||||||
"contacts_zeroHopContactAdvertFailed": "Kontakt konnte nicht gesendet werden.",
|
"contacts_zeroHopContactAdvertFailed": "Kontakt konnte nicht gesendet werden.",
|
||||||
"contacts_zeroHopContactAdvertSent": "Kontakt über Anzeige gesendet",
|
"contacts_zeroHopContactAdvertSent": "Kontakt über Anzeige gesendet",
|
||||||
"contacts_contactAdvertCopied": "Anzeige in die Zwischenablage kopiert.",
|
"contacts_contactAdvertCopied": "Anzeige in die Zwischenablage kopiert.",
|
||||||
"contacts_contactAdvertCopyFailed": "Kopieren des Werbeinhalts in die Zwischenablage fehlgeschlagen.",
|
"contacts_contactAdvertCopyFailed": "Kopieren der Ankündigung in die Zwischenablage fehlgeschlagen.",
|
||||||
|
|
||||||
"notification_activityTitle": "MeshCore Aktivität",
|
"notification_activityTitle": "MeshCore Aktivität",
|
||||||
"notification_messagesCount": "{count} {count, plural, =1{Nachricht} other{Nachrichten}}",
|
"notification_messagesCount": "{count} {count, plural, =1{Nachricht} other{Nachrichten}}",
|
||||||
@@ -1596,22 +1596,22 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"notification_receivedNewMessage": "Neue Nachricht empfangen",
|
"notification_receivedNewMessage": "Neue Nachricht empfangen",
|
||||||
"contacts_contactAdvertCopyFailed": "Kopieren des Werbeinhalts in die Zwischenablage fehlgeschlagen.",
|
"contacts_contactAdvertCopyFailed": "Kopieren der Ankündigung in die Zwischenablage fehlgeschlagen.",
|
||||||
"settings_gpxExportAll": "Alle Kontakte nach GPX exportieren",
|
"settings_gpxExportAll": "Alle Knoten als GPX exportieren",
|
||||||
"settings_gpxExportAllSubtitle": "Exportiert alle Kontakte mit einem Standort in eine GPX-Datei.",
|
"settings_gpxExportAllSubtitle": "Exportiert alle Knoten mit einem Standort in eine GPX-Datei.",
|
||||||
"settings_gpxExportRepeaters": "Repeater und Raumserver nach GPX exportieren",
|
"settings_gpxExportRepeaters": "Repeater und Raumserver als GPX exportieren",
|
||||||
"settings_gpxExportContacts": "Begleiter nach GPX exportieren",
|
"settings_gpxExportContacts": "Kontakte als GPX exportieren",
|
||||||
"settings_gpxExportRepeatersSubtitle": "Exportiert Repeater und Raumserver mit einem Standort in eine GPX-Datei.",
|
"settings_gpxExportRepeatersSubtitle": "Exportiert Repeater und Raumserver mit einem Standort in eine GPX-Datei.",
|
||||||
"settings_gpxExportContactsSubtitle": "Exportiert Begleiter mit einem Ort in eine GPX-Datei.",
|
"settings_gpxExportContactsSubtitle": "Exportiert Kontakte mit einem Ort in eine GPX-Datei.",
|
||||||
"settings_gpxExportRepeatersRoom": "Repeater- und Raumserver-Standorte",
|
"settings_gpxExportRepeatersRoom": "Repeater- und Raumserver-Standorte",
|
||||||
"settings_gpxExportChat": "Begleiterstandorte",
|
"settings_gpxExportChat": "Kontaktstandorte",
|
||||||
"settings_gpxExportNoContacts": "Keine Kontakte zum Exportieren.",
|
"settings_gpxExportNoContacts": "Keine Kontakte zum Exportieren.",
|
||||||
"settings_gpxExportError": "Beim Export ist ein Fehler aufgetreten.",
|
"settings_gpxExportError": "Beim Export ist ein Fehler aufgetreten.",
|
||||||
"settings_gpxExportNotAvailable": "Nicht auf Ihrem Gerät/Betriebssystem unterstützt",
|
"settings_gpxExportNotAvailable": "Nicht auf Ihrem Gerät/Betriebssystem unterstützt",
|
||||||
"settings_gpxExportSuccess": "Erfolgreich GPX-Datei exportiert.",
|
"settings_gpxExportSuccess": "GPX-Datei erfolgreich exportiert.",
|
||||||
"settings_gpxExportAllContacts": "Alle Kontaktstandorte",
|
"settings_gpxExportAllContacts": "Alle Kontaktstandorte",
|
||||||
"settings_gpxExportShareSubject": "meshcore-open GPX-Kartendaten exportieren",
|
"settings_gpxExportShareSubject": "GPX-Kartendaten aus meshcore-open exportieren",
|
||||||
"settings_gpxExportShareText": "Kartendaten aus meshcore-open exportiert",
|
"settings_gpxExportShareText": "GPX-Kartendaten aus meshcore-open exportiert",
|
||||||
"pathTrace_someHopsNoLocation": "Eine oder mehrere der Hopfen fehlen einen Standort!"
|
"pathTrace_someHopsNoLocation": "Bei einer oder mehreren Knoten fehlt der Standort!"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -619,6 +619,7 @@
|
|||||||
"map_sharedPin": "Shared pin",
|
"map_sharedPin": "Shared pin",
|
||||||
"map_joinRoom": "Join Room",
|
"map_joinRoom": "Join Room",
|
||||||
"map_manageRepeater": "Manage Repeater",
|
"map_manageRepeater": "Manage Repeater",
|
||||||
|
"map_updateMyLocation": "Update Location",
|
||||||
"mapCache_title": "Offline Map Cache",
|
"mapCache_title": "Offline Map Cache",
|
||||||
"mapCache_selectAreaFirst": "Select an area to cache first",
|
"mapCache_selectAreaFirst": "Select an area to cache first",
|
||||||
"mapCache_noTilesToDownload": "No tiles to download for this area",
|
"mapCache_noTilesToDownload": "No tiles to download for this area",
|
||||||
|
|||||||
+35
-37
@@ -210,8 +210,8 @@
|
|||||||
"appSettings_batteryLifepo4": "LiFePO4 (2,6-3,65V)",
|
"appSettings_batteryLifepo4": "LiFePO4 (2,6-3,65V)",
|
||||||
"appSettings_batteryLipo": "LiPo (3,0-4,2V)",
|
"appSettings_batteryLipo": "LiPo (3,0-4,2V)",
|
||||||
"appSettings_mapDisplay": "Affichage de la carte",
|
"appSettings_mapDisplay": "Affichage de la carte",
|
||||||
"appSettings_showRepeaters": "Afficher les répétiteurs",
|
"appSettings_showRepeaters": "Afficher les répéteurs",
|
||||||
"appSettings_showRepeatersSubtitle": "Afficher les nœuds répétiteurs sur la carte",
|
"appSettings_showRepeatersSubtitle": "Afficher les nœuds répéteurs sur la carte",
|
||||||
"appSettings_showChatNodes": "Afficher les nœuds de discussion",
|
"appSettings_showChatNodes": "Afficher les nœuds de discussion",
|
||||||
"appSettings_showChatNodesSubtitle": "Afficher les nœuds de chat sur la carte",
|
"appSettings_showChatNodesSubtitle": "Afficher les nœuds de chat sur la carte",
|
||||||
"appSettings_showOtherNodes": "Afficher d'autres nœuds",
|
"appSettings_showOtherNodes": "Afficher d'autres nœuds",
|
||||||
@@ -266,7 +266,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"contacts_manageRepeater": "Gérer le répétiteur",
|
"contacts_manageRepeater": "Gérer le répéteur",
|
||||||
"contacts_roomLogin": "Connexion Salle",
|
"contacts_roomLogin": "Connexion Salle",
|
||||||
"contacts_openChat": "Ouverture du Chat",
|
"contacts_openChat": "Ouverture du Chat",
|
||||||
"contacts_editGroup": "Modifier le groupe",
|
"contacts_editGroup": "Modifier le groupe",
|
||||||
@@ -542,9 +542,9 @@
|
|||||||
"chat_forceFloodMode": "Mode tout le réseau forcé",
|
"chat_forceFloodMode": "Mode tout le réseau forcé",
|
||||||
"chat_recentAckPaths": "Chemins ACK récents (touchez pour utiliser) :",
|
"chat_recentAckPaths": "Chemins ACK récents (touchez pour utiliser) :",
|
||||||
"chat_pathHistoryFull": "L'historique du chemin est plein. Supprimez les entrées pour en ajouter de nouvelles.",
|
"chat_pathHistoryFull": "L'historique du chemin est plein. Supprimez les entrées pour en ajouter de nouvelles.",
|
||||||
"chat_hopSingular": "Sautez",
|
"chat_hopSingular": "saut",
|
||||||
"chat_hopPlural": "sautez",
|
"chat_hopPlural": "sauts",
|
||||||
"chat_hopsCount": "{count} {count, plural, =1{hop} other{hops}}",
|
"chat_hopsCount": "{count} {count, plural, =1{saut} other{sauts}}",
|
||||||
"@chat_hopsCount": {
|
"@chat_hopsCount": {
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
"count": {
|
"count": {
|
||||||
@@ -636,7 +636,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"map_chat": "Chat",
|
"map_chat": "Chat",
|
||||||
"map_repeater": "Répétiteur",
|
"map_repeater": "Répéteur",
|
||||||
"map_room": "Salle",
|
"map_room": "Salle",
|
||||||
"map_sensor": "Capteur",
|
"map_sensor": "Capteur",
|
||||||
"map_pinDm": "Clé (DM)",
|
"map_pinDm": "Clé (DM)",
|
||||||
@@ -677,7 +677,7 @@
|
|||||||
"map_lastSeenTime": "Dernière fois vu",
|
"map_lastSeenTime": "Dernière fois vu",
|
||||||
"map_sharedPin": "Clé partagée",
|
"map_sharedPin": "Clé partagée",
|
||||||
"map_joinRoom": "Rejoindre la salle",
|
"map_joinRoom": "Rejoindre la salle",
|
||||||
"map_manageRepeater": "Gérer le répétiteur",
|
"map_manageRepeater": "Gérer le répéteur",
|
||||||
"mapCache_title": "Cache de Carte Hors Ligne",
|
"mapCache_title": "Cache de Carte Hors Ligne",
|
||||||
"mapCache_selectAreaFirst": "Sélectionner une zone pour la mise en cache en premier",
|
"mapCache_selectAreaFirst": "Sélectionner une zone pour la mise en cache en premier",
|
||||||
"mapCache_noTilesToDownload": "Aucun tuilage à télécharger pour cette zone.",
|
"mapCache_noTilesToDownload": "Aucun tuilage à télécharger pour cette zone.",
|
||||||
@@ -800,13 +800,13 @@
|
|||||||
"time_allTime": "Tout le temps",
|
"time_allTime": "Tout le temps",
|
||||||
"dialog_disconnect": "Déconnecter",
|
"dialog_disconnect": "Déconnecter",
|
||||||
"dialog_disconnectConfirm": "Êtes-vous sûr de vouloir vous déconnecter de cet appareil ?",
|
"dialog_disconnectConfirm": "Êtes-vous sûr de vouloir vous déconnecter de cet appareil ?",
|
||||||
"login_repeaterLogin": "Connexion au répétiteur",
|
"login_repeaterLogin": "Connexion au répéteur",
|
||||||
"login_roomLogin": "Connexion Salle",
|
"login_roomLogin": "Connexion Salle",
|
||||||
"login_password": "Mot de passe",
|
"login_password": "Mot de passe",
|
||||||
"login_enterPassword": "Entrez votre mot de passe",
|
"login_enterPassword": "Entrez votre mot de passe",
|
||||||
"login_savePassword": "Sauvegarder le mot de passe",
|
"login_savePassword": "Sauvegarder le mot de passe",
|
||||||
"login_savePasswordSubtitle": "Le mot de passe sera stocké en toute sécurité sur cet appareil.",
|
"login_savePasswordSubtitle": "Le mot de passe sera stocké en toute sécurité sur cet appareil.",
|
||||||
"login_repeaterDescription": "Entrez le mot de passe du répétiteur pour accéder aux paramètres et à l'état.",
|
"login_repeaterDescription": "Entrez le mot de passe du répéteur pour accéder aux paramètres et à l'état.",
|
||||||
"login_roomDescription": "Entrez le mot de passe de la pièce pour accéder aux paramètres et à l'état.",
|
"login_roomDescription": "Entrez le mot de passe de la pièce pour accéder aux paramètres et à l'état.",
|
||||||
"login_routing": "Redirection",
|
"login_routing": "Redirection",
|
||||||
"login_routingMode": "Mode de routage",
|
"login_routingMode": "Mode de routage",
|
||||||
@@ -871,17 +871,17 @@
|
|||||||
},
|
},
|
||||||
"path_tooLong": "Le chemin est trop long. Maximum 64 sauts autorisés.",
|
"path_tooLong": "Le chemin est trop long. Maximum 64 sauts autorisés.",
|
||||||
"path_setPath": "Définir le chemin",
|
"path_setPath": "Définir le chemin",
|
||||||
"repeater_management": "Gestion des répétiteurs",
|
"repeater_management": "Gestion des répéteurs",
|
||||||
"repeater_managementTools": "Outils de Gestion",
|
"repeater_managementTools": "Outils de Gestion",
|
||||||
"repeater_status": "État",
|
"repeater_status": "État",
|
||||||
"repeater_statusSubtitle": "Afficher l'état, les statistiques et les voisins du répétiteur",
|
"repeater_statusSubtitle": "Afficher l'état, les statistiques et les voisins du répéteur",
|
||||||
"repeater_telemetry": "Télémetrie",
|
"repeater_telemetry": "Télémetrie",
|
||||||
"repeater_telemetrySubtitle": "Afficher la télémétrie des capteurs et les statistiques du système",
|
"repeater_telemetrySubtitle": "Afficher la télémétrie des capteurs et les statistiques du système",
|
||||||
"repeater_cli": "CLI",
|
"repeater_cli": "CLI",
|
||||||
"repeater_cliSubtitle": "Envoyer des commandes au répétiteur",
|
"repeater_cliSubtitle": "Envoyer des commandes au répéteur",
|
||||||
"repeater_settings": "Paramètres",
|
"repeater_settings": "Paramètres",
|
||||||
"repeater_settingsSubtitle": "Configurer les paramètres du répétiteur",
|
"repeater_settingsSubtitle": "Configurer les paramètres du répéteur",
|
||||||
"repeater_statusTitle": "État du répétiteur",
|
"repeater_statusTitle": "État du répéteur",
|
||||||
"repeater_routingMode": "Mode de routage",
|
"repeater_routingMode": "Mode de routage",
|
||||||
"repeater_autoUseSavedPath": "Auto (utiliser le chemin sauvegardé)",
|
"repeater_autoUseSavedPath": "Auto (utiliser le chemin sauvegardé)",
|
||||||
"repeater_forceFloodMode": "Mode tout le réseau forcé",
|
"repeater_forceFloodMode": "Mode tout le réseau forcé",
|
||||||
@@ -976,10 +976,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"repeater_settingsTitle": "Paramètres du répétiteur",
|
"repeater_settingsTitle": "Paramètres du répéteur",
|
||||||
"repeater_basicSettings": "Paramètres de base",
|
"repeater_basicSettings": "Paramètres de base",
|
||||||
"repeater_repeaterName": "Nom du répétiteur",
|
"repeater_repeaterName": "Nom du répéteur",
|
||||||
"repeater_repeaterNameHelper": "Afficher le nom de ce répétiteur",
|
"repeater_repeaterNameHelper": "Afficher le nom de ce répéteur",
|
||||||
"repeater_adminPassword": "Mot de passe Administrateur",
|
"repeater_adminPassword": "Mot de passe Administrateur",
|
||||||
"repeater_adminPasswordHelper": "Mot de passe d'accès complet",
|
"repeater_adminPasswordHelper": "Mot de passe d'accès complet",
|
||||||
"repeater_guestPassword": "Mot de passe invité",
|
"repeater_guestPassword": "Mot de passe invité",
|
||||||
@@ -999,7 +999,7 @@
|
|||||||
"repeater_longitudeHelper": "Degrés décimaux (par exemple, -122,4194)",
|
"repeater_longitudeHelper": "Degrés décimaux (par exemple, -122,4194)",
|
||||||
"repeater_features": "Fonctionnalités",
|
"repeater_features": "Fonctionnalités",
|
||||||
"repeater_packetForwarding": "Transfert de paquets",
|
"repeater_packetForwarding": "Transfert de paquets",
|
||||||
"repeater_packetForwardingSubtitle": "Activer le répétiteur pour transmettre des paquets",
|
"repeater_packetForwardingSubtitle": "Activer le répéteur pour transmettre des paquets",
|
||||||
"repeater_guestAccess": "Accès Invité",
|
"repeater_guestAccess": "Accès Invité",
|
||||||
"repeater_guestAccessSubtitle": "Autoriser l'accès invité en lecture seule",
|
"repeater_guestAccessSubtitle": "Autoriser l'accès invité en lecture seule",
|
||||||
"repeater_privacyMode": "Mode de confidentialité",
|
"repeater_privacyMode": "Mode de confidentialité",
|
||||||
@@ -1026,14 +1026,14 @@
|
|||||||
"repeater_encryptedAdvertInterval": "Intervalle d'annonces cryptées",
|
"repeater_encryptedAdvertInterval": "Intervalle d'annonces cryptées",
|
||||||
"repeater_dangerZone": "Zone dangereuse",
|
"repeater_dangerZone": "Zone dangereuse",
|
||||||
"repeater_rebootRepeater": "Redémarrer Répéteur",
|
"repeater_rebootRepeater": "Redémarrer Répéteur",
|
||||||
"repeater_rebootRepeaterSubtitle": "Réinitialiser l'appareil répétiteur",
|
"repeater_rebootRepeaterSubtitle": "Réinitialiser l'appareil répéteur",
|
||||||
"repeater_rebootRepeaterConfirm": "Êtes-vous sûr de vouloir redémarrer ce répétiteur ?",
|
"repeater_rebootRepeaterConfirm": "Êtes-vous sûr de vouloir redémarrer ce répéteur ?",
|
||||||
"repeater_regenerateIdentityKey": "Ré générer la clé d'identité",
|
"repeater_regenerateIdentityKey": "Ré générer la clé d'identité",
|
||||||
"repeater_regenerateIdentityKeySubtitle": "Générer une nouvelle paire de clés publique/privée",
|
"repeater_regenerateIdentityKeySubtitle": "Générer une nouvelle paire de clés publique/privée",
|
||||||
"repeater_regenerateIdentityKeyConfirm": "Cela générera une nouvelle identité pour le répétiteur. Continuer ?",
|
"repeater_regenerateIdentityKeyConfirm": "Cela générera une nouvelle identité pour le répéteur. Continuer ?",
|
||||||
"repeater_eraseFileSystem": "Supprimer le système de fichiers",
|
"repeater_eraseFileSystem": "Supprimer le système de fichiers",
|
||||||
"repeater_eraseFileSystemSubtitle": "Formater le système de fichiers du répétiteur",
|
"repeater_eraseFileSystemSubtitle": "Formater le système de fichiers du répéteur",
|
||||||
"repeater_eraseFileSystemConfirm": "AVERTISSEMENT : Cela effacera toutes les données du répétiteur. Cela ne peut pas être annulé !",
|
"repeater_eraseFileSystemConfirm": "AVERTISSEMENT : Cela effacera toutes les données du répéteur. Cela ne peut pas être annulé !",
|
||||||
"repeater_eraseSerialOnly": "Erase n'est disponible que via la console série.",
|
"repeater_eraseSerialOnly": "Erase n'est disponible que via la console série.",
|
||||||
"repeater_commandSent": "Commande envoyée : {command}",
|
"repeater_commandSent": "Commande envoyée : {command}",
|
||||||
"@repeater_commandSent": {
|
"@repeater_commandSent": {
|
||||||
@@ -1085,7 +1085,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"repeater_cliTitle": "Répétiteur CLI",
|
"repeater_cliTitle": "Répéteur CLI",
|
||||||
"repeater_debugNextCommand": "Déboguer Prochaine Commande",
|
"repeater_debugNextCommand": "Déboguer Prochaine Commande",
|
||||||
"repeater_commandHelp": "Aide",
|
"repeater_commandHelp": "Aide",
|
||||||
"repeater_clearHistory": "Effacer l'historique",
|
"repeater_clearHistory": "Effacer l'historique",
|
||||||
@@ -1119,13 +1119,13 @@
|
|||||||
"repeater_cliHelpClearStats": "Réinitialise divers compteurs de statistiques à zéro.",
|
"repeater_cliHelpClearStats": "Réinitialise divers compteurs de statistiques à zéro.",
|
||||||
"repeater_cliHelpSetAf": "Définit le facteur de temps d'air.",
|
"repeater_cliHelpSetAf": "Définit le facteur de temps d'air.",
|
||||||
"repeater_cliHelpSetTx": "Définit la puissance de transmission LoRa en dBm (réinitialisation requise pour appliquer).",
|
"repeater_cliHelpSetTx": "Définit la puissance de transmission LoRa en dBm (réinitialisation requise pour appliquer).",
|
||||||
"repeater_cliHelpSetRepeat": "Active ou désactive le rôle du répétiteur pour ce nœud.",
|
"repeater_cliHelpSetRepeat": "Active ou désactive le rôle du répéteur pour ce nœud.",
|
||||||
"repeater_cliHelpSetAllowReadOnly": "(Room server) Si \"activé\", alors un mot de passe vide permettra la connexion, mais ne permettra pas de publier dans la pièce. (lecture seule uniquement)",
|
"repeater_cliHelpSetAllowReadOnly": "(Room server) Si \"activé\", alors un mot de passe vide permettra la connexion, mais ne permettra pas de publier dans la pièce. (lecture seule uniquement)",
|
||||||
"repeater_cliHelpSetFloodMax": "Définit le nombre maximal de sauts pour les paquets de balayage entrants (si >= max, le paquet n'est pas acheminé).",
|
"repeater_cliHelpSetFloodMax": "Définit le nombre maximal de sauts pour les paquets de balayage entrants (si >= max, le paquet n'est pas acheminé).",
|
||||||
"repeater_cliHelpSetIntThresh": "Définit le seuil d'interférence (en dB). La valeur par défaut est de 14. Définir sur 0 désactive la détection des interférences de canal.",
|
"repeater_cliHelpSetIntThresh": "Définit le seuil d'interférence (en dB). La valeur par défaut est de 14. Définir sur 0 désactive la détection des interférences de canal.",
|
||||||
"repeater_cliHelpSetAgcResetInterval": "Définit l'intervalle pour réinitialiser le contrôleur de gain automatique. Mettez à 0 pour désactiver.",
|
"repeater_cliHelpSetAgcResetInterval": "Définit l'intervalle pour réinitialiser le contrôleur de gain automatique. Mettez à 0 pour désactiver.",
|
||||||
"repeater_cliHelpSetMultiAcks": "Active ou désactive la fonctionnalité « double ACKs ».",
|
"repeater_cliHelpSetMultiAcks": "Active ou désactive la fonctionnalité « double ACKs ».",
|
||||||
"repeater_cliHelpSetAdvertInterval": "Définit l'intervalle du minuteur pour envoyer un paquet d'annonce local (sans relais). Définir sur 0 pour désactiver.",
|
"repeater_cliHelpSetAdvertInterval": "Définit l'intervalle entre chaque émission d'une annonce locale (sans relais). Définir sur 0 pour désactiver.",
|
||||||
"repeater_cliHelpSetFloodAdvertInterval": "Définit l'intervalle du minuteur en heures pour envoyer un paquet d'annonce massive. Définir sur 0 pour désactiver.",
|
"repeater_cliHelpSetFloodAdvertInterval": "Définit l'intervalle du minuteur en heures pour envoyer un paquet d'annonce massive. Définir sur 0 pour désactiver.",
|
||||||
"repeater_cliHelpSetGuestPassword": "Définit/met à jour le mot de passe de l'invité. (pour les répéteurs, les connexions d'invités peuvent envoyer la requête \"Get Stats\")",
|
"repeater_cliHelpSetGuestPassword": "Définit/met à jour le mot de passe de l'invité. (pour les répéteurs, les connexions d'invités peuvent envoyer la requête \"Get Stats\")",
|
||||||
"repeater_cliHelpSetName": "Définit le nom de l'annonce.",
|
"repeater_cliHelpSetName": "Définit le nom de l'annonce.",
|
||||||
@@ -1147,7 +1147,7 @@
|
|||||||
"repeater_cliHelpLogStart": "Démarre l'enregistrement des paquets dans le système de fichiers.",
|
"repeater_cliHelpLogStart": "Démarre l'enregistrement des paquets dans le système de fichiers.",
|
||||||
"repeater_cliHelpLogStop": "Arrêter de journaliser les paquets vers le système de fichiers.",
|
"repeater_cliHelpLogStop": "Arrêter de journaliser les paquets vers le système de fichiers.",
|
||||||
"repeater_cliHelpLogErase": "Supprime les journaux de paquets du système de fichiers.",
|
"repeater_cliHelpLogErase": "Supprime les journaux de paquets du système de fichiers.",
|
||||||
"repeater_cliHelpNeighbors": "Affiche une liste d'autres nœuds répétiteurs entendus via des annonces sans relais. Chaque ligne est id-préfixe-hexadécimal:timestamp:snr-fois-4",
|
"repeater_cliHelpNeighbors": "Affiche une liste d'autres nœuds répéteurs entendus via des annonces sans relais. Chaque ligne est id-préfixe-hexadécimal:timestamp:snr-fois-4",
|
||||||
"repeater_cliHelpNeighborRemove": "Supprime la première entrée correspondante (par préfixe de clé publique (hexadécimal)) de la liste des voisins.",
|
"repeater_cliHelpNeighborRemove": "Supprime la première entrée correspondante (par préfixe de clé publique (hexadécimal)) de la liste des voisins.",
|
||||||
"repeater_cliHelpRegion": "(série uniquement) Liste toutes les régions définies et les autorisations actuelles d'annonces sur tout le réseau (flood).",
|
"repeater_cliHelpRegion": "(série uniquement) Liste toutes les régions définies et les autorisations actuelles d'annonces sur tout le réseau (flood).",
|
||||||
"repeater_cliHelpRegionLoad": "REMARQUE : il s'agit d'une invocation multi-commande spéciale. Chaque commande subséquente est un nom de région (indenté avec des espaces pour indiquer la hiérarchie parent, avec un minimum d'un espace). Terminé par l'envoi d'une ligne vide/commande.",
|
"repeater_cliHelpRegionLoad": "REMARQUE : il s'agit d'une invocation multi-commande spéciale. Chaque commande subséquente est un nom de région (indenté avec des espaces pour indiquer la hiérarchie parent, avec un minimum d'un espace). Terminé par l'envoi d'une ligne vide/commande.",
|
||||||
@@ -1171,8 +1171,8 @@
|
|||||||
"repeater_settingsCategory": "Paramètres",
|
"repeater_settingsCategory": "Paramètres",
|
||||||
"repeater_bridge": "Pont",
|
"repeater_bridge": "Pont",
|
||||||
"repeater_logging": "Journalisation",
|
"repeater_logging": "Journalisation",
|
||||||
"repeater_neighborsRepeaterOnly": "Voisins (Uniquement répétiteur)",
|
"repeater_neighborsRepeaterOnly": "Voisins (Uniquement répéteur)",
|
||||||
"repeater_regionManagementRepeaterOnly": "Gestion des régions (uniquement pour le répétiteur)",
|
"repeater_regionManagementRepeaterOnly": "Gestion des régions (uniquement pour le répéteur)",
|
||||||
"repeater_regionNote": "Les commandes de région ont été introduites pour gérer les définitions et les autorisations des régions.",
|
"repeater_regionNote": "Les commandes de région ont été introduites pour gérer les définitions et les autorisations des régions.",
|
||||||
"repeater_gpsManagement": "Gestion GPS",
|
"repeater_gpsManagement": "Gestion GPS",
|
||||||
"repeater_gpsNote": "La commande GPS a été introduite pour gérer les sujets liés à la localisation.",
|
"repeater_gpsNote": "La commande GPS a été introduite pour gérer les sujets liés à la localisation.",
|
||||||
@@ -1241,7 +1241,7 @@
|
|||||||
"channelPath_title": "Chemin de paquet",
|
"channelPath_title": "Chemin de paquet",
|
||||||
"channelPath_viewMap": "Afficher la carte",
|
"channelPath_viewMap": "Afficher la carte",
|
||||||
"channelPath_otherObservedPaths": "Autres chemins observés",
|
"channelPath_otherObservedPaths": "Autres chemins observés",
|
||||||
"channelPath_repeaterHops": "Sauts du répétiteur",
|
"channelPath_repeaterHops": "Sauts du répéteur",
|
||||||
"channelPath_noHopDetails": "Les détails de l'envoi ne sont pas fournis pour ce paquet.",
|
"channelPath_noHopDetails": "Les détails de l'envoi ne sont pas fournis pour ce paquet.",
|
||||||
"channelPath_messageDetails": "Détails du message",
|
"channelPath_messageDetails": "Détails du message",
|
||||||
"channelPath_senderLabel": "Expéditeur",
|
"channelPath_senderLabel": "Expéditeur",
|
||||||
@@ -1306,7 +1306,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"channelPath_mapTitle": "Carte du chemin",
|
"channelPath_mapTitle": "Carte du chemin",
|
||||||
"channelPath_noRepeaterLocations": "Aucune position de répétiteur disponible pour ce chemin.",
|
"channelPath_noRepeaterLocations": "Aucune position de répéteur disponible pour ce chemin.",
|
||||||
"channelPath_primaryPath": "Chemin {index} (Principal)",
|
"channelPath_primaryPath": "Chemin {index} (Principal)",
|
||||||
"@channelPath_primaryPath": {
|
"@channelPath_primaryPath": {
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
@@ -1558,9 +1558,9 @@
|
|||||||
"appSettings_languageRu": "Russe",
|
"appSettings_languageRu": "Russe",
|
||||||
"contacts_clipboardEmpty": "Le presse-papiers est vide.",
|
"contacts_clipboardEmpty": "Le presse-papiers est vide.",
|
||||||
"contacts_contactImported": "Le contact a été importé.",
|
"contacts_contactImported": "Le contact a été importé.",
|
||||||
"contacts_floodAdvert": "Annonce de crue",
|
"contacts_floodAdvert": "Annonce à tout le réseau",
|
||||||
"contacts_contactImportFailed": "Échec de l'importation du contact.",
|
"contacts_contactImportFailed": "Échec de l'importation du contact.",
|
||||||
"contacts_zeroHopAdvert": "Annonce Zero Hop",
|
"contacts_zeroHopAdvert": "Annonce Zero saut",
|
||||||
"contacts_copyAdvertToClipboard": "Copier l'annonce dans le presse-papiers",
|
"contacts_copyAdvertToClipboard": "Copier l'annonce dans le presse-papiers",
|
||||||
"contacts_addContactFromClipboard": "Ajouter un contact depuis le presse-papiers",
|
"contacts_addContactFromClipboard": "Ajouter un contact depuis le presse-papiers",
|
||||||
"contacts_ShareContact": "Copier le contact dans le presse-papiers",
|
"contacts_ShareContact": "Copier le contact dans le presse-papiers",
|
||||||
@@ -1575,7 +1575,6 @@
|
|||||||
"notification_newNodesCount": "{count} {count, plural, =1{nouveau nœud} other{nouveaux nœuds}}",
|
"notification_newNodesCount": "{count} {count, plural, =1{nouveau nœud} other{nouveaux nœuds}}",
|
||||||
"notification_newTypeDiscovered": "Nouveau {contactType} découvert",
|
"notification_newTypeDiscovered": "Nouveau {contactType} découvert",
|
||||||
"notification_receivedNewMessage": "Nouveau message reçu",
|
"notification_receivedNewMessage": "Nouveau message reçu",
|
||||||
"contacts_zeroHopContactAdvertFailed": "Échec de l'envoi du contact.",
|
|
||||||
"settings_gpxExportRepeaters": "Exporter les répéteurs / serveur de salle au format GPX",
|
"settings_gpxExportRepeaters": "Exporter les répéteurs / serveur de salle au format GPX",
|
||||||
"settings_gpxExportRepeatersSubtitle": "Exporte les répéteurs / roomserver avec une localisation vers un fichier GPX.",
|
"settings_gpxExportRepeatersSubtitle": "Exporte les répéteurs / roomserver avec une localisation vers un fichier GPX.",
|
||||||
"settings_gpxExportNoContacts": "Aucun contact à exporter.",
|
"settings_gpxExportNoContacts": "Aucun contact à exporter.",
|
||||||
@@ -1591,6 +1590,5 @@
|
|||||||
"settings_gpxExportAllContacts": "Tous les emplacements des contacts",
|
"settings_gpxExportAllContacts": "Tous les emplacements des contacts",
|
||||||
"settings_gpxExportShareText": "Données de carte exportées à partir de meshcore-open",
|
"settings_gpxExportShareText": "Données de carte exportées à partir de meshcore-open",
|
||||||
"settings_gpxExportShareSubject": "meshcore-open exporter les données de carte GPX",
|
"settings_gpxExportShareSubject": "meshcore-open exporter les données de carte GPX",
|
||||||
"pathTrace_someHopsNoLocation": "Une ou plusieurs des houblons manquent d'une localisation !"
|
"pathTrace_someHopsNoLocation": "Un ou plusieurs des sauts manquent d'une localisation !"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -244,10 +244,10 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
String get settings_actions => 'Aktionen';
|
String get settings_actions => 'Aktionen';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_sendAdvertisement => 'Sende eine Ankündigung';
|
String get settings_sendAdvertisement => 'Sende Ankündigung';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_sendAdvertisementSubtitle => 'Sende Ankündigung';
|
String get settings_sendAdvertisementSubtitle => 'Sende eine Ankündigung';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_advertisementSent => 'Ankündigung gesendet';
|
String get settings_advertisementSent => 'Ankündigung gesendet';
|
||||||
@@ -267,7 +267,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_refreshContactsSubtitle =>
|
String get settings_refreshContactsSubtitle =>
|
||||||
'Kontakte-Liste vom Gerät neu laden';
|
'Kontakt-Liste vom Gerät neu laden';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_rebootDevice => 'Gerät neu starten';
|
String get settings_rebootDevice => 'Gerät neu starten';
|
||||||
@@ -1086,7 +1086,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get chat_recentAckPaths =>
|
String get chat_recentAckPaths =>
|
||||||
'Aktuelle ACK-Pfade (tasten, um zu verwenden):';
|
'Aktuelle ACK-Pfade (antippen, um zu verwenden):';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get chat_pathHistoryFull =>
|
String get chat_pathHistoryFull =>
|
||||||
@@ -1117,7 +1117,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get chat_noPathHistoryYet =>
|
String get chat_noPathHistoryYet =>
|
||||||
'Keine eine Pfadhistorie vorhanden.\nSende eine Nachricht, um Pfade zu entdecken.';
|
'Keine Pfadhistorie vorhanden.\nSende eine Nachricht, um Pfade zu entdecken.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get chat_pathActions => 'Pfadaktionen:';
|
String get chat_pathActions => 'Pfadaktionen:';
|
||||||
@@ -1418,7 +1418,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String mapCache_estimatedTiles(int count) {
|
String mapCache_estimatedTiles(int count) {
|
||||||
return 'Geschätzte Fliesen: $count';
|
return 'Geschätzte Kacheln: $count';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -1592,7 +1592,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get path_hexPrefixInstructions =>
|
String get path_hexPrefixInstructions =>
|
||||||
'Gebe für jeden Hopfen 2-stellige Hex-Präfixe ein, getrennt durch Kommas.';
|
'Gebe für jeden Zwischen-Hop das 2-stellige Hex-Präfix ein, getrennt durch Kommas.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get path_hexPrefixExample =>
|
String get path_hexPrefixExample =>
|
||||||
@@ -1689,7 +1689,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_statusRequestTimeout =>
|
String get repeater_statusRequestTimeout =>
|
||||||
'Statusanfrage zeitweise fehlgeschlagen.';
|
'Statusanfrage durch Timeout fehlgeschlagen.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String repeater_errorLoadingStatus(String error) {
|
String repeater_errorLoadingStatus(String error) {
|
||||||
@@ -1766,7 +1766,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String repeater_duplicatesFloodDirect(String flood, String direct) {
|
String repeater_duplicatesFloodDirect(String flood, String direct) {
|
||||||
return 'Überflut: $flood, Direkt: $direct';
|
return 'Flut: $flood, Direkt: $direct';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -1797,7 +1797,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_guestPasswordHelper =>
|
String get repeater_guestPasswordHelper =>
|
||||||
'Schreibgeschützter Zugriffspasswort';
|
'Schreibgeschütztes Zugriffspasswort';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_radioSettings => 'Funk Einstellungen';
|
String get repeater_radioSettings => 'Funk Einstellungen';
|
||||||
@@ -1992,7 +1992,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
String get repeater_cliTitle => 'Repeater CLI';
|
String get repeater_cliTitle => 'Repeater CLI';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_debugNextCommand => 'Fehlersuche Nächster Befehl';
|
String get repeater_debugNextCommand => 'Fehlersuche des nächsten Befehls';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_commandHelp => 'Hilfe';
|
String get repeater_commandHelp => 'Hilfe';
|
||||||
@@ -2005,7 +2005,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_typeCommandOrUseQuick =>
|
String get repeater_typeCommandOrUseQuick =>
|
||||||
'Geben Sie einen Befehl unten ein oder verwenden Sie Schnellbefehle';
|
'Geben Sie unten einen Befehl ein oder verwenden Sie die Schnellbefehle';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_enterCommandHint => 'Geben Sie den Befehl ein...';
|
String get repeater_enterCommandHint => 'Geben Sie den Befehl ein...';
|
||||||
@@ -2131,7 +2131,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_cliHelpSetRxDelay =>
|
String get repeater_cliHelpSetRxDelay =>
|
||||||
'Sets (experimentell) als Basis (muss > 1 sein für den Effekt) zur Anwendung einer leichten Verzögerung bei empfangenen Paketen, basierend auf Signalstärke/Punktzahl. Auf 0 setzen, um die Funktion zu deaktivieren.';
|
'Fügt eine leichte Verzögerung bei empfangenen Paketen hinzu, basierend auf Signalstärke/Punktzahl. Auf 0 setzen, um die Funktion zu deaktivieren.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_cliHelpSetTxDelay =>
|
String get repeater_cliHelpSetTxDelay =>
|
||||||
@@ -2175,7 +2175,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_cliHelpGetBridgeType =>
|
String get repeater_cliHelpGetBridgeType =>
|
||||||
'Ruft Brückentyp none, rs232, espnow ab.';
|
'Ruft Brückentyp: none, rs232, espnow ab.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_cliHelpLogStart =>
|
String get repeater_cliHelpLogStart =>
|
||||||
@@ -2202,7 +2202,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_cliHelpRegionLoad =>
|
String get repeater_cliHelpRegionLoad =>
|
||||||
'Hinweis: Dies ist ein spezieller Mehrbefehl-Aufruf. Jeder nachfolgende Befehl ist ein Regionsname (eingedruckt mit Leerzeichen zur Angabe der übergeordneten Hierarchie, mit mindestens einem Leerzeichen). Beendet durch das Senden einer Leerzeile/des Befehls.';
|
'Hinweis: Dies ist ein spezieller Mehrbefehl-Aufruf. Jeder nachfolgende Befehl ist ein Regionsname (eingerückt mit Leerzeichen zur Angabe der übergeordneten Hierarchie, mit mindestens einem Leerzeichen). Beendet durch das Senden einer Leerzeile.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_cliHelpRegionGet =>
|
String get repeater_cliHelpRegionGet =>
|
||||||
@@ -2351,10 +2351,11 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get neighbors_receivedData => 'Empfangene Nachbarendaten';
|
String get neighbors_receivedData => 'Empfangene Nachbarsdaten';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get neighbors_requestTimedOut => 'Nachbarn melden zeitweise Ausfall.';
|
String get neighbors_requestTimedOut =>
|
||||||
|
'Anfrage durch Timeout fehlgeschlagen.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String neighbors_errorLoading(String error) {
|
String neighbors_errorLoading(String error) {
|
||||||
@@ -2365,16 +2366,16 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
String get neighbors_repeatersNeighbours => 'Nachbarn';
|
String get neighbors_repeatersNeighbours => 'Nachbarn';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get neighbors_noData => 'Keine Nachbardaten verfügbar.';
|
String get neighbors_noData => 'Keine Nachbarsdaten verfügbar.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String neighbors_unknownContact(String pubkey) {
|
String neighbors_unknownContact(String pubkey) {
|
||||||
return 'Unbekannte $pubkey';
|
return 'Unbekannt $pubkey';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String neighbors_heardAgo(String time) {
|
String neighbors_heardAgo(String time) {
|
||||||
return 'Hörte: $time vor her.';
|
return 'Gehört vor: $time';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -2394,7 +2395,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
'Die Detailangaben für dieses Paket sind nicht verfügbar.';
|
'Die Detailangaben für dieses Paket sind nicht verfügbar.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get channelPath_messageDetails => 'Nachrichtsdetails';
|
String get channelPath_messageDetails => 'Nachrichtendetails';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get channelPath_senderLabel => 'Sender';
|
String get channelPath_senderLabel => 'Sender';
|
||||||
@@ -2630,14 +2631,14 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
'Füge einen Hashtag-Kanal für diese Community hinzu';
|
'Füge einen Hashtag-Kanal für diese Community hinzu';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get community_selectCommunity => 'Wählen Sie Community';
|
String get community_selectCommunity => 'Wählen Sie eine Community';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get community_regularHashtag => 'Regulärer Hashtag';
|
String get community_regularHashtag => 'Regulärer Hashtag';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get community_regularHashtagDesc =>
|
String get community_regularHashtagDesc =>
|
||||||
'Öffentliches Hashtag (jeder kann teilnehmen)';
|
'Öffentlicher Hashtag (jeder kann teilnehmen)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get community_communityHashtag => 'Community Hashtag';
|
String get community_communityHashtag => 'Community Hashtag';
|
||||||
@@ -2682,7 +2683,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
String get listFilter_roomServers => 'Raumserver';
|
String get listFilter_roomServers => 'Raumserver';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get listFilter_unreadOnly => 'Nur nicht gelesen';
|
String get listFilter_unreadOnly => 'Nicht gelesen';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get listFilter_newGroup => 'Neue Gruppe';
|
String get listFilter_newGroup => 'Neue Gruppe';
|
||||||
@@ -2701,7 +2702,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get pathTrace_someHopsNoLocation =>
|
String get pathTrace_someHopsNoLocation =>
|
||||||
'Eine oder mehrere der Hopfen fehlen einen Standort!';
|
'Bei einer oder mehreren Knoten fehlt der Standort!';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contacts_pathTrace => 'Pfadverfolgung';
|
String get contacts_pathTrace => 'Pfadverfolgung';
|
||||||
@@ -2743,14 +2744,14 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
'Kontakt konnte nicht importiert werden';
|
'Kontakt konnte nicht importiert werden';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contacts_zeroHopAdvert => 'Zero-Hop-Anzeige';
|
String get contacts_zeroHopAdvert => 'Zero-Hop-Ankündigung';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contacts_floodAdvert => 'Überflutungsanzeige';
|
String get contacts_floodAdvert => 'Flut-Ankündigung';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contacts_copyAdvertToClipboard =>
|
String get contacts_copyAdvertToClipboard =>
|
||||||
'Werbung in die Zwischenablage kopieren';
|
'Ankündigung in die Zwischenablage kopieren';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contacts_addContactFromClipboard =>
|
String get contacts_addContactFromClipboard =>
|
||||||
@@ -2776,7 +2777,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get contacts_contactAdvertCopyFailed =>
|
String get contacts_contactAdvertCopyFailed =>
|
||||||
'Kopieren des Werbeinhalts in die Zwischenablage fehlgeschlagen.';
|
'Kopieren der Ankündigung in die Zwischenablage fehlgeschlagen.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get notification_activityTitle => 'MeshCore Aktivität';
|
String get notification_activityTitle => 'MeshCore Aktivität';
|
||||||
@@ -2824,28 +2825,28 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_gpxExportRepeaters =>
|
String get settings_gpxExportRepeaters =>
|
||||||
'Repeater und Raumserver nach GPX exportieren';
|
'Repeater und Raumserver als GPX exportieren';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_gpxExportRepeatersSubtitle =>
|
String get settings_gpxExportRepeatersSubtitle =>
|
||||||
'Exportiert Repeater und Raumserver mit einem Standort in eine GPX-Datei.';
|
'Exportiert Repeater und Raumserver mit einem Standort in eine GPX-Datei.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_gpxExportContacts => 'Begleiter nach GPX exportieren';
|
String get settings_gpxExportContacts => 'Kontakte als GPX exportieren';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_gpxExportContactsSubtitle =>
|
String get settings_gpxExportContactsSubtitle =>
|
||||||
'Exportiert Begleiter mit einem Ort in eine GPX-Datei.';
|
'Exportiert Kontakte mit einem Ort in eine GPX-Datei.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_gpxExportAll => 'Alle Kontakte nach GPX exportieren';
|
String get settings_gpxExportAll => 'Alle Knoten als GPX exportieren';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_gpxExportAllSubtitle =>
|
String get settings_gpxExportAllSubtitle =>
|
||||||
'Exportiert alle Kontakte mit einem Standort in eine GPX-Datei.';
|
'Exportiert alle Knoten mit einem Standort in eine GPX-Datei.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_gpxExportSuccess => 'Erfolgreich GPX-Datei exportiert.';
|
String get settings_gpxExportSuccess => 'GPX-Datei erfolgreich exportiert.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_gpxExportNoContacts => 'Keine Kontakte zum Exportieren.';
|
String get settings_gpxExportNoContacts => 'Keine Kontakte zum Exportieren.';
|
||||||
@@ -2863,16 +2864,16 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
'Repeater- und Raumserver-Standorte';
|
'Repeater- und Raumserver-Standorte';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_gpxExportChat => 'Begleiterstandorte';
|
String get settings_gpxExportChat => 'Kontaktstandorte';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_gpxExportAllContacts => 'Alle Kontaktstandorte';
|
String get settings_gpxExportAllContacts => 'Alle Kontaktstandorte';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_gpxExportShareText =>
|
String get settings_gpxExportShareText =>
|
||||||
'Kartendaten aus meshcore-open exportiert';
|
'GPX-Kartendaten aus meshcore-open exportiert';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_gpxExportShareSubject =>
|
String get settings_gpxExportShareSubject =>
|
||||||
'meshcore-open GPX-Kartendaten exportieren';
|
'GPX-Kartendaten aus meshcore-open exportieren';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -560,11 +560,11 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
String get appSettings_mapDisplay => 'Affichage de la carte';
|
String get appSettings_mapDisplay => 'Affichage de la carte';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get appSettings_showRepeaters => 'Afficher les répétiteurs';
|
String get appSettings_showRepeaters => 'Afficher les répéteurs';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get appSettings_showRepeatersSubtitle =>
|
String get appSettings_showRepeatersSubtitle =>
|
||||||
'Afficher les nœuds répétiteurs sur la carte';
|
'Afficher les nœuds répéteurs sur la carte';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get appSettings_showChatNodes => 'Afficher les nœuds de discussion';
|
String get appSettings_showChatNodes => 'Afficher les nœuds de discussion';
|
||||||
@@ -671,7 +671,7 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contacts_manageRepeater => 'Gérer le répétiteur';
|
String get contacts_manageRepeater => 'Gérer le répéteur';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contacts_manageRoom => 'Gérer le Room Server';
|
String get contacts_manageRoom => 'Gérer le Room Server';
|
||||||
@@ -1094,18 +1094,18 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
'L\'historique du chemin est plein. Supprimez les entrées pour en ajouter de nouvelles.';
|
'L\'historique du chemin est plein. Supprimez les entrées pour en ajouter de nouvelles.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get chat_hopSingular => 'Sautez';
|
String get chat_hopSingular => 'saut';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get chat_hopPlural => 'sautez';
|
String get chat_hopPlural => 'sauts';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String chat_hopsCount(int count) {
|
String chat_hopsCount(int count) {
|
||||||
String _temp0 = intl.Intl.pluralLogic(
|
String _temp0 = intl.Intl.pluralLogic(
|
||||||
count,
|
count,
|
||||||
locale: localeName,
|
locale: localeName,
|
||||||
other: 'hops',
|
other: 'sauts',
|
||||||
one: 'hop',
|
one: 'saut',
|
||||||
);
|
);
|
||||||
return '$count $_temp0';
|
return '$count $_temp0';
|
||||||
}
|
}
|
||||||
@@ -1259,7 +1259,7 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
String get map_chat => 'Chat';
|
String get map_chat => 'Chat';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get map_repeater => 'Répétiteur';
|
String get map_repeater => 'Répéteur';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get map_room => 'Salle';
|
String get map_room => 'Salle';
|
||||||
@@ -1365,7 +1365,7 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
String get map_joinRoom => 'Rejoindre la salle';
|
String get map_joinRoom => 'Rejoindre la salle';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get map_manageRepeater => 'Gérer le répétiteur';
|
String get map_manageRepeater => 'Gérer le répéteur';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get mapCache_title => 'Cache de Carte Hors Ligne';
|
String get mapCache_title => 'Cache de Carte Hors Ligne';
|
||||||
@@ -1509,7 +1509,7 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
'Êtes-vous sûr de vouloir vous déconnecter de cet appareil ?';
|
'Êtes-vous sûr de vouloir vous déconnecter de cet appareil ?';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get login_repeaterLogin => 'Connexion au répétiteur';
|
String get login_repeaterLogin => 'Connexion au répéteur';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get login_roomLogin => 'Connexion Salle';
|
String get login_roomLogin => 'Connexion Salle';
|
||||||
@@ -1529,7 +1529,7 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get login_repeaterDescription =>
|
String get login_repeaterDescription =>
|
||||||
'Entrez le mot de passe du répétiteur pour accéder aux paramètres et à l\'état.';
|
'Entrez le mot de passe du répéteur pour accéder aux paramètres et à l\'état.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get login_roomDescription =>
|
String get login_roomDescription =>
|
||||||
@@ -1634,7 +1634,7 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
String get path_setPath => 'Définir le chemin';
|
String get path_setPath => 'Définir le chemin';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_management => 'Gestion des répétiteurs';
|
String get repeater_management => 'Gestion des répéteurs';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get room_management => 'Administración del Servidor de Habitación';
|
String get room_management => 'Administración del Servidor de Habitación';
|
||||||
@@ -1647,7 +1647,7 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_statusSubtitle =>
|
String get repeater_statusSubtitle =>
|
||||||
'Afficher l\'état, les statistiques et les voisins du répétiteur';
|
'Afficher l\'état, les statistiques et les voisins du répéteur';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_telemetry => 'Télémetrie';
|
String get repeater_telemetry => 'Télémetrie';
|
||||||
@@ -1660,7 +1660,7 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
String get repeater_cli => 'CLI';
|
String get repeater_cli => 'CLI';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_cliSubtitle => 'Envoyer des commandes au répétiteur';
|
String get repeater_cliSubtitle => 'Envoyer des commandes au répéteur';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_neighbours => 'Voisins';
|
String get repeater_neighbours => 'Voisins';
|
||||||
@@ -1674,10 +1674,10 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_settingsSubtitle =>
|
String get repeater_settingsSubtitle =>
|
||||||
'Configurer les paramètres du répétiteur';
|
'Configurer les paramètres du répéteur';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_statusTitle => 'État du répétiteur';
|
String get repeater_statusTitle => 'État du répéteur';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_routingMode => 'Mode de routage';
|
String get repeater_routingMode => 'Mode de routage';
|
||||||
@@ -1783,16 +1783,16 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_settingsTitle => 'Paramètres du répétiteur';
|
String get repeater_settingsTitle => 'Paramètres du répéteur';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_basicSettings => 'Paramètres de base';
|
String get repeater_basicSettings => 'Paramètres de base';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_repeaterName => 'Nom du répétiteur';
|
String get repeater_repeaterName => 'Nom du répéteur';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_repeaterNameHelper => 'Afficher le nom de ce répétiteur';
|
String get repeater_repeaterNameHelper => 'Afficher le nom de ce répéteur';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_adminPassword => 'Mot de passe Administrateur';
|
String get repeater_adminPassword => 'Mot de passe Administrateur';
|
||||||
@@ -1856,7 +1856,7 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_packetForwardingSubtitle =>
|
String get repeater_packetForwardingSubtitle =>
|
||||||
'Activer le répétiteur pour transmettre des paquets';
|
'Activer le répéteur pour transmettre des paquets';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_guestAccess => 'Accès Invité';
|
String get repeater_guestAccess => 'Accès Invité';
|
||||||
@@ -1905,11 +1905,11 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_rebootRepeaterSubtitle =>
|
String get repeater_rebootRepeaterSubtitle =>
|
||||||
'Réinitialiser l\'appareil répétiteur';
|
'Réinitialiser l\'appareil répéteur';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_rebootRepeaterConfirm =>
|
String get repeater_rebootRepeaterConfirm =>
|
||||||
'Êtes-vous sûr de vouloir redémarrer ce répétiteur ?';
|
'Êtes-vous sûr de vouloir redémarrer ce répéteur ?';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_regenerateIdentityKey => 'Ré générer la clé d\'identité';
|
String get repeater_regenerateIdentityKey => 'Ré générer la clé d\'identité';
|
||||||
@@ -1920,18 +1920,18 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_regenerateIdentityKeyConfirm =>
|
String get repeater_regenerateIdentityKeyConfirm =>
|
||||||
'Cela générera une nouvelle identité pour le répétiteur. Continuer ?';
|
'Cela générera une nouvelle identité pour le répéteur. Continuer ?';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_eraseFileSystem => 'Supprimer le système de fichiers';
|
String get repeater_eraseFileSystem => 'Supprimer le système de fichiers';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_eraseFileSystemSubtitle =>
|
String get repeater_eraseFileSystemSubtitle =>
|
||||||
'Formater le système de fichiers du répétiteur';
|
'Formater le système de fichiers du répéteur';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_eraseFileSystemConfirm =>
|
String get repeater_eraseFileSystemConfirm =>
|
||||||
'AVERTISSEMENT : Cela effacera toutes les données du répétiteur. Cela ne peut pas être annulé !';
|
'AVERTISSEMENT : Cela effacera toutes les données du répéteur. Cela ne peut pas être annulé !';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_eraseSerialOnly =>
|
String get repeater_eraseSerialOnly =>
|
||||||
@@ -1999,7 +1999,7 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_cliTitle => 'Répétiteur CLI';
|
String get repeater_cliTitle => 'Répéteur CLI';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_debugNextCommand => 'Déboguer Prochaine Commande';
|
String get repeater_debugNextCommand => 'Déboguer Prochaine Commande';
|
||||||
@@ -2091,7 +2091,7 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_cliHelpSetRepeat =>
|
String get repeater_cliHelpSetRepeat =>
|
||||||
'Active ou désactive le rôle du répétiteur pour ce nœud.';
|
'Active ou désactive le rôle du répéteur pour ce nœud.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_cliHelpSetAllowReadOnly =>
|
String get repeater_cliHelpSetAllowReadOnly =>
|
||||||
@@ -2115,7 +2115,7 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_cliHelpSetAdvertInterval =>
|
String get repeater_cliHelpSetAdvertInterval =>
|
||||||
'Définit l\'intervalle du minuteur pour envoyer un paquet d\'annonce local (sans relais). Définir sur 0 pour désactiver.';
|
'Définit l\'intervalle entre chaque émission d\'une annonce locale (sans relais). Définir sur 0 pour désactiver.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_cliHelpSetFloodAdvertInterval =>
|
String get repeater_cliHelpSetFloodAdvertInterval =>
|
||||||
@@ -2201,7 +2201,7 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_cliHelpNeighbors =>
|
String get repeater_cliHelpNeighbors =>
|
||||||
'Affiche une liste d\'autres nœuds répétiteurs entendus via des annonces sans relais. Chaque ligne est id-préfixe-hexadécimal:timestamp:snr-fois-4';
|
'Affiche une liste d\'autres nœuds répéteurs entendus via des annonces sans relais. Chaque ligne est id-préfixe-hexadécimal:timestamp:snr-fois-4';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_cliHelpNeighborRemove =>
|
String get repeater_cliHelpNeighborRemove =>
|
||||||
@@ -2289,12 +2289,11 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
String get repeater_logging => 'Journalisation';
|
String get repeater_logging => 'Journalisation';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_neighborsRepeaterOnly =>
|
String get repeater_neighborsRepeaterOnly => 'Voisins (Uniquement répéteur)';
|
||||||
'Voisins (Uniquement répétiteur)';
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_regionManagementRepeaterOnly =>
|
String get repeater_regionManagementRepeaterOnly =>
|
||||||
'Gestion des régions (uniquement pour le répétiteur)';
|
'Gestion des régions (uniquement pour le répéteur)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get repeater_regionNote =>
|
String get repeater_regionNote =>
|
||||||
@@ -2399,7 +2398,7 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
String get channelPath_otherObservedPaths => 'Autres chemins observés';
|
String get channelPath_otherObservedPaths => 'Autres chemins observés';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get channelPath_repeaterHops => 'Sauts du répétiteur';
|
String get channelPath_repeaterHops => 'Sauts du répéteur';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get channelPath_noHopDetails =>
|
String get channelPath_noHopDetails =>
|
||||||
@@ -2467,7 +2466,7 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get channelPath_noRepeaterLocations =>
|
String get channelPath_noRepeaterLocations =>
|
||||||
'Aucune position de répétiteur disponible pour ce chemin.';
|
'Aucune position de répéteur disponible pour ce chemin.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String channelPath_primaryPath(int index) {
|
String channelPath_primaryPath(int index) {
|
||||||
@@ -2713,7 +2712,7 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get pathTrace_someHopsNoLocation =>
|
String get pathTrace_someHopsNoLocation =>
|
||||||
'Une ou plusieurs des houblons manquent d\'une localisation !';
|
'Un ou plusieurs des sauts manquent d\'une localisation !';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contacts_pathTrace => 'Traçage de chemin';
|
String get contacts_pathTrace => 'Traçage de chemin';
|
||||||
@@ -2756,10 +2755,10 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
'Échec de l\'importation du contact.';
|
'Échec de l\'importation du contact.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contacts_zeroHopAdvert => 'Annonce Zero Hop';
|
String get contacts_zeroHopAdvert => 'Annonce Zero saut';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contacts_floodAdvert => 'Annonce de crue';
|
String get contacts_floodAdvert => 'Annonce à tout le réseau';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contacts_copyAdvertToClipboard =>
|
String get contacts_copyAdvertToClipboard =>
|
||||||
|
|||||||
+6
-1
@@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
|
import 'package:meshcore_open/services/sparse_location_logger.dart';
|
||||||
import 'l10n/app_localizations.dart';
|
import 'l10n/app_localizations.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
@@ -33,7 +34,7 @@ void main() async {
|
|||||||
final appDebugLogService = AppDebugLogService();
|
final appDebugLogService = AppDebugLogService();
|
||||||
final backgroundService = BackgroundService();
|
final backgroundService = BackgroundService();
|
||||||
final mapTileCacheService = MapTileCacheService();
|
final mapTileCacheService = MapTileCacheService();
|
||||||
|
final sparseLocationLogger = SparseLocationLogger();
|
||||||
// Load settings
|
// Load settings
|
||||||
await appSettingsService.loadSettings();
|
await appSettingsService.loadSettings();
|
||||||
|
|
||||||
@@ -56,6 +57,7 @@ void main() async {
|
|||||||
bleDebugLogService: bleDebugLogService,
|
bleDebugLogService: bleDebugLogService,
|
||||||
appDebugLogService: appDebugLogService,
|
appDebugLogService: appDebugLogService,
|
||||||
backgroundService: backgroundService,
|
backgroundService: backgroundService,
|
||||||
|
sparseLocationLogger: sparseLocationLogger,
|
||||||
);
|
);
|
||||||
|
|
||||||
await connector.loadContactCache();
|
await connector.loadContactCache();
|
||||||
@@ -76,6 +78,7 @@ void main() async {
|
|||||||
bleDebugLogService: bleDebugLogService,
|
bleDebugLogService: bleDebugLogService,
|
||||||
appDebugLogService: appDebugLogService,
|
appDebugLogService: appDebugLogService,
|
||||||
mapTileCacheService: mapTileCacheService,
|
mapTileCacheService: mapTileCacheService,
|
||||||
|
sparseLocationLogger: sparseLocationLogger,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -89,6 +92,7 @@ class MeshCoreApp extends StatelessWidget {
|
|||||||
final BleDebugLogService bleDebugLogService;
|
final BleDebugLogService bleDebugLogService;
|
||||||
final AppDebugLogService appDebugLogService;
|
final AppDebugLogService appDebugLogService;
|
||||||
final MapTileCacheService mapTileCacheService;
|
final MapTileCacheService mapTileCacheService;
|
||||||
|
final SparseLocationLogger sparseLocationLogger;
|
||||||
|
|
||||||
const MeshCoreApp({
|
const MeshCoreApp({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -100,6 +104,7 @@ class MeshCoreApp extends StatelessWidget {
|
|||||||
required this.bleDebugLogService,
|
required this.bleDebugLogService,
|
||||||
required this.appDebugLogService,
|
required this.appDebugLogService,
|
||||||
required this.mapTileCacheService,
|
required this.mapTileCacheService,
|
||||||
|
required this.sparseLocationLogger,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -384,6 +384,7 @@ class AppSettingsScreen extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fixed rendering issues
|
||||||
Widget _buildBatteryCard(
|
Widget _buildBatteryCard(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
AppSettingsService settingsService,
|
AppSettingsService settingsService,
|
||||||
@@ -399,6 +400,7 @@ class AppSettingsScreen extends StatelessWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
|
const SizedBox(height: 4),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(16, 16, 16, 8),
|
padding: const EdgeInsets.fromLTRB(16, 16, 16, 8),
|
||||||
child: Text(
|
child: Text(
|
||||||
@@ -406,6 +408,8 @@ class AppSettingsScreen extends StatelessWidget {
|
|||||||
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
// Main tile (icon + text only)
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.battery_full),
|
leading: const Icon(Icons.battery_full),
|
||||||
title: Text(context.l10n.appSettings_batteryChemistry),
|
title: Text(context.l10n.appSettings_batteryChemistry),
|
||||||
@@ -416,8 +420,19 @@ class AppSettingsScreen extends StatelessWidget {
|
|||||||
)
|
)
|
||||||
: context.l10n.appSettings_batteryChemistryConnectFirst,
|
: context.l10n.appSettings_batteryChemistryConnectFirst,
|
||||||
),
|
),
|
||||||
trailing: DropdownButton<String>(
|
contentPadding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
value: selection,
|
),
|
||||||
|
|
||||||
|
// Dropdown (separate full-width row)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
|
||||||
|
child: DropdownButtonFormField<String>(
|
||||||
|
initialValue: selection,
|
||||||
|
isExpanded: true,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
border: UnderlineInputBorder(),
|
||||||
|
isDense: true,
|
||||||
|
),
|
||||||
onChanged: isConnected
|
onChanged: isConnected
|
||||||
? (value) {
|
? (value) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
|
|||||||
+106
-52
@@ -51,6 +51,7 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
bool _isSelectingPoi = false;
|
bool _isSelectingPoi = false;
|
||||||
bool _hasInitializedMap = false;
|
bool _hasInitializedMap = false;
|
||||||
bool _removedMarkersLoaded = false;
|
bool _removedMarkersLoaded = false;
|
||||||
|
bool _legendExpanded = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -274,6 +275,17 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
PopupMenuItem(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.refresh),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(context.l10n.map_updateMyLocation),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
onTap: () =>
|
||||||
|
connector.sparseLocationLogger?.updateMyLocation(),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
icon: const Icon(Icons.more_vert),
|
icon: const Icon(Icons.more_vert),
|
||||||
),
|
),
|
||||||
@@ -356,8 +368,8 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
connector.selfLatitude!,
|
connector.selfLatitude!,
|
||||||
connector.selfLongitude!,
|
connector.selfLongitude!,
|
||||||
),
|
),
|
||||||
width: 35,
|
width: 40,
|
||||||
height: 35,
|
height: 40,
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.all(4),
|
padding: const EdgeInsets.all(4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
@@ -503,60 +515,102 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
top: 16,
|
top: 16,
|
||||||
right: 16,
|
right: 16,
|
||||||
child: Card(
|
child: Card(
|
||||||
child: Padding(
|
child: Column(
|
||||||
padding: const EdgeInsets.all(12.0),
|
mainAxisSize: MainAxisSize.min,
|
||||||
child: Column(
|
children: [
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
InkWell(
|
||||||
mainAxisSize: MainAxisSize.min,
|
borderRadius: BorderRadius.circular(12),
|
||||||
children: [
|
onTap: () {
|
||||||
Text(
|
setState(() {
|
||||||
context.l10n.map_nodesCount(nodeCount),
|
_legendExpanded = !_legendExpanded;
|
||||||
style: const TextStyle(
|
});
|
||||||
fontWeight: FontWeight.bold,
|
},
|
||||||
fontSize: 14,
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(12, 10, 12, 10),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
context.l10n.map_nodesCount(nodeCount),
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
context.l10n.map_pinsCount(markerCount),
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
AnimatedRotation(
|
||||||
|
turns: _legendExpanded ? 0.5 : 0,
|
||||||
|
duration: const Duration(milliseconds: 200),
|
||||||
|
child: const Icon(Icons.expand_more, size: 20),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
),
|
||||||
context.l10n.map_pinsCount(markerCount),
|
AnimatedCrossFade(
|
||||||
style: const TextStyle(
|
firstChild: const SizedBox.shrink(),
|
||||||
fontWeight: FontWeight.bold,
|
secondChild: Padding(
|
||||||
fontSize: 12,
|
padding: const EdgeInsets.fromLTRB(12, 0, 12, 12),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 6),
|
||||||
|
_buildLegendItem(
|
||||||
|
Icons.person,
|
||||||
|
context.l10n.map_chat,
|
||||||
|
Colors.blue,
|
||||||
|
),
|
||||||
|
_buildLegendItem(
|
||||||
|
Icons.router,
|
||||||
|
context.l10n.map_repeater,
|
||||||
|
Colors.green,
|
||||||
|
),
|
||||||
|
_buildLegendItem(
|
||||||
|
Icons.meeting_room,
|
||||||
|
context.l10n.map_room,
|
||||||
|
Colors.purple,
|
||||||
|
),
|
||||||
|
_buildLegendItem(
|
||||||
|
Icons.sensors,
|
||||||
|
context.l10n.map_sensor,
|
||||||
|
Colors.orange,
|
||||||
|
),
|
||||||
|
_buildLegendItem(
|
||||||
|
Icons.flag,
|
||||||
|
context.l10n.map_pinDm,
|
||||||
|
Colors.blue,
|
||||||
|
),
|
||||||
|
_buildLegendItem(
|
||||||
|
Icons.flag,
|
||||||
|
context.l10n.map_pinPrivate,
|
||||||
|
Colors.purple,
|
||||||
|
),
|
||||||
|
_buildLegendItem(
|
||||||
|
Icons.flag,
|
||||||
|
context.l10n.map_pinPublic,
|
||||||
|
Colors.orange,
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
crossFadeState: _legendExpanded
|
||||||
_buildLegendItem(
|
? CrossFadeState.showSecond
|
||||||
Icons.person,
|
: CrossFadeState.showFirst,
|
||||||
context.l10n.map_chat,
|
duration: const Duration(milliseconds: 200),
|
||||||
Colors.blue,
|
),
|
||||||
),
|
],
|
||||||
_buildLegendItem(
|
|
||||||
Icons.router,
|
|
||||||
context.l10n.map_repeater,
|
|
||||||
Colors.green,
|
|
||||||
),
|
|
||||||
_buildLegendItem(
|
|
||||||
Icons.meeting_room,
|
|
||||||
context.l10n.map_room,
|
|
||||||
Colors.purple,
|
|
||||||
),
|
|
||||||
_buildLegendItem(
|
|
||||||
Icons.sensors,
|
|
||||||
context.l10n.map_sensor,
|
|
||||||
Colors.orange,
|
|
||||||
),
|
|
||||||
_buildLegendItem(Icons.flag, context.l10n.map_pinDm, Colors.blue),
|
|
||||||
_buildLegendItem(
|
|
||||||
Icons.flag,
|
|
||||||
context.l10n.map_pinPrivate,
|
|
||||||
Colors.purple,
|
|
||||||
),
|
|
||||||
_buildLegendItem(
|
|
||||||
Icons.flag,
|
|
||||||
context.l10n.map_pinPublic,
|
|
||||||
Colors.orange,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ class SettingsScreen extends StatefulWidget {
|
|||||||
|
|
||||||
class _SettingsScreenState extends State<SettingsScreen> {
|
class _SettingsScreenState extends State<SettingsScreen> {
|
||||||
bool _showBatteryVoltage = false;
|
bool _showBatteryVoltage = false;
|
||||||
|
bool _deviceInfoExpanded = false;
|
||||||
String _appVersion = '';
|
String _appVersion = '';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -74,43 +75,84 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
MeshCoreConnector connector,
|
MeshCoreConnector connector,
|
||||||
) {
|
) {
|
||||||
final l10n = context.l10n;
|
final l10n = context.l10n;
|
||||||
|
|
||||||
return Card(
|
return Card(
|
||||||
child: Padding(
|
child: Column(
|
||||||
padding: const EdgeInsets.all(16),
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
child: Column(
|
children: [
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
InkWell(
|
||||||
children: [
|
borderRadius: BorderRadius.circular(12),
|
||||||
Text(
|
onTap: () {
|
||||||
l10n.settings_deviceInfo,
|
setState(() {
|
||||||
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
_deviceInfoExpanded = !_deviceInfoExpanded;
|
||||||
),
|
});
|
||||||
const SizedBox(height: 16),
|
},
|
||||||
_buildInfoRow(l10n.settings_infoName, connector.deviceDisplayName),
|
child: Padding(
|
||||||
_buildInfoRow(l10n.settings_infoId, connector.deviceIdLabel),
|
padding: const EdgeInsets.fromLTRB(16, 16, 16, 16),
|
||||||
_buildInfoRow(
|
child: Row(
|
||||||
l10n.settings_infoStatus,
|
children: [
|
||||||
connector.isConnected
|
Expanded(
|
||||||
? l10n.common_connected
|
child: Text(
|
||||||
: l10n.common_disconnected,
|
l10n.settings_deviceInfo,
|
||||||
),
|
style: const TextStyle(
|
||||||
_buildBatteryInfoRow(context, connector),
|
fontSize: 18,
|
||||||
if (connector.selfName != null)
|
fontWeight: FontWeight.bold,
|
||||||
_buildInfoRow(l10n.settings_nodeName, connector.selfName!),
|
),
|
||||||
if (connector.selfPublicKey != null)
|
),
|
||||||
_buildInfoRow(
|
),
|
||||||
l10n.settings_infoPublicKey,
|
AnimatedRotation(
|
||||||
'${pubKeyToHex(connector.selfPublicKey!).substring(0, 16)}...',
|
turns: _deviceInfoExpanded ? 0.5 : 0,
|
||||||
|
duration: const Duration(milliseconds: 200),
|
||||||
|
child: const Icon(Icons.expand_more),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
_buildInfoRow(
|
|
||||||
l10n.settings_infoContactsCount,
|
|
||||||
'${connector.contacts.length}',
|
|
||||||
),
|
),
|
||||||
_buildInfoRow(
|
),
|
||||||
l10n.settings_infoChannelCount,
|
|
||||||
'${connector.channels.length}',
|
AnimatedCrossFade(
|
||||||
|
firstChild: const SizedBox.shrink(),
|
||||||
|
secondChild: Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
_buildInfoRow(
|
||||||
|
l10n.settings_infoName,
|
||||||
|
connector.deviceDisplayName,
|
||||||
|
),
|
||||||
|
_buildInfoRow(l10n.settings_infoId, connector.deviceIdLabel),
|
||||||
|
_buildInfoRow(
|
||||||
|
l10n.settings_infoStatus,
|
||||||
|
connector.isConnected
|
||||||
|
? l10n.common_connected
|
||||||
|
: l10n.common_disconnected,
|
||||||
|
),
|
||||||
|
_buildBatteryInfoRow(context, connector),
|
||||||
|
if (connector.selfName != null)
|
||||||
|
_buildInfoRow(l10n.settings_nodeName, connector.selfName!),
|
||||||
|
if (connector.selfPublicKey != null)
|
||||||
|
_buildInfoRow(
|
||||||
|
l10n.settings_infoPublicKey,
|
||||||
|
'${pubKeyToHex(connector.selfPublicKey!).substring(0, 16)}...',
|
||||||
|
),
|
||||||
|
_buildInfoRow(
|
||||||
|
l10n.settings_infoContactsCount,
|
||||||
|
'${connector.contacts.length}',
|
||||||
|
),
|
||||||
|
_buildInfoRow(
|
||||||
|
l10n.settings_infoChannelCount,
|
||||||
|
'${connector.channels.length}',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
crossFadeState: _deviceInfoExpanded
|
||||||
),
|
? CrossFadeState.showSecond
|
||||||
|
: CrossFadeState.showFirst,
|
||||||
|
duration: const Duration(milliseconds: 200),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -355,22 +397,33 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
Color? valueColor,
|
Color? valueColor,
|
||||||
VoidCallback? onTap,
|
VoidCallback? onTap,
|
||||||
}) {
|
}) {
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
|
||||||
final row = Padding(
|
final row = Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||||
child: Row(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
if (leading != null) ...[leading, const SizedBox(width: 8)],
|
if (leading != null) ...[leading, const SizedBox(width: 8)],
|
||||||
Text(label, style: TextStyle(color: Colors.grey[600])),
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
label,
|
||||||
|
style: theme.textTheme.bodySmall?.copyWith(
|
||||||
|
color: theme.colorScheme.onSurfaceVariant,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Flexible(
|
const SizedBox(height: 4),
|
||||||
child: Text(
|
Text(
|
||||||
value,
|
value,
|
||||||
style: TextStyle(fontWeight: FontWeight.w500, color: valueColor),
|
style: theme.textTheme.bodyLarge?.copyWith(
|
||||||
overflow: TextOverflow.ellipsis,
|
color: valueColor,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -379,11 +432,12 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
|
|
||||||
if (onTap != null) {
|
if (onTap != null) {
|
||||||
return InkWell(
|
return InkWell(
|
||||||
|
borderRadius: BorderRadius.circular(6),
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
borderRadius: BorderRadius.circular(4),
|
|
||||||
child: row,
|
child: row,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return row;
|
return row;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -436,6 +490,8 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
final latController = TextEditingController();
|
final latController = TextEditingController();
|
||||||
final lonController = TextEditingController();
|
final lonController = TextEditingController();
|
||||||
final intervalController = TextEditingController();
|
final intervalController = TextEditingController();
|
||||||
|
bool isLogging = connector.sparseLocationLogger?.isLogging() ?? false;
|
||||||
|
|
||||||
latController.text = connector.selfLatitude?.toStringAsFixed(6) ?? '';
|
latController.text = connector.selfLatitude?.toStringAsFixed(6) ?? '';
|
||||||
lonController.text = connector.selfLongitude?.toStringAsFixed(6) ?? '';
|
lonController.text = connector.selfLongitude?.toStringAsFixed(6) ?? '';
|
||||||
|
|
||||||
@@ -480,6 +536,20 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
signed: true,
|
signed: true,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
FeatureToggleRow(
|
||||||
|
title: "GPS Logging",
|
||||||
|
subtitle: "Enable GPS logging on the device",
|
||||||
|
value: isLogging,
|
||||||
|
onChanged: (value) async {
|
||||||
|
setDialogState(() => isLogging = value);
|
||||||
|
if (value) {
|
||||||
|
await connector.sparseLocationLogger?.startLogging();
|
||||||
|
} else {
|
||||||
|
await connector.sparseLocationLogger?.stopLogging();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
if (hasGPS) ...[
|
if (hasGPS) ...[
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
TextField(
|
TextField(
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ class BackgroundService {
|
|||||||
foregroundTaskOptions: const ForegroundTaskOptions(
|
foregroundTaskOptions: const ForegroundTaskOptions(
|
||||||
interval: 5000,
|
interval: 5000,
|
||||||
autoRunOnBoot: false,
|
autoRunOnBoot: false,
|
||||||
allowWakeLock: true,
|
|
||||||
allowWifiLock: false,
|
allowWifiLock: false,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,244 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'dart:math' as math;
|
||||||
|
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:geolocator/geolocator.dart';
|
||||||
|
import 'package:gpx/gpx.dart';
|
||||||
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
|
import 'package:share_plus/share_plus.dart';
|
||||||
|
|
||||||
|
class SparseLocationLogger {
|
||||||
|
static const double distanceThresholdMiles = 0.25;
|
||||||
|
static const double distanceThresholdMeters =
|
||||||
|
distanceThresholdMiles * 1609.34;
|
||||||
|
static const double headingChangeThresholdDeg = 35.0;
|
||||||
|
static const double minSpeedForTurnKmh = 8.0;
|
||||||
|
static const double minTime = 120.0; // seconds
|
||||||
|
|
||||||
|
Position? _lastLoggedPosition;
|
||||||
|
double? _lastHeading;
|
||||||
|
DateTime? _lastLoggedTime;
|
||||||
|
StreamSubscription<Position>? _positionStream;
|
||||||
|
Timer? _timer;
|
||||||
|
Function(Position position)? _onNewLogPoint;
|
||||||
|
// GPX structures
|
||||||
|
final Gpx _gpx = Gpx();
|
||||||
|
Trkseg _currentSegment = Trkseg(); // one segment for the whole session
|
||||||
|
|
||||||
|
File? _gpxFile;
|
||||||
|
|
||||||
|
bool _isInitialized = false;
|
||||||
|
|
||||||
|
void initialize(Function(Position position) onNewLogPoint) {
|
||||||
|
_onNewLogPoint = onNewLogPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> getPremissons() async {
|
||||||
|
// Permissions & service check (same as before)
|
||||||
|
var status = await Permission.location.request();
|
||||||
|
if (!status.isGranted) {
|
||||||
|
debugPrint('Location permission denied');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
|
||||||
|
if (!serviceEnabled) {
|
||||||
|
debugPrint('Location services disabled');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> startLogging() async {
|
||||||
|
if (!await getPremissons()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare files
|
||||||
|
final directory = await getApplicationDocumentsDirectory();
|
||||||
|
final timestamp = DateTime.now()
|
||||||
|
.toIso8601String()
|
||||||
|
.replaceAll(':', '-')
|
||||||
|
.split('.')
|
||||||
|
.first;
|
||||||
|
_gpxFile = File('${directory.path}/track_$timestamp.gpx');
|
||||||
|
|
||||||
|
// Init GPX metadata
|
||||||
|
_gpx.metadata = Metadata(
|
||||||
|
name: 'Sparse Track ${DateTime.now().toString().split(' ').first}',
|
||||||
|
desc: 'Sparse GPS log: ~every 1.5 mi or significant turns',
|
||||||
|
time: DateTime.now(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add one track with one segment
|
||||||
|
final track = Trk(name: 'Main Track');
|
||||||
|
_currentSegment = Trkseg();
|
||||||
|
track.trksegs.add(_currentSegment);
|
||||||
|
_gpx.trks.add(track);
|
||||||
|
|
||||||
|
_isInitialized = true;
|
||||||
|
|
||||||
|
// Start location stream
|
||||||
|
_positionStream = Geolocator.getPositionStream(
|
||||||
|
locationSettings: const LocationSettings(
|
||||||
|
accuracy: LocationAccuracy.high,
|
||||||
|
distanceFilter: 152, // meters (~0.16 mi) - helps battery
|
||||||
|
),
|
||||||
|
).listen(_onPositionReceived);
|
||||||
|
|
||||||
|
// Also poll via timer as fallback
|
||||||
|
_timer = Timer.periodic(Duration(seconds: (minTime / 2).toInt()), (
|
||||||
|
_,
|
||||||
|
) async {
|
||||||
|
final position = await Geolocator.getCurrentPosition();
|
||||||
|
await _onPositionReceived(position);
|
||||||
|
});
|
||||||
|
|
||||||
|
_lastLoggedPosition = null;
|
||||||
|
_lastHeading = null;
|
||||||
|
_lastLoggedTime = null;
|
||||||
|
|
||||||
|
debugPrint('Sparse GPX logging started → ${_gpxFile?.path}');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> stopLogging() async {
|
||||||
|
await _positionStream?.cancel();
|
||||||
|
_positionStream = null;
|
||||||
|
_timer?.cancel();
|
||||||
|
_timer = null;
|
||||||
|
|
||||||
|
if (_isInitialized && _currentSegment.trkpts.isNotEmpty) {
|
||||||
|
// Write GPX file on stop
|
||||||
|
final xmlString = GpxWriter().asString(_gpx, pretty: true);
|
||||||
|
|
||||||
|
await _gpxFile?.writeAsString(xmlString);
|
||||||
|
|
||||||
|
await SharePlus.instance.share(
|
||||||
|
ShareParams(
|
||||||
|
text: 'Sparse GPS track',
|
||||||
|
subject: 'Sparse GPS track',
|
||||||
|
files: [XFile(_gpxFile?.path ?? '')],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await _gpxFile?.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
debugPrint('Logging stopped');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> updateMyLocation() async {
|
||||||
|
if (!await getPremissons()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final position = await Geolocator.getCurrentPosition();
|
||||||
|
_onNewLogPoint?.call(position);
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('Error updating location: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onPositionReceived(Position position) async {
|
||||||
|
final now = DateTime.now();
|
||||||
|
final speedKmh = position.speed * 3.6;
|
||||||
|
final heading = position.heading;
|
||||||
|
|
||||||
|
bool shouldLog = false;
|
||||||
|
String reason = '';
|
||||||
|
|
||||||
|
if (_lastLoggedPosition == null) {
|
||||||
|
shouldLog = true;
|
||||||
|
reason = 'start';
|
||||||
|
} else {
|
||||||
|
final distanceMeters = Geolocator.distanceBetween(
|
||||||
|
_lastLoggedPosition!.latitude,
|
||||||
|
_lastLoggedPosition!.longitude,
|
||||||
|
position.latitude,
|
||||||
|
position.longitude,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (distanceMeters >= distanceThresholdMeters) {
|
||||||
|
shouldLog = true;
|
||||||
|
reason =
|
||||||
|
'distance (${(distanceMeters / 1609.34).toStringAsFixed(2)} mi)';
|
||||||
|
} else if (speedKmh > minSpeedForTurnKmh && _lastHeading != null) {
|
||||||
|
double delta = (heading - _lastHeading!).abs();
|
||||||
|
delta = math.min(delta, 360 - delta);
|
||||||
|
if (delta > headingChangeThresholdDeg) {
|
||||||
|
shouldLog = true;
|
||||||
|
reason = 'turn (${delta.toStringAsFixed(1)}°)';
|
||||||
|
}
|
||||||
|
} else if (_lastLoggedTime != null) {
|
||||||
|
final elapsed = now.difference(_lastLoggedTime!).inSeconds;
|
||||||
|
if (elapsed >= minTime && distanceMeters >= distanceThresholdMeters) {
|
||||||
|
shouldLog = true;
|
||||||
|
reason = 'time (${elapsed}s)';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldLog) {
|
||||||
|
// Create GPX Waypoint (trkpt)
|
||||||
|
final pt = Wpt(
|
||||||
|
lat: position.latitude,
|
||||||
|
lon: position.longitude,
|
||||||
|
ele: position.altitude, // if available
|
||||||
|
time: now,
|
||||||
|
extensions: {
|
||||||
|
"course": ?heading.isFinite ? heading : null,
|
||||||
|
"speed": ?speedKmh > 0 ? speedKmh / 3.6 : null, // GPX speed in m/s
|
||||||
|
},
|
||||||
|
// You can add hdop, vdop, etc. from position if desired
|
||||||
|
);
|
||||||
|
|
||||||
|
_currentSegment.trkpts.add(pt);
|
||||||
|
_onNewLogPoint?.call(position);
|
||||||
|
debugPrint('Logged point: ${pt.lat}, ${pt.lon} ($reason)');
|
||||||
|
|
||||||
|
_lastLoggedPosition = position;
|
||||||
|
_lastHeading = heading;
|
||||||
|
_lastLoggedTime = now;
|
||||||
|
} else {
|
||||||
|
debugPrint('Skipped point: ${position.latitude}, ${position.longitude}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Position snapToGridCenter({
|
||||||
|
required Position position,
|
||||||
|
required double cellSizeMeters,
|
||||||
|
}) {
|
||||||
|
Position snappedPosition = position;
|
||||||
|
// Snap latitude
|
||||||
|
final latFloor =
|
||||||
|
(position.latitude / cellSizeMeters).floor() * cellSizeMeters;
|
||||||
|
final snappedLat = latFloor + (cellSizeMeters / 2);
|
||||||
|
|
||||||
|
// Snap longitude
|
||||||
|
final lonFloor =
|
||||||
|
(position.longitude / cellSizeMeters).floor() * cellSizeMeters;
|
||||||
|
final snappedLon = lonFloor + (cellSizeMeters / 2);
|
||||||
|
|
||||||
|
snappedPosition = Position(
|
||||||
|
latitude: snappedLat,
|
||||||
|
longitude: snappedLon,
|
||||||
|
altitude: position.altitude,
|
||||||
|
accuracy: position.accuracy,
|
||||||
|
heading: position.heading,
|
||||||
|
speed: position.speed,
|
||||||
|
speedAccuracy: position.speedAccuracy,
|
||||||
|
altitudeAccuracy: position.altitudeAccuracy,
|
||||||
|
headingAccuracy: position.headingAccuracy,
|
||||||
|
timestamp: position.timestamp,
|
||||||
|
);
|
||||||
|
|
||||||
|
return snappedPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> getGpxFilePath() async => _gpxFile?.path ?? 'Not started';
|
||||||
|
bool isLogging() => _positionStream != null;
|
||||||
|
int getPointCount() => _currentSegment.trkpts.length;
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ import Foundation
|
|||||||
|
|
||||||
import flutter_blue_plus_darwin
|
import flutter_blue_plus_darwin
|
||||||
import flutter_local_notifications
|
import flutter_local_notifications
|
||||||
|
import geolocator_apple
|
||||||
import mobile_scanner
|
import mobile_scanner
|
||||||
import package_info_plus
|
import package_info_plus
|
||||||
import share_plus
|
import share_plus
|
||||||
@@ -18,6 +19,7 @@ import wakelock_plus
|
|||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
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"))
|
||||||
|
GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin"))
|
||||||
MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin"))
|
MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin"))
|
||||||
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
||||||
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
|
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
|
||||||
|
|||||||
+120
@@ -341,6 +341,70 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
geoclue:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: geoclue
|
||||||
|
sha256: c2a998c77474fc57aa00c6baa2928e58f4b267649057a1c76738656e9dbd2a7f
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.1"
|
||||||
|
geolocator:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: geolocator
|
||||||
|
sha256: "79939537046c9025be47ec645f35c8090ecadb6fe98eba146a0d25e8c1357516"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "14.0.2"
|
||||||
|
geolocator_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: geolocator_android
|
||||||
|
sha256: "179c3cb66dfa674fc9ccbf2be872a02658724d1c067634e2c427cf6df7df901a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.0.2"
|
||||||
|
geolocator_apple:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: geolocator_apple
|
||||||
|
sha256: dbdd8789d5aaf14cf69f74d4925ad1336b4433a6efdf2fce91e8955dc921bf22
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.13"
|
||||||
|
geolocator_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: geolocator_linux
|
||||||
|
sha256: c4e966f0a7a87e70049eac7a2617f9e16fd4c585a26e4330bdfc3a71e6a721f3
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.3"
|
||||||
|
geolocator_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: geolocator_platform_interface
|
||||||
|
sha256: "30cb64f0b9adcc0fb36f628b4ebf4f731a2961a0ebd849f4b56200205056fe67"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.2.6"
|
||||||
|
geolocator_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: geolocator_web
|
||||||
|
sha256: b1ae9bdfd90f861fde8fd4f209c37b953d65e92823cb73c7dee1fa021b06f172
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.1.3"
|
||||||
|
geolocator_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: geolocator_windows
|
||||||
|
sha256: "175435404d20278ffd220de83c2ca293b73db95eafbdc8131fe8609be1421eb6"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.5"
|
||||||
glob:
|
glob:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -357,6 +421,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.0"
|
version: "2.3.0"
|
||||||
|
gsettings:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: gsettings
|
||||||
|
sha256: "1b0ce661f5436d2db1e51f3c4295a49849f03d304003a7ba177d01e3a858249c"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.8"
|
||||||
hooks:
|
hooks:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -637,6 +709,54 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.0"
|
version: "2.3.0"
|
||||||
|
permission_handler:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: permission_handler
|
||||||
|
sha256: bc917da36261b00137bbc8896bf1482169cd76f866282368948f032c8c1caae1
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "12.0.1"
|
||||||
|
permission_handler_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: permission_handler_android
|
||||||
|
sha256: "1e3bc410ca1bf84662104b100eb126e066cb55791b7451307f9708d4007350e6"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "13.0.1"
|
||||||
|
permission_handler_apple:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: permission_handler_apple
|
||||||
|
sha256: f000131e755c54cf4d84a5d8bd6e4149e262cc31c5a8b1d698de1ac85fa41023
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "9.4.7"
|
||||||
|
permission_handler_html:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: permission_handler_html
|
||||||
|
sha256: "38f000e83355abb3392140f6bc3030660cfaef189e1f87824facb76300b4ff24"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.3+5"
|
||||||
|
permission_handler_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: permission_handler_platform_interface
|
||||||
|
sha256: eb99b295153abce5d683cac8c02e22faab63e50679b937fa1bf67d58bb282878
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.3.0"
|
||||||
|
permission_handler_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: permission_handler_windows
|
||||||
|
sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.1"
|
||||||
petitparser:
|
petitparser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
+3
-1
@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
|||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
# In Windows, build-name is used as the major, minor, and patch parts
|
# In Windows, build-name is used as the major, minor, and patch parts
|
||||||
# of the product and file versions while build-number is used as the build suffix.
|
# of the product and file versions while build-number is used as the build suffix.
|
||||||
version: 5.0.0+6
|
version: 6.0.0+1
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.9.2
|
sdk: ^3.9.2
|
||||||
@@ -60,6 +60,8 @@ dependencies:
|
|||||||
gpx: ^2.3.0
|
gpx: ^2.3.0
|
||||||
path_provider: ^2.1.5
|
path_provider: ^2.1.5
|
||||||
share_plus: ^12.0.1
|
share_plus: ^12.0.1
|
||||||
|
geolocator: ^14.0.2
|
||||||
|
permission_handler: ^12.0.1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|||||||
+1
-127
@@ -1,127 +1 @@
|
|||||||
{
|
{}
|
||||||
"bg": [
|
|
||||||
"notification_activityTitle",
|
|
||||||
"notification_messagesCount",
|
|
||||||
"notification_channelMessagesCount",
|
|
||||||
"notification_newNodesCount",
|
|
||||||
"notification_newTypeDiscovered",
|
|
||||||
"notification_receivedNewMessage"
|
|
||||||
],
|
|
||||||
|
|
||||||
"de": [
|
|
||||||
"notification_activityTitle",
|
|
||||||
"notification_messagesCount",
|
|
||||||
"notification_channelMessagesCount",
|
|
||||||
"notification_newNodesCount",
|
|
||||||
"notification_newTypeDiscovered",
|
|
||||||
"notification_receivedNewMessage"
|
|
||||||
],
|
|
||||||
|
|
||||||
"es": [
|
|
||||||
"notification_activityTitle",
|
|
||||||
"notification_messagesCount",
|
|
||||||
"notification_channelMessagesCount",
|
|
||||||
"notification_newNodesCount",
|
|
||||||
"notification_newTypeDiscovered",
|
|
||||||
"notification_receivedNewMessage"
|
|
||||||
],
|
|
||||||
|
|
||||||
"fr": [
|
|
||||||
"notification_activityTitle",
|
|
||||||
"notification_messagesCount",
|
|
||||||
"notification_channelMessagesCount",
|
|
||||||
"notification_newNodesCount",
|
|
||||||
"notification_newTypeDiscovered",
|
|
||||||
"notification_receivedNewMessage"
|
|
||||||
],
|
|
||||||
|
|
||||||
"it": [
|
|
||||||
"notification_activityTitle",
|
|
||||||
"notification_messagesCount",
|
|
||||||
"notification_channelMessagesCount",
|
|
||||||
"notification_newNodesCount",
|
|
||||||
"notification_newTypeDiscovered",
|
|
||||||
"notification_receivedNewMessage"
|
|
||||||
],
|
|
||||||
|
|
||||||
"nl": [
|
|
||||||
"notification_activityTitle",
|
|
||||||
"notification_messagesCount",
|
|
||||||
"notification_channelMessagesCount",
|
|
||||||
"notification_newNodesCount",
|
|
||||||
"notification_newTypeDiscovered",
|
|
||||||
"notification_receivedNewMessage"
|
|
||||||
],
|
|
||||||
|
|
||||||
"pl": [
|
|
||||||
"notification_activityTitle",
|
|
||||||
"notification_messagesCount",
|
|
||||||
"notification_channelMessagesCount",
|
|
||||||
"notification_newNodesCount",
|
|
||||||
"notification_newTypeDiscovered",
|
|
||||||
"notification_receivedNewMessage"
|
|
||||||
],
|
|
||||||
|
|
||||||
"pt": [
|
|
||||||
"notification_activityTitle",
|
|
||||||
"notification_messagesCount",
|
|
||||||
"notification_channelMessagesCount",
|
|
||||||
"notification_newNodesCount",
|
|
||||||
"notification_newTypeDiscovered",
|
|
||||||
"notification_receivedNewMessage"
|
|
||||||
],
|
|
||||||
|
|
||||||
"ru": [
|
|
||||||
"notification_activityTitle",
|
|
||||||
"notification_messagesCount",
|
|
||||||
"notification_channelMessagesCount",
|
|
||||||
"notification_newNodesCount",
|
|
||||||
"notification_newTypeDiscovered",
|
|
||||||
"notification_receivedNewMessage"
|
|
||||||
],
|
|
||||||
|
|
||||||
"sk": [
|
|
||||||
"notification_activityTitle",
|
|
||||||
"notification_messagesCount",
|
|
||||||
"notification_channelMessagesCount",
|
|
||||||
"notification_newNodesCount",
|
|
||||||
"notification_newTypeDiscovered",
|
|
||||||
"notification_receivedNewMessage"
|
|
||||||
],
|
|
||||||
|
|
||||||
"sl": [
|
|
||||||
"notification_activityTitle",
|
|
||||||
"notification_messagesCount",
|
|
||||||
"notification_channelMessagesCount",
|
|
||||||
"notification_newNodesCount",
|
|
||||||
"notification_newTypeDiscovered",
|
|
||||||
"notification_receivedNewMessage"
|
|
||||||
],
|
|
||||||
|
|
||||||
"sv": [
|
|
||||||
"notification_activityTitle",
|
|
||||||
"notification_messagesCount",
|
|
||||||
"notification_channelMessagesCount",
|
|
||||||
"notification_newNodesCount",
|
|
||||||
"notification_newTypeDiscovered",
|
|
||||||
"notification_receivedNewMessage"
|
|
||||||
],
|
|
||||||
|
|
||||||
"uk": [
|
|
||||||
"notification_activityTitle",
|
|
||||||
"notification_messagesCount",
|
|
||||||
"notification_channelMessagesCount",
|
|
||||||
"notification_newNodesCount",
|
|
||||||
"notification_newTypeDiscovered",
|
|
||||||
"notification_receivedNewMessage"
|
|
||||||
],
|
|
||||||
|
|
||||||
"zh": [
|
|
||||||
"notification_activityTitle",
|
|
||||||
"notification_messagesCount",
|
|
||||||
"notification_channelMessagesCount",
|
|
||||||
"notification_newNodesCount",
|
|
||||||
"notification_newTypeDiscovered",
|
|
||||||
"notification_receivedNewMessage"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -7,12 +7,18 @@
|
|||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
#include <flutter_blue_plus_winrt/flutter_blue_plus_plugin.h>
|
#include <flutter_blue_plus_winrt/flutter_blue_plus_plugin.h>
|
||||||
|
#include <geolocator_windows/geolocator_windows.h>
|
||||||
|
#include <permission_handler_windows/permission_handler_windows_plugin.h>
|
||||||
#include <share_plus/share_plus_windows_plugin_c_api.h>
|
#include <share_plus/share_plus_windows_plugin_c_api.h>
|
||||||
#include <url_launcher_windows/url_launcher_windows.h>
|
#include <url_launcher_windows/url_launcher_windows.h>
|
||||||
|
|
||||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
FlutterBluePlusPluginRegisterWithRegistrar(
|
FlutterBluePlusPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("FlutterBluePlusPlugin"));
|
registry->GetRegistrarForPlugin("FlutterBluePlusPlugin"));
|
||||||
|
GeolocatorWindowsRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("GeolocatorWindows"));
|
||||||
|
PermissionHandlerWindowsPluginRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
|
||||||
SharePlusWindowsPluginCApiRegisterWithRegistrar(
|
SharePlusWindowsPluginCApiRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi"));
|
registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi"));
|
||||||
UrlLauncherWindowsRegisterWithRegistrar(
|
UrlLauncherWindowsRegisterWithRegistrar(
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
flutter_blue_plus_winrt
|
flutter_blue_plus_winrt
|
||||||
|
geolocator_windows
|
||||||
|
permission_handler_windows
|
||||||
share_plus
|
share_plus
|
||||||
url_launcher_windows
|
url_launcher_windows
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user