Merge pull request #251 from zjs81/dev-discoverScreen

Contact discovery
This commit is contained in:
zjs81 2026-03-06 11:59:24 -07:00 committed by GitHub
commit d96cd34771
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
43 changed files with 3084 additions and 105 deletions

View file

@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:convert';
import 'package:crypto/crypto.dart' as crypto;
import 'package:meshcore_open/models/discovery_contact.dart';
import 'package:pointycastle/export.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
@ -24,6 +25,7 @@ import '../storage/channel_message_store.dart';
import '../storage/channel_order_store.dart';
import '../storage/channel_settings_store.dart';
import '../storage/channel_store.dart';
import '../storage/contact_discovery_store.dart';
import '../storage/contact_settings_store.dart';
import '../storage/contact_store.dart';
import '../storage/message_store.dart';
@ -111,6 +113,7 @@ class MeshCoreConnector extends ChangeNotifier {
final List<ScanResult> _scanResults = [];
final List<Contact> _contacts = [];
final List<DiscoveryContact> _discoveredContacts = [];
final List<Channel> _channels = [];
final Map<String, List<Message>> _conversations = {};
final Map<int, List<ChannelMessage>> _channelMessages = {};
@ -155,6 +158,18 @@ class MeshCoreConnector extends ChangeNotifier {
bool _batteryRequested = false;
bool _awaitingSelfInfo = false;
bool _preserveContactsOnRefresh = false;
bool _autoAddUsers = false;
bool _autoAddRepeaters = false;
bool _autoAddRoomServers = false;
bool _autoAddSensors = false;
bool _overwriteOldest = false;
bool _manualAddContacts = false;
int _telemetryModeBase = 0;
int _telemetryModeLoc = 0;
int _telemetryModeEnv = 0;
int _advertLocPolicy = 0;
int _multiAcks = 0;
static const int _defaultMaxContacts = 32;
static const int _defaultMaxChannels = 8;
int _maxContacts = _defaultMaxContacts;
@ -195,6 +210,7 @@ class MeshCoreConnector extends ChangeNotifier {
final ChannelSettingsStore _channelSettingsStore = ChannelSettingsStore();
final ContactSettingsStore _contactSettingsStore = ContactSettingsStore();
final ContactStore _contactStore = ContactStore();
final ContactDiscoveryStore _discoveryContactStore = ContactDiscoveryStore();
final ChannelStore _channelStore = ChannelStore();
final UnreadStore _unreadStore = UnreadStore();
List<Channel> _cachedChannels = [];
@ -242,6 +258,10 @@ class MeshCoreConnector extends ChangeNotifier {
);
}
List<DiscoveryContact> get discoveredContacts {
return List.unmodifiable(_discoveredContacts);
}
List<Channel> get channels => List.unmodifiable(_channels);
bool get isConnected => _state == MeshCoreConnectionState.connected;
bool get isLoadingContacts => _isLoadingContacts;
@ -258,12 +278,18 @@ class MeshCoreConnector extends ChangeNotifier {
int? get currentBwHz => _currentBwHz;
int? get currentSf => _currentSf;
int? get currentCr => _currentCr;
bool? get autoAddUsers => _autoAddUsers;
bool? get autoAddRepeaters => _autoAddRepeaters;
bool? get autoAddRoomServers => _autoAddRoomServers;
bool? get autoAddSensors => _autoAddSensors;
bool? get autoAddOverwriteOldest => _overwriteOldest;
bool? get clientRepeat => _clientRepeat;
int? get firmwareVerCode => _firmwareVerCode;
Map<String, String>? get currentCustomVars => _currentCustomVars;
int? get batteryMillivolts => _batteryMillivolts;
int get maxContacts => _maxContacts;
int get maxChannels => _maxChannels;
Set<String> get knownContactKeys => Set.unmodifiable(_knownContactKeys);
bool get isSyncingQueuedMessages => _isSyncingQueuedMessages;
bool get isSyncingChannels => _isSyncingChannels;
int get channelSyncProgress =>
@ -602,6 +628,13 @@ class MeshCoreConnector extends ChangeNotifier {
}
}
Future<void> loadDiscoveredContactCache() async {
final cached = await _discoveryContactStore.loadContacts();
_discoveredContacts
..clear()
..addAll(cached);
}
Future<void> loadChannelSettings({int? maxChannels}) async {
_channelSmazEnabled.clear();
final channelCount = maxChannels ?? _maxChannels;
@ -848,6 +881,9 @@ class MeshCoreConnector extends ChangeNotifier {
// Fetch channels so we can track unread counts for incoming messages
unawaited(getChannels());
// Load discovered contacts from storage
unawaited(loadDiscoveredContactCache());
} catch (e) {
debugPrint("Connection error: $e");
await disconnect(manual: false);
@ -968,6 +1004,7 @@ class MeshCoreConnector extends ChangeNotifier {
_deviceDisplayName = null;
_deviceId = null;
_contacts.clear();
_discoveredContacts.clear();
_conversations.clear();
_loadedConversationKeys.clear();
_selfPublicKey = null;
@ -1058,8 +1095,9 @@ class MeshCoreConnector extends ChangeNotifier {
await sendFrame(buildDeviceQueryFrame());
await sendFrame(buildAppStartFrame());
await requestBatteryStatus(force: true);
await sendFrame(buildGetRadioSettingsFrame());
await sendFrame(buildGetCustomVarsFrame());
await sendFrame(buildGetAutoAddFlagsFrame());
_scheduleSelfInfoRetry();
}
@ -1070,7 +1108,7 @@ class MeshCoreConnector extends ChangeNotifier {
await sendFrame(buildAppStartFrame());
await sendFrame(buildGetCustomVarsFrame());
await requestBatteryStatus();
await sendFrame(buildGetAutoAddFlagsFrame());
_scheduleSelfInfoRetry();
}
@ -1485,6 +1523,8 @@ class MeshCoreConnector extends ChangeNotifier {
Future<void> removeContact(Contact contact) async {
if (!isConnected) return;
_handleDiscovery(contact, Uint8List(0), noNotify: true);
await sendFrame(buildRemoveContactFrame(contact.publicKey));
_contacts.removeWhere((c) => c.publicKeyHex == contact.publicKeyHex);
_knownContactKeys.remove(contact.publicKeyHex);
@ -1499,6 +1539,42 @@ class MeshCoreConnector extends ChangeNotifier {
notifyListeners();
}
Future<void> removeDiscoveredContact(DiscoveryContact contact) async {
if (!isConnected) return;
_discoveredContacts.removeWhere(
(c) => c.publicKeyHex == contact.publicKeyHex,
);
unawaited(_persistDiscoveredContacts());
notifyListeners();
}
Future<void> importDiscoveredContact(DiscoveryContact contact) async {
if (!isConnected) return;
await sendFrame(
buildUpdateContactPathFrame(
contact.publicKey,
contact.path,
contact.pathLength,
type: contact.type,
flags: 0,
name: contact.name,
),
);
_handleContactAdvert(
Contact(
publicKey: contact.publicKey,
name: contact.name,
type: contact.type,
pathLength: contact.pathLength,
path: contact.path,
lastSeen: DateTime.now(),
),
);
notifyListeners();
}
Future<void> clearContactPath(Contact contact) async {
if (!isConnected) return;
@ -1899,8 +1975,9 @@ class MeshCoreConnector extends ChangeNotifier {
case respCodeChannelInfo:
_handleChannelInfo(frame);
break;
case respCodeRadioSettings:
_handleRadioSettings(frame);
case respCodeAutoAddConfig:
_handleAutoAddConfig(frame);
_checkManualAddContacts();
break;
case respCodeBattAndStorage:
_handleBatteryAndStorage(frame);
@ -1973,25 +2050,35 @@ class MeshCoreConnector extends ChangeNotifier {
// [56] = sf
// [57] = cr
// [58+] = node_name
if (frame.length < 4 + pubKeySize) return;
final reader = BufferReader(frame);
try {
reader.skipBytes(2);
_currentTxPower = reader.readByte();
_maxTxPower = reader.readByte();
_selfPublicKey = reader.readBytes(pubKeySize);
_selfLatitude = reader.readInt32LE() / 1000000.0;
_selfLongitude = reader.readInt32LE() / 1000000.0;
_multiAcks = reader.readByte();
_advertLocPolicy = reader.readByte();
_currentTxPower = frame[2];
_maxTxPower = frame[3];
_selfPublicKey = Uint8List.fromList(frame.sublist(4, 4 + pubKeySize));
_selfLatitude = readInt32LE(frame, 36) / 1000000.0;
_selfLongitude = readInt32LE(frame, 40) / 1000000.0;
final telemetryFlag = reader.readByte();
_telemetryModeBase = telemetryFlag & 0x03;
_telemetryModeEnv = telemetryFlag >> 2 & 0x03;
_telemetryModeLoc = telemetryFlag >> 4 & 0x03;
// Radio settings (if frame is long enough)
if (frame.length >= 58) {
_currentFreqHz = readUint32LE(frame, 48);
_currentBwHz = readUint32LE(frame, 52);
_currentSf = frame[56];
_currentCr = frame[57];
}
_manualAddContacts = reader.readByte() & 0x01 == 0x00;
// Node name starts at offset 58 if frame is long enough
if (frame.length > 58) {
_selfName = readCString(frame, 58, frame.length - 58);
_currentFreqHz = reader.readUInt32LE();
_currentBwHz = reader.readUInt32LE();
_currentSf = reader.readByte();
_currentCr = reader.readByte();
_selfName = reader.readString();
} catch (e) {
_appDebugLogService?.error(
'Error parsing SELF_INFO frame: $e',
tag: 'Connector',
);
}
_awaitingSelfInfo = false;
_selfInfoRetryTimer?.cancel();
@ -2052,25 +2139,6 @@ class MeshCoreConnector extends ChangeNotifier {
unawaited(_requestNextQueuedMessage());
}
void _handleRadioSettings(Uint8List frame) {
// Frame format from C++:
// [0] = RESP_CODE_RADIO_SETTINGS
// [1-4] = freq (uint32 LE, in Hz)
// [5-8] = bw (uint32 LE, in Hz)
// [9] = sf
// [10] = cr
if (frame.length >= 11) {
_currentFreqHz = readUint32LE(frame, 1);
_currentBwHz = readUint32LE(frame, 5);
_currentSf = frame[9];
_currentCr = frame[10];
debugPrint(
'Radio settings: freq=$_currentFreqHz bw=$_currentBwHz sf=$_currentSf cr=$_currentCr',
);
notifyListeners();
}
}
void _handleBatteryAndStorage(Uint8List frame) {
// Frame format from C++:
// [0] = RESP_CODE_BATT_AND_STORAGE
@ -2088,6 +2156,32 @@ class MeshCoreConnector extends ChangeNotifier {
}
}
void _checkManualAddContacts() async {
// If manual add contacts is enabled, set auto add config and other params.
// and disable it after
if (_manualAddContacts) {
await sendFrame(
buildSetAutoAddConfigFrame(
autoAddChat: true,
autoAddRepeater: true,
autoAddRoomServer: true,
autoAddSensor: true,
overwriteOldest: _overwriteOldest,
),
);
await sendFrame(
buildSetOtherParamsFrame(
(_telemetryModeEnv << 4) |
(_telemetryModeLoc << 2) |
(_telemetryModeBase),
_advertLocPolicy,
_multiAcks,
),
);
_manualAddContacts = false;
}
}
/// Calculate timeout for a message based on radio settings and path length
/// Returns timeout in milliseconds, considering number of hops
int calculateTimeout({required int pathLength, int messageBytes = 100}) {
@ -2271,6 +2365,10 @@ class MeshCoreConnector extends ChangeNotifier {
await _contactStore.saveContacts(_contacts);
}
Future<void> _persistDiscoveredContacts() async {
await _discoveryContactStore.saveContacts(_discoveredContacts);
}
int _latestContactLastmod() {
if (_contacts.isEmpty) return 0;
var latest = 0;
@ -3705,22 +3803,99 @@ class MeshCoreConnector extends ChangeNotifier {
appLogger.warn('Malformed RX frame: $e', tag: 'Connector');
return;
}
final rawPacket = frame.sublist(3);
switch (payloadType) {
case payloadTypeADVERT:
_handlePayloadAdvertReceived(payload, pathBytes, routeType, snr);
_handlePayloadAdvertReceived(
rawPacket,
payload,
pathBytes,
routeType,
snr,
);
break;
default:
}
}
void importContact(Uint8List frame) {
final packet = BufferReader(frame);
int payloadType = 0;
Uint8List pathBytes = Uint8List(0);
try {
packet.skipBytes(1); // Skip frame type byte
packet.skipBytes(1); // Skip SNR byte
packet.skipBytes(1); // Skip RSSI byte
final header = packet.readByte();
payloadType = (header >> 2) & 0x0F;
//final payloadVer = (header >> 6) & 0x03;
final pathLen = packet.readByte();
pathBytes = packet.readBytes(pathLen);
} catch (e) {
appLogger.warn('Malformed RX frame: $e', tag: 'Connector');
return;
}
double latitude = 0.0;
double longitude = 0.0;
String name = '';
Uint8List publicKey = Uint8List(0);
int type = 0;
int timestamp = 0;
bool hasLocation = false;
bool hasName = false;
if (payloadType != payloadTypeADVERT) {
appLogger.warn('Unexpected payload type: $payloadType', tag: 'Connector');
return;
}
try {
publicKey = packet.readBytes(32);
timestamp = packet.readInt32LE();
//TODO add signature verification
packet.skipBytes(64); // Skip signature for now
final flags = packet.readByte();
type = flags & 0x0F;
hasLocation = (flags & 0x10) != 0;
// For future use:
//final hasFeature1 = (flags & 0x20) != 0;
//final hasFeature2 = (flags & 0x40) != 0;
hasName = (flags & 0x80) != 0;
if (hasLocation && packet.remaining >= 8) {
latitude = packet.readInt32LE() / 1e6;
longitude = packet.readInt32LE() / 1e6;
}
if (hasName && packet.remaining > 0) {
name = packet.readString();
}
} catch (e) {
appLogger.warn('Malformed advert frame: $e', tag: 'Connector');
return;
}
importDiscoveredContact(
DiscoveryContact(
rawPacket: frame,
publicKey: publicKey,
name: name,
type: type,
pathLength: pathBytes.length,
path: Uint8List.fromList(
pathBytes.reversed.toList(),
), // Store path in reverse for easier use in outgoing messages
latitude: latitude,
longitude: longitude,
lastSeen: DateTime.fromMillisecondsSinceEpoch(timestamp * 1000),
),
);
}
void _handlePayloadAdvertReceived(
Uint8List frame,
Uint8List rawPacket,
Uint8List payload,
Uint8List path,
int routeType,
double snr,
) {
final advert = BufferReader(frame);
final advert = BufferReader(payload);
double latitude = 0.0;
double longitude = 0.0;
String name = '';
@ -3758,6 +3933,7 @@ class MeshCoreConnector extends ChangeNotifier {
return;
}
//We ignore our own adverts
if (listEquals(publicKey, _selfPublicKey)) {
return;
}
@ -3778,7 +3954,14 @@ class MeshCoreConnector extends ChangeNotifier {
longitude: longitude,
lastSeen: DateTime.fromMillisecondsSinceEpoch(timestamp * 1000),
);
_handleContactAdvert(newContact);
if ((_autoAddUsers && type == advTypeChat) ||
(_autoAddRepeaters && type == advTypeRepeater) ||
(_autoAddRoomServers && type == advTypeRoom) ||
(_autoAddSensors && type == advTypeSensor)) {
_handleContactAdvert(newContact);
} else {
_handleDiscovery(newContact, rawPacket);
}
_updateDirectRepeater(newContact, snr, path);
return;
}
@ -3866,6 +4049,84 @@ class MeshCoreConnector extends ChangeNotifier {
}
notifyListeners();
}
void _handleAutoAddConfig(Uint8List frame) {
final reader = BufferReader(frame);
try {
reader.skipBytes(1); // Skip the response code byte
final flags = reader.readByte();
_autoAddUsers = flags & autoAddChatFlag != 0;
_autoAddRepeaters = flags & autoAddRepeaterFlag != 0;
_autoAddRoomServers = flags & autoAddRoomServerFlag != 0;
_autoAddSensors = flags & autoAddSensorFlag != 0;
_overwriteOldest = flags & autoAddOverwriteOldestFlag != 0;
} catch (e) {
appLogger.error('Failed to parse auto-add config: $e', tag: 'Connector');
}
}
void _handleDiscovery(
Contact contact,
Uint8List rawPacket, {
bool noNotify = false,
}) {
appLogger.info('Discovered new contact: ${contact.name}', tag: 'Connector');
final existingIndex = _discoveredContacts.indexWhere(
(c) => c.publicKeyHex == contact.publicKeyHex,
);
// Update existing contact
if (existingIndex >= 0) {
_discoveredContacts[existingIndex] = _discoveredContacts[existingIndex]
.copyWith(
rawPacket: rawPacket,
name: contact.name,
type: contact.type,
pathLength: contact.pathLength,
path: contact.path,
latitude: contact.latitude,
longitude: contact.longitude,
lastSeen: contact.lastSeen,
);
notifyListeners();
unawaited(_persistDiscoveredContacts());
return;
}
final disContact = DiscoveryContact(
rawPacket: rawPacket,
publicKey: contact.publicKey,
name: contact.name,
type: contact.type,
pathLength: contact.pathLength,
path: contact.path,
latitude: contact.latitude,
longitude: contact.longitude,
lastSeen: contact.lastSeen,
);
_discoveredContacts.add(disContact);
unawaited(_persistDiscoveredContacts());
// Show notification for new contact (advertisement)
if (_appSettingsService != null && !noNotify) {
final settings = _appSettingsService!.settings;
if (settings.notificationsEnabled && settings.notifyOnNewAdvert) {
_notificationService.showAdvertNotification(
contactName: contact.name,
contactType: contact.typeLabel,
contactId: contact.publicKeyHex,
);
}
}
}
void removeAllDiscoveredContacts() {
_discoveredContacts.clear();
unawaited(_persistDiscoveredContacts());
notifyListeners();
}
}
const int _phRouteMask = 0x03;

View file

@ -120,25 +120,27 @@ class BufferWriter {
}
void writeHex(String hex) {
// Validate hex string length is even and not empty
if (hex.isEmpty || hex.length % 2 != 0) {
throw FormatException('Invalid hex string length: ${hex.length}');
}
List<int> result = [];
for (int i = 0; i < hex.length ~/ 2; i++) {
final hexByte = hex.substring(i * 2, i * 2 + 2);
final byte = int.tryParse(hexByte, radix: 16);
if (byte == null) {
throw FormatException(
'Invalid hex characters at position $i: $hexByte',
);
}
result.add(byte);
}
writeBytes(Uint8List.fromList(result));
writeBytes(hex2Uint8List(hex));
}
}
Uint8List hex2Uint8List(String hex) {
// Validate hex string length is even and not empty
if (hex.isEmpty || hex.length % 2 != 0) {
throw FormatException('Invalid hex string length: ${hex.length}');
}
List<int> result = [];
for (int i = 0; i < hex.length ~/ 2; i++) {
final hexByte = hex.substring(i * 2, i * 2 + 2);
final byte = int.tryParse(hexByte, radix: 16);
if (byte == null) {
throw FormatException('Invalid hex characters at position $i: $hexByte');
}
result.add(byte);
}
return Uint8List.fromList(result);
}
// Command codes (to device)
const int cmdAppStart = 1;
const int cmdSendTxtMsg = 2;
@ -168,11 +170,13 @@ const int cmdGetChannel = 31;
const int cmdSetChannel = 32;
const int cmdSendTracePath = 36;
const int cmdSetOtherParams = 38;
const int cmdGetRadioSettings = 57;
const int cmdSendAnonReq = 57;
const int cmdGetTelemetryReq = 39;
const int cmdGetCustomVar = 40;
const int cmdSetCustomVar = 41;
const int cmdSendBinaryReq = 50;
const int cmdSetAutoAddConfig = 58;
const int cmdGetAutoAddConfig = 59;
// Text message types
const int txtTypePlain = 0;
@ -206,8 +210,8 @@ const int respCodeDeviceInfo = 13;
const int respCodeContactMsgRecvV3 = 16;
const int respCodeChannelMsgRecvV3 = 17;
const int respCodeChannelInfo = 18;
const int respCodeRadioSettings = 25;
const int respCodeCustomVars = 21;
const int respCodeAutoAddConfig = 25;
// Push codes (async from device)
const int pushCodeAdvert = 0x80;
@ -253,6 +257,18 @@ const int payloadTypeCONTROL = 0x0B; // a control/discovery packet
const int payloadTypeRawCustom =
0x0F; // custom packet as raw bytes, for applications with custom encryption, payloads, etc
//auto-add flags
const int autoAddOverwriteOldestFlag =
1 << 0; // 0x01 - overwrite oldest non-favourite when full
const int autoAddChatFlag =
1 << 1; // 0x02 - auto-add Chat (Companion) (ADV_TYPE_CHAT)
const int autoAddRepeaterFlag =
1 << 2; // 0x04 - auto-add Repeater (ADV_TYPE_REPEATER)
const int autoAddRoomServerFlag =
1 << 3; // 0x08 - auto-add Room Server (ADV_TYPE_ROOM)
const int autoAddSensorFlag =
1 << 4; // 0x10 - auto-add Sensor (ADV_TYPE_SENSOR)
// Sizes
const int pubKeySize = 32;
const int maxPathSize = 64;
@ -303,7 +319,7 @@ const int contactNameOffset = 100;
const int contactTimestampOffset = 132;
const int contactLatOffset = 136;
const int contactLonOffset = 140;
const int contactLastmodOffset = 144;
const int contactLastModOffset = 144;
const int contactFrameSize = 148;
// Message frame offsets
@ -681,16 +697,15 @@ Uint8List buildGetContactByKeyFrame(Uint8List pubKey) {
return writer.toBytes();
}
// Build CMD_GET_RADIO_SETTINGS frame
Uint8List buildGetRadioSettingsFrame() {
return Uint8List.fromList([cmdGetRadioSettings]);
}
//Build CMD_GET_CUSTOM_VARS frame
Uint8List buildGetCustomVarsFrame() {
return Uint8List.fromList([cmdGetCustomVar]);
}
Uint8List buildGetAutoAddFlagsFrame() {
return Uint8List.fromList([cmdGetAutoAddConfig]);
}
// Calculate LoRa airtime for a packet
// Based on Semtech SX127x datasheet formula
// Returns airtime in milliseconds
@ -815,10 +830,10 @@ Uint8List buildExportContactFrame(Uint8List pubKey) {
// Build a import contact frame
// [cmd][contact_frame x98+]
Uint8List buildImportContactFrame(String contactFrame) {
Uint8List buildImportContactFrame(Uint8List contactFrame) {
final writer = BufferWriter();
writer.writeByte(cmdImportContact);
writer.writeHex(contactFrame);
writer.writeBytes(contactFrame);
return writer.toBytes();
}
@ -832,20 +847,40 @@ Uint8List buildZeroHopContact(Uint8List pubKey) {
}
// Build CMD_SET_OTHER_PARAMS frame
// Format: [cmd][allowAutoAddContacts][allowTelemetryFlags][advertLocationPolicy][multiAcks]
// Format: [cmd][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
//Going forward the app will just set Auto Add Contacts to disabled, and use the filter flags
//Allow Auto Add Contacts use inverted logic (0x01 = disabled, 0x00 = enabled).
writer.writeByte(0x01);
writer.writeByte(allowTelemetryFlags); // Allow Telemetry Flags
writer.writeByte(advertLocationPolicy); // Advertisement Location Policy
writer.writeByte(multiAcks); // Multi Acknowledgements
return writer.toBytes();
}
// Build CMD_SET_AUTO_ADD_CONFIG frame
// Format: [cmd][flags]
Uint8List buildSetAutoAddConfigFrame({
required bool autoAddChat,
required bool autoAddRepeater,
required bool autoAddRoomServer,
required bool autoAddSensor,
required bool overwriteOldest,
}) {
final writer = BufferWriter();
writer.writeByte(cmdSetAutoAddConfig);
int flags = 0;
if (autoAddChat) flags |= autoAddChatFlag;
if (autoAddRepeater) flags |= autoAddRepeaterFlag;
if (autoAddRoomServer) flags |= autoAddRoomServerFlag;
if (autoAddSensor) flags |= autoAddSensorFlag;
if (overwriteOldest) flags |= autoAddOverwriteOldestFlag;
writer.writeByte(flags);
return writer.toBytes();
}

View file

@ -1801,5 +1801,30 @@
"contacts_unread": "Непрочетено",
"contacts_searchRepeaters": "Търсене на {number}{str} повтарящи се...",
"contacts_searchContactsNoNumber": "Търси контакти...",
"contacts_searchUsers": "Търсене на {number}{str} потребители..."
"contacts_searchUsers": "Търсене на {number}{str} потребители...",
"contactsSettings_title": "Настройки на контактите",
"contactsSettings_autoAddTitle": "Автоматично откриване",
"contactsSettings_autoAddUsersTitle": "Автоматично добавяне на потребители",
"contactsSettings_otherTitle": "Други настройки свързани с контакти",
"settings_contactSettingsSubtitle": "Настройки за добавяне на контакти.",
"settings_contactSettings": "Настройки за контакти",
"contactsSettings_autoAddSensorsTitle": "Автоматично добавяне на датчици",
"contactsSettings_autoAddRoomServersTitle": "Автоматично добавяне на сървъри на стаите",
"contactsSettings_autoAddRoomServersSubtitle": "Позволи на спътника да добавя автоматично откритите сървъри на стаите.",
"contactsSettings_autoAddRepeatersTitle": "Автоматично добавяне на повтарящи се елементи",
"contactsSettings_autoAddUsersSubtitle": "Позволи на спътника да добавя автоматично откритите потребители.",
"contactsSettings_autoAddRepeatersSubtitle": "Позволи на спътника да добавя автоматично откритите повтарящи се устройства.",
"contactsSettings_autoAddSensorsSubtitle": "Позволи на спътника да добавя автоматично откритите датчици.",
"contactsSettings_overwriteOldestTitle": "Премахни най-старото",
"discoveredContacts_Title": "Открити контакти",
"discoveredContacts_searchHint": "Търсене на открити контакти",
"discoveredContacts_noMatching": "Няма съвпадащи контакти",
"discoveredContacts_contactAdded": "Контакт добавен",
"discoveredContacts_copyContact": "Копирай контакт в клипборда",
"discoveredContacts_deleteContact": "Изтрий контакт",
"discoveredContacts_addContact": "Добави контакт",
"contactsSettings_overwriteOldestSubtitle": "Когато списъкът с контакти е пълен, най-старият неключов контакт ще бъде заменен.",
"discoveredContacts_deleteContactAll": "Изтриване на Всички Открити Контакти",
"discoveredContacts_deleteContactAllContent": "Сигурни ли сте, че искате да изтриете всички открити контакти?",
"common_deleteAll": "Изтрий всичко"
}

View file

@ -1829,5 +1829,30 @@
"contacts_searchRepeaters": "Suche {number}{str} Repeater...",
"contacts_searchFavorites": "Suche {number}{str} Favoriten...",
"contacts_searchUsers": "Suche {number}{str} Benutzer...",
"contacts_searchRoomServers": "Suche {number}{str} Raumserver..."
"contacts_searchRoomServers": "Suche {number}{str} Raumserver...",
"settings_contactSettings": "Kontakteinstellungen",
"contactsSettings_otherTitle": "Weitere Einstellungen zu Kontakten",
"contactsSettings_title": "Kontakteinstellungen",
"contactsSettings_autoAddTitle": "Automatische Erkennung",
"contactsSettings_autoAddUsersTitle": "Automatische Hinzufügung von Benutzern",
"settings_contactSettingsSubtitle": "Einstellungen für das Hinzufügen von Kontakten",
"contactsSettings_autoAddSensorsTitle": "Automatisch Sensoren hinzufügen",
"contactsSettings_autoAddUsersSubtitle": "Ermöglichen Sie dem Begleiter, automatisch entdeckte Benutzer hinzuzufügen",
"contactsSettings_autoAddRoomServersTitle": "Automatisch Raumservers hinzufügen",
"contactsSettings_autoAddRoomServersSubtitle": "Ermöglichen Sie dem Begleiter, entdeckte Raumserver automatisch hinzuzufügen",
"contactsSettings_autoAddRepeatersTitle": "Automatisch Repeater hinzufügen",
"contactsSettings_autoAddRepeatersSubtitle": "Ermöglichen Sie dem Begleiter, automatisch entdeckte Repeater hinzuzufügen.",
"discoveredContacts_noMatching": "Keine passenden Kontakte",
"discoveredContacts_searchHint": "Entdeckte Kontakte suchen",
"discoveredContacts_addContact": "Kontakt hinzufügen",
"discoveredContacts_contactAdded": "Kontakt hinzugefügt",
"discoveredContacts_deleteContact": "Kontakt löschen",
"discoveredContacts_Title": "Entdeckte Kontakte",
"discoveredContacts_copyContact": "Kontakt in die Zwischenablage kopieren",
"contactsSettings_overwriteOldestTitle": "Überschreiben des Ältesten",
"contactsSettings_autoAddSensorsSubtitle": "Ermöglichen Sie dem Begleiter, automatisch entdeckte Sensoren hinzuzufügen",
"contactsSettings_overwriteOldestSubtitle": "Wenn die Kontaktliste voll ist, wird der älteste nicht favorisierte Kontakt ersetzt.",
"common_deleteAll": "Alles löschen",
"discoveredContacts_deleteContactAllContent": "Sind Sie sicher, dass Sie alle gefundenen Kontakte löschen möchten?",
"discoveredContacts_deleteContactAll": "Alle entdeckten Kontakte löschen"
}

View file

@ -10,6 +10,7 @@
"common_unknownDevice": "Unknown Device",
"common_save": "Save",
"common_delete": "Delete",
"common_deleteAll": "Delete All",
"common_close": "Close",
"common_edit": "Edit",
"common_add": "Add",
@ -100,6 +101,8 @@
"settings_locationIntervalInvalid": "Interval must be at least 60 seconds, and less than 86400 seconds.",
"settings_latitude": "Latitude",
"settings_longitude": "Longitude",
"settings_contactSettings": "Contact Settings",
"settings_contactSettingsSubtitle": "Settings for how contacts are added.",
"settings_privacyMode": "Privacy Mode",
"settings_privacyModeSubtitle": "Hide name/location in advertisements",
"settings_privacyModeToggle": "Toggle privacy mode to hide your name and location in advertisements.",
@ -1839,5 +1842,27 @@
"settings_gpxExportShareText": "Map data exported from meshcore-open",
"settings_gpxExportShareSubject": "meshcore-open GPX map data export",
"snrIndicator_nearByRepeaters": "Nearby Repeaters",
"snrIndicator_lastSeen": "Last seen"
}
"snrIndicator_lastSeen": "Last seen",
"contactsSettings_title": "Contacts settings",
"contactsSettings_autoAddTitle": "Automatic Discovery",
"contactsSettings_otherTitle": "Other contact related settings",
"contactsSettings_autoAddUsersTitle": "Auto-add users",
"contactsSettings_autoAddUsersSubtitle": "Allow the companion to automatically add discovered users.",
"contactsSettings_autoAddRepeatersTitle": "Auto-add repeaters",
"contactsSettings_autoAddRepeatersSubtitle": "Allow the companion to automatically add discovered repeaters.",
"contactsSettings_autoAddRoomServersTitle": "Auto-add room servers",
"contactsSettings_autoAddRoomServersSubtitle": "Allow the companion to automatically add discovered room servers.",
"contactsSettings_autoAddSensorsTitle": "Auto-add sensors",
"contactsSettings_autoAddSensorsSubtitle": "Allow the companion to automatically add discovered sensors.",
"contactsSettings_overwriteOldestTitle": "Overwrite Oldest",
"contactsSettings_overwriteOldestSubtitle": "When the contact list is full, the oldest non-favorited contact will be replaced.",
"discoveredContacts_Title": "Discovered Contacts",
"discoveredContacts_noMatching": "No matching contacts",
"discoveredContacts_searchHint": "Search discovered contacts",
"discoveredContacts_contactAdded": "Contact added",
"discoveredContacts_addContact": "Add Contact",
"discoveredContacts_copyContact": "Copy Contact to clipboard",
"discoveredContacts_deleteContact": "Delete Discovered Contact",
"discoveredContacts_deleteContactAll": "Delete All Discovered Contacts",
"discoveredContacts_deleteContactAllContent": "Are you sure you want to delete all discovered contacts?"
}

View file

@ -1829,5 +1829,30 @@
"contacts_searchFavorites": "Buscar {number}{str} Favoritos...",
"contacts_searchUsers": "Buscar {number}{str} Usuarios...",
"contacts_searchRepeaters": "Buscar {number}{str} Repetidores...",
"contacts_searchRoomServers": "Buscar {number}{str} servidores de sala..."
"contacts_searchRoomServers": "Buscar {number}{str} servidores de sala...",
"contactsSettings_autoAddTitle": "Detección automática",
"settings_contactSettings": "Configuración de contacto",
"contactsSettings_autoAddUsersTitle": "Agregar usuarios automáticamente",
"contactsSettings_otherTitle": "Otras configuraciones relacionadas con el contacto",
"contactsSettings_autoAddUsersSubtitle": "Permitir que el compañero agregue automáticamente a los usuarios descubiertos.",
"contactsSettings_autoAddRepeatersSubtitle": "Permitir que el compañero agregue automáticamente los repetidores descubiertos.",
"contactsSettings_autoAddRoomServersSubtitle": "Permitir que el compañero agregue automáticamente los servidores de salas descubiertos.",
"contactsSettings_autoAddSensorsTitle": "Agregar sensores automáticamente",
"contactsSettings_title": "Configuración de contactos",
"settings_contactSettingsSubtitle": "Configuración de cómo se agregan los contactos.",
"contactsSettings_autoAddSensorsSubtitle": "Permitir que el compañero agregue automáticamente los sensores descubiertos.",
"contactsSettings_autoAddRepeatersTitle": "Agregar repetidores automáticamente",
"contactsSettings_overwriteOldestTitle": "Sobreescribir el más antiguo",
"contactsSettings_autoAddRoomServersTitle": "Agregar automáticamente servidores de sala",
"discoveredContacts_noMatching": "No se encontraron contactos coincidentes",
"discoveredContacts_contactAdded": "Contacto agregado",
"discoveredContacts_copyContact": "Copiar contacto al portapapeles",
"discoveredContacts_deleteContact": "Eliminar contacto",
"discoveredContacts_Title": "Contactos descubiertos",
"discoveredContacts_searchHint": "Buscar contactos descubiertos",
"discoveredContacts_addContact": "Agregar contacto",
"contactsSettings_overwriteOldestSubtitle": "Cuando la lista de contactos esté llena, se reemplazará el contacto no favorito más antiguo.",
"common_deleteAll": "Eliminar todo",
"discoveredContacts_deleteContactAll": "Eliminar Todos los Contactos Descubiertos",
"discoveredContacts_deleteContactAllContent": "¿Está seguro de que desea eliminar todos los contactos descubiertos!"
}

View file

@ -1801,5 +1801,30 @@
"contacts_searchUsers": "Rechercher {number}{str} utilisateurs...",
"contacts_searchRoomServers": "Rechercher {number}{str} serveurs de salle...",
"contacts_searchRepeaters": "Rechercher {number}{str} Répéteurs...",
"contacts_searchContactsNoNumber": "Rechercher des contacts..."
"contacts_searchContactsNoNumber": "Rechercher des contacts...",
"settings_contactSettings": "Paramètres de contact",
"settings_contactSettingsSubtitle": "Paramètres pour l'ajout de contacts",
"contactsSettings_autoAddRepeatersTitle": "Ajouter automatiquement les répéteurs",
"contactsSettings_autoAddRepeatersSubtitle": "Autoriser le compagnon à ajouter automatiquement les répéteurs découverts",
"contactsSettings_autoAddRoomServersTitle": "Ajouter automatiquement les serveurs de salle",
"contactsSettings_autoAddRoomServersSubtitle": "Autoriser le compagnon à ajouter automatiquement les serveurs de salles découverts",
"contactsSettings_otherTitle": "Autres paramètres liés aux contacts",
"contactsSettings_title": "Paramètres des contacts",
"contactsSettings_autoAddUsersTitle": "Ajouter automatiquement les utilisateurs",
"contactsSettings_autoAddTitle": "Découverte automatique",
"contactsSettings_autoAddSensorsTitle": "Ajouter automatiquement les capteurs",
"contactsSettings_autoAddUsersSubtitle": "Autoriser le compagnon à ajouter automatiquement les utilisateurs découverts",
"discoveredContacts_noMatching": "Aucun contact correspondant",
"discoveredContacts_contactAdded": "Contact ajouté",
"discoveredContacts_addContact": "Ajouter un contact",
"discoveredContacts_copyContact": "Copier le contact dans le presse-papiers",
"discoveredContacts_deleteContact": "Supprimer le contact",
"contactsSettings_overwriteOldestTitle": "Écraser le plus ancien",
"contactsSettings_autoAddSensorsSubtitle": "Autoriser le compagnon à ajouter automatiquement les capteurs découverts.",
"discoveredContacts_Title": "Contacts découverts",
"discoveredContacts_searchHint": "Rechercher des contacts découverts",
"contactsSettings_overwriteOldestSubtitle": "Lorsque la liste de contacts est pleine, le contact le plus ancien non favori sera remplacé.",
"common_deleteAll": "Supprimer tout",
"discoveredContacts_deleteContactAll": "Supprimer tous les contacts découverts",
"discoveredContacts_deleteContactAllContent": "Êtes-vous sûr de vouloir supprimer tous les contacts découverts ?"
}

View file

@ -1801,5 +1801,30 @@
"contacts_searchFavorites": "Cerca {number}{str} Preferiti...",
"contacts_unread": "Non letti",
"contacts_searchRepeaters": "Cerca {number}{str} Ripetitori...",
"contacts_searchRoomServers": "Cerca {number}{str} server Room..."
"contacts_searchRoomServers": "Cerca {number}{str} server Room...",
"contactsSettings_title": "Impostazioni dei contatti",
"settings_contactSettings": "Impostazioni di contatto",
"contactsSettings_otherTitle": "Altre impostazioni relative ai contatti",
"contactsSettings_autoAddUsersSubtitle": "Consenti al compagno di aggiungere automaticamente gli utenti scoperti.",
"contactsSettings_autoAddRepeatersTitle": "Aggiungere ripetitori automaticamente",
"contactsSettings_autoAddRoomServersSubtitle": "Consenti al compagno di aggiungere automaticamente i server delle stanze scoperte.",
"contactsSettings_autoAddSensorsTitle": "Aggiungere automaticamente i sensori",
"settings_contactSettingsSubtitle": "Impostazioni per l'aggiunta dei contatti",
"contactsSettings_autoAddUsersTitle": "Aggiungere utenti automaticamente",
"contactsSettings_autoAddTitle": "Scoperta automatica",
"contactsSettings_autoAddSensorsSubtitle": "Consenti al compagno di aggiungere automaticamente i sensori scoperti",
"discoveredContacts_noMatching": "Nessun contatto corrispondente",
"contactsSettings_autoAddRepeatersSubtitle": "Consenti al compagno di aggiungere automaticamente i ripetitori scoperti.",
"discoveredContacts_searchHint": "Cerca contatti scoperti",
"contactsSettings_autoAddRoomServersTitle": "Aggiungere automaticamente i server delle stanze",
"discoveredContacts_addContact": "Aggiungi contatto",
"contactsSettings_overwriteOldestTitle": "Sostituisci il più vecchio",
"discoveredContacts_Title": "Contatti scoperti",
"discoveredContacts_contactAdded": "Contatto aggiunto",
"discoveredContacts_deleteContact": "Elimina Contatto",
"discoveredContacts_copyContact": "Copia contatto negli appunti",
"contactsSettings_overwriteOldestSubtitle": "Quando l'elenco dei contatti è pieno, il contatto più vecchio non tra i preferiti verrà sostituito.",
"common_deleteAll": "Elimina tutto",
"discoveredContacts_deleteContactAllContent": "Sei sicuro di voler eliminare tutti i contatti scoperti?",
"discoveredContacts_deleteContactAll": "Eliminare tutti i contatti scoperti"
}

View file

@ -184,6 +184,12 @@ abstract class AppLocalizations {
/// **'Delete'**
String get common_delete;
/// No description provided for @common_deleteAll.
///
/// In en, this message translates to:
/// **'Delete All'**
String get common_deleteAll;
/// No description provided for @common_close.
///
/// In en, this message translates to:
@ -556,6 +562,18 @@ abstract class AppLocalizations {
/// **'Longitude'**
String get settings_longitude;
/// No description provided for @settings_contactSettings.
///
/// In en, this message translates to:
/// **'Contact Settings'**
String get settings_contactSettings;
/// No description provided for @settings_contactSettingsSubtitle.
///
/// In en, this message translates to:
/// **'Settings for how contacts are added.'**
String get settings_contactSettingsSubtitle;
/// No description provided for @settings_privacyMode.
///
/// In en, this message translates to:
@ -5392,6 +5410,138 @@ abstract class AppLocalizations {
/// In en, this message translates to:
/// **'Last seen'**
String get snrIndicator_lastSeen;
/// No description provided for @contactsSettings_title.
///
/// In en, this message translates to:
/// **'Contacts settings'**
String get contactsSettings_title;
/// No description provided for @contactsSettings_autoAddTitle.
///
/// In en, this message translates to:
/// **'Automatic Discovery'**
String get contactsSettings_autoAddTitle;
/// No description provided for @contactsSettings_otherTitle.
///
/// In en, this message translates to:
/// **'Other contact related settings'**
String get contactsSettings_otherTitle;
/// No description provided for @contactsSettings_autoAddUsersTitle.
///
/// In en, this message translates to:
/// **'Auto-add users'**
String get contactsSettings_autoAddUsersTitle;
/// No description provided for @contactsSettings_autoAddUsersSubtitle.
///
/// In en, this message translates to:
/// **'Allow the companion to automatically add discovered users.'**
String get contactsSettings_autoAddUsersSubtitle;
/// No description provided for @contactsSettings_autoAddRepeatersTitle.
///
/// In en, this message translates to:
/// **'Auto-add repeaters'**
String get contactsSettings_autoAddRepeatersTitle;
/// No description provided for @contactsSettings_autoAddRepeatersSubtitle.
///
/// In en, this message translates to:
/// **'Allow the companion to automatically add discovered repeaters.'**
String get contactsSettings_autoAddRepeatersSubtitle;
/// No description provided for @contactsSettings_autoAddRoomServersTitle.
///
/// In en, this message translates to:
/// **'Auto-add room servers'**
String get contactsSettings_autoAddRoomServersTitle;
/// No description provided for @contactsSettings_autoAddRoomServersSubtitle.
///
/// In en, this message translates to:
/// **'Allow the companion to automatically add discovered room servers.'**
String get contactsSettings_autoAddRoomServersSubtitle;
/// No description provided for @contactsSettings_autoAddSensorsTitle.
///
/// In en, this message translates to:
/// **'Auto-add sensors'**
String get contactsSettings_autoAddSensorsTitle;
/// No description provided for @contactsSettings_autoAddSensorsSubtitle.
///
/// In en, this message translates to:
/// **'Allow the companion to automatically add discovered sensors.'**
String get contactsSettings_autoAddSensorsSubtitle;
/// No description provided for @contactsSettings_overwriteOldestTitle.
///
/// In en, this message translates to:
/// **'Overwrite Oldest'**
String get contactsSettings_overwriteOldestTitle;
/// No description provided for @contactsSettings_overwriteOldestSubtitle.
///
/// In en, this message translates to:
/// **'When the contact list is full, the oldest non-favorited contact will be replaced.'**
String get contactsSettings_overwriteOldestSubtitle;
/// No description provided for @discoveredContacts_Title.
///
/// In en, this message translates to:
/// **'Discovered Contacts'**
String get discoveredContacts_Title;
/// No description provided for @discoveredContacts_noMatching.
///
/// In en, this message translates to:
/// **'No matching contacts'**
String get discoveredContacts_noMatching;
/// No description provided for @discoveredContacts_searchHint.
///
/// In en, this message translates to:
/// **'Search discovered contacts'**
String get discoveredContacts_searchHint;
/// No description provided for @discoveredContacts_contactAdded.
///
/// In en, this message translates to:
/// **'Contact added'**
String get discoveredContacts_contactAdded;
/// No description provided for @discoveredContacts_addContact.
///
/// In en, this message translates to:
/// **'Add Contact'**
String get discoveredContacts_addContact;
/// No description provided for @discoveredContacts_copyContact.
///
/// In en, this message translates to:
/// **'Copy Contact to clipboard'**
String get discoveredContacts_copyContact;
/// No description provided for @discoveredContacts_deleteContact.
///
/// In en, this message translates to:
/// **'Delete Discovered Contact'**
String get discoveredContacts_deleteContact;
/// No description provided for @discoveredContacts_deleteContactAll.
///
/// In en, this message translates to:
/// **'Delete All Discovered Contacts'**
String get discoveredContacts_deleteContactAll;
/// No description provided for @discoveredContacts_deleteContactAllContent.
///
/// In en, this message translates to:
/// **'Are you sure you want to delete all discovered contacts?'**
String get discoveredContacts_deleteContactAllContent;
}
class _AppLocalizationsDelegate

View file

@ -38,6 +38,9 @@ class AppLocalizationsBg extends AppLocalizations {
@override
String get common_delete => 'Изтрий';
@override
String get common_deleteAll => 'Изтрий всичко';
@override
String get common_close => 'Затвори';
@ -241,6 +244,13 @@ class AppLocalizationsBg extends AppLocalizations {
@override
String get settings_longitude => 'Дължина';
@override
String get settings_contactSettings => 'Настройки за контакти';
@override
String get settings_contactSettingsSubtitle =>
'Настройки за добавяне на контакти.';
@override
String get settings_privacyMode => 'Режим на поверителност';
@ -3119,4 +3129,82 @@ class AppLocalizationsBg extends AppLocalizations {
@override
String get snrIndicator_lastSeen => 'Последно видян';
@override
String get contactsSettings_title => 'Настройки на контактите';
@override
String get contactsSettings_autoAddTitle => 'Автоматично откриване';
@override
String get contactsSettings_otherTitle =>
'Други настройки свързани с контакти';
@override
String get contactsSettings_autoAddUsersTitle =>
'Автоматично добавяне на потребители';
@override
String get contactsSettings_autoAddUsersSubtitle =>
'Позволи на спътника да добавя автоматично откритите потребители.';
@override
String get contactsSettings_autoAddRepeatersTitle =>
'Автоматично добавяне на повтарящи се елементи';
@override
String get contactsSettings_autoAddRepeatersSubtitle =>
'Позволи на спътника да добавя автоматично откритите повтарящи се устройства.';
@override
String get contactsSettings_autoAddRoomServersTitle =>
'Автоматично добавяне на сървъри на стаите';
@override
String get contactsSettings_autoAddRoomServersSubtitle =>
'Позволи на спътника да добавя автоматично откритите сървъри на стаите.';
@override
String get contactsSettings_autoAddSensorsTitle =>
'Автоматично добавяне на датчици';
@override
String get contactsSettings_autoAddSensorsSubtitle =>
'Позволи на спътника да добавя автоматично откритите датчици.';
@override
String get contactsSettings_overwriteOldestTitle => 'Премахни най-старото';
@override
String get contactsSettings_overwriteOldestSubtitle =>
'Когато списъкът с контакти е пълен, най-старият неключов контакт ще бъде заменен.';
@override
String get discoveredContacts_Title => 'Открити контакти';
@override
String get discoveredContacts_noMatching => 'Няма съвпадащи контакти';
@override
String get discoveredContacts_searchHint => 'Търсене на открити контакти';
@override
String get discoveredContacts_contactAdded => 'Контакт добавен';
@override
String get discoveredContacts_addContact => 'Добави контакт';
@override
String get discoveredContacts_copyContact => 'Копирай контакт в клипборда';
@override
String get discoveredContacts_deleteContact => 'Изтрий контакт';
@override
String get discoveredContacts_deleteContactAll =>
'Изтриване на Всички Открити Контакти';
@override
String get discoveredContacts_deleteContactAllContent =>
'Сигурни ли сте, че искате да изтриете всички открити контакти?';
}

View file

@ -38,6 +38,9 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get common_delete => 'Löschen';
@override
String get common_deleteAll => 'Alles löschen';
@override
String get common_close => 'Schließen';
@ -240,6 +243,13 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get settings_longitude => 'Längengrad';
@override
String get settings_contactSettings => 'Kontakteinstellungen';
@override
String get settings_contactSettingsSubtitle =>
'Einstellungen für das Hinzufügen von Kontakten';
@override
String get settings_privacyMode => 'Privatsphäreeinstellung';
@ -3128,4 +3138,84 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get snrIndicator_lastSeen => 'Zuletzt gesehen';
@override
String get contactsSettings_title => 'Kontakteinstellungen';
@override
String get contactsSettings_autoAddTitle => 'Automatische Erkennung';
@override
String get contactsSettings_otherTitle =>
'Weitere Einstellungen zu Kontakten';
@override
String get contactsSettings_autoAddUsersTitle =>
'Automatische Hinzufügung von Benutzern';
@override
String get contactsSettings_autoAddUsersSubtitle =>
'Ermöglichen Sie dem Begleiter, automatisch entdeckte Benutzer hinzuzufügen';
@override
String get contactsSettings_autoAddRepeatersTitle =>
'Automatisch Repeater hinzufügen';
@override
String get contactsSettings_autoAddRepeatersSubtitle =>
'Ermöglichen Sie dem Begleiter, automatisch entdeckte Repeater hinzuzufügen.';
@override
String get contactsSettings_autoAddRoomServersTitle =>
'Automatisch Raumservers hinzufügen';
@override
String get contactsSettings_autoAddRoomServersSubtitle =>
'Ermöglichen Sie dem Begleiter, entdeckte Raumserver automatisch hinzuzufügen';
@override
String get contactsSettings_autoAddSensorsTitle =>
'Automatisch Sensoren hinzufügen';
@override
String get contactsSettings_autoAddSensorsSubtitle =>
'Ermöglichen Sie dem Begleiter, automatisch entdeckte Sensoren hinzuzufügen';
@override
String get contactsSettings_overwriteOldestTitle =>
'Überschreiben des Ältesten';
@override
String get contactsSettings_overwriteOldestSubtitle =>
'Wenn die Kontaktliste voll ist, wird der älteste nicht favorisierte Kontakt ersetzt.';
@override
String get discoveredContacts_Title => 'Entdeckte Kontakte';
@override
String get discoveredContacts_noMatching => 'Keine passenden Kontakte';
@override
String get discoveredContacts_searchHint => 'Entdeckte Kontakte suchen';
@override
String get discoveredContacts_contactAdded => 'Kontakt hinzugefügt';
@override
String get discoveredContacts_addContact => 'Kontakt hinzufügen';
@override
String get discoveredContacts_copyContact =>
'Kontakt in die Zwischenablage kopieren';
@override
String get discoveredContacts_deleteContact => 'Kontakt löschen';
@override
String get discoveredContacts_deleteContactAll =>
'Alle entdeckten Kontakte löschen';
@override
String get discoveredContacts_deleteContactAllContent =>
'Sind Sie sicher, dass Sie alle gefundenen Kontakte löschen möchten?';
}

View file

@ -38,6 +38,9 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get common_delete => 'Delete';
@override
String get common_deleteAll => 'Delete All';
@override
String get common_close => 'Close';
@ -239,6 +242,13 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get settings_longitude => 'Longitude';
@override
String get settings_contactSettings => 'Contact Settings';
@override
String get settings_contactSettingsSubtitle =>
'Settings for how contacts are added.';
@override
String get settings_privacyMode => 'Privacy Mode';
@ -3072,4 +3082,78 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get snrIndicator_lastSeen => 'Last seen';
@override
String get contactsSettings_title => 'Contacts settings';
@override
String get contactsSettings_autoAddTitle => 'Automatic Discovery';
@override
String get contactsSettings_otherTitle => 'Other contact related settings';
@override
String get contactsSettings_autoAddUsersTitle => 'Auto-add users';
@override
String get contactsSettings_autoAddUsersSubtitle =>
'Allow the companion to automatically add discovered users.';
@override
String get contactsSettings_autoAddRepeatersTitle => 'Auto-add repeaters';
@override
String get contactsSettings_autoAddRepeatersSubtitle =>
'Allow the companion to automatically add discovered repeaters.';
@override
String get contactsSettings_autoAddRoomServersTitle =>
'Auto-add room servers';
@override
String get contactsSettings_autoAddRoomServersSubtitle =>
'Allow the companion to automatically add discovered room servers.';
@override
String get contactsSettings_autoAddSensorsTitle => 'Auto-add sensors';
@override
String get contactsSettings_autoAddSensorsSubtitle =>
'Allow the companion to automatically add discovered sensors.';
@override
String get contactsSettings_overwriteOldestTitle => 'Overwrite Oldest';
@override
String get contactsSettings_overwriteOldestSubtitle =>
'When the contact list is full, the oldest non-favorited contact will be replaced.';
@override
String get discoveredContacts_Title => 'Discovered Contacts';
@override
String get discoveredContacts_noMatching => 'No matching contacts';
@override
String get discoveredContacts_searchHint => 'Search discovered contacts';
@override
String get discoveredContacts_contactAdded => 'Contact added';
@override
String get discoveredContacts_addContact => 'Add Contact';
@override
String get discoveredContacts_copyContact => 'Copy Contact to clipboard';
@override
String get discoveredContacts_deleteContact => 'Delete Discovered Contact';
@override
String get discoveredContacts_deleteContactAll =>
'Delete All Discovered Contacts';
@override
String get discoveredContacts_deleteContactAllContent =>
'Are you sure you want to delete all discovered contacts?';
}

View file

@ -38,6 +38,9 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get common_delete => 'Eliminar';
@override
String get common_deleteAll => 'Eliminar todo';
@override
String get common_close => 'Cerrar';
@ -240,6 +243,13 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get settings_longitude => 'Longitud';
@override
String get settings_contactSettings => 'Configuración de contacto';
@override
String get settings_contactSettingsSubtitle =>
'Configuración de cómo se agregan los contactos.';
@override
String get settings_privacyMode => 'Modo Privacidad';
@ -3120,4 +3130,85 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get snrIndicator_lastSeen => 'Visto por última vez';
@override
String get contactsSettings_title => 'Configuración de contactos';
@override
String get contactsSettings_autoAddTitle => 'Detección automática';
@override
String get contactsSettings_otherTitle =>
'Otras configuraciones relacionadas con el contacto';
@override
String get contactsSettings_autoAddUsersTitle =>
'Agregar usuarios automáticamente';
@override
String get contactsSettings_autoAddUsersSubtitle =>
'Permitir que el compañero agregue automáticamente a los usuarios descubiertos.';
@override
String get contactsSettings_autoAddRepeatersTitle =>
'Agregar repetidores automáticamente';
@override
String get contactsSettings_autoAddRepeatersSubtitle =>
'Permitir que el compañero agregue automáticamente los repetidores descubiertos.';
@override
String get contactsSettings_autoAddRoomServersTitle =>
'Agregar automáticamente servidores de sala';
@override
String get contactsSettings_autoAddRoomServersSubtitle =>
'Permitir que el compañero agregue automáticamente los servidores de salas descubiertos.';
@override
String get contactsSettings_autoAddSensorsTitle =>
'Agregar sensores automáticamente';
@override
String get contactsSettings_autoAddSensorsSubtitle =>
'Permitir que el compañero agregue automáticamente los sensores descubiertos.';
@override
String get contactsSettings_overwriteOldestTitle =>
'Sobreescribir el más antiguo';
@override
String get contactsSettings_overwriteOldestSubtitle =>
'Cuando la lista de contactos esté llena, se reemplazará el contacto no favorito más antiguo.';
@override
String get discoveredContacts_Title => 'Contactos descubiertos';
@override
String get discoveredContacts_noMatching =>
'No se encontraron contactos coincidentes';
@override
String get discoveredContacts_searchHint => 'Buscar contactos descubiertos';
@override
String get discoveredContacts_contactAdded => 'Contacto agregado';
@override
String get discoveredContacts_addContact => 'Agregar contacto';
@override
String get discoveredContacts_copyContact =>
'Copiar contacto al portapapeles';
@override
String get discoveredContacts_deleteContact => 'Eliminar contacto';
@override
String get discoveredContacts_deleteContactAll =>
'Eliminar Todos los Contactos Descubiertos';
@override
String get discoveredContacts_deleteContactAllContent =>
'¿Está seguro de que desea eliminar todos los contactos descubiertos!';
}

View file

@ -38,6 +38,9 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get common_delete => 'Supprimer';
@override
String get common_deleteAll => 'Supprimer tout';
@override
String get common_close => 'Fermer';
@ -241,6 +244,13 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get settings_longitude => 'Longitude';
@override
String get settings_contactSettings => 'Paramètres de contact';
@override
String get settings_contactSettingsSubtitle =>
'Paramètres pour l\'ajout de contacts';
@override
String get settings_privacyMode => 'Mode de confidentialité';
@ -3142,4 +3152,84 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get snrIndicator_lastSeen => 'Dernière fois vu';
@override
String get contactsSettings_title => 'Paramètres des contacts';
@override
String get contactsSettings_autoAddTitle => 'Découverte automatique';
@override
String get contactsSettings_otherTitle =>
'Autres paramètres liés aux contacts';
@override
String get contactsSettings_autoAddUsersTitle =>
'Ajouter automatiquement les utilisateurs';
@override
String get contactsSettings_autoAddUsersSubtitle =>
'Autoriser le compagnon à ajouter automatiquement les utilisateurs découverts';
@override
String get contactsSettings_autoAddRepeatersTitle =>
'Ajouter automatiquement les répéteurs';
@override
String get contactsSettings_autoAddRepeatersSubtitle =>
'Autoriser le compagnon à ajouter automatiquement les répéteurs découverts';
@override
String get contactsSettings_autoAddRoomServersTitle =>
'Ajouter automatiquement les serveurs de salle';
@override
String get contactsSettings_autoAddRoomServersSubtitle =>
'Autoriser le compagnon à ajouter automatiquement les serveurs de salles découverts';
@override
String get contactsSettings_autoAddSensorsTitle =>
'Ajouter automatiquement les capteurs';
@override
String get contactsSettings_autoAddSensorsSubtitle =>
'Autoriser le compagnon à ajouter automatiquement les capteurs découverts.';
@override
String get contactsSettings_overwriteOldestTitle => 'Écraser le plus ancien';
@override
String get contactsSettings_overwriteOldestSubtitle =>
'Lorsque la liste de contacts est pleine, le contact le plus ancien non favori sera remplacé.';
@override
String get discoveredContacts_Title => 'Contacts découverts';
@override
String get discoveredContacts_noMatching => 'Aucun contact correspondant';
@override
String get discoveredContacts_searchHint =>
'Rechercher des contacts découverts';
@override
String get discoveredContacts_contactAdded => 'Contact ajouté';
@override
String get discoveredContacts_addContact => 'Ajouter un contact';
@override
String get discoveredContacts_copyContact =>
'Copier le contact dans le presse-papiers';
@override
String get discoveredContacts_deleteContact => 'Supprimer le contact';
@override
String get discoveredContacts_deleteContactAll =>
'Supprimer tous les contacts découverts';
@override
String get discoveredContacts_deleteContactAllContent =>
'Êtes-vous sûr de vouloir supprimer tous les contacts découverts ?';
}

View file

@ -38,6 +38,9 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get common_delete => 'Elimina';
@override
String get common_deleteAll => 'Elimina tutto';
@override
String get common_close => 'Chiudi';
@ -240,6 +243,13 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get settings_longitude => 'Longitudine';
@override
String get settings_contactSettings => 'Impostazioni di contatto';
@override
String get settings_contactSettingsSubtitle =>
'Impostazioni per l\'aggiunta dei contatti';
@override
String get settings_privacyMode => 'Modalità Privacy';
@ -3123,4 +3133,83 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get snrIndicator_lastSeen => 'Ultimo accesso';
@override
String get contactsSettings_title => 'Impostazioni dei contatti';
@override
String get contactsSettings_autoAddTitle => 'Scoperta automatica';
@override
String get contactsSettings_otherTitle =>
'Altre impostazioni relative ai contatti';
@override
String get contactsSettings_autoAddUsersTitle =>
'Aggiungere utenti automaticamente';
@override
String get contactsSettings_autoAddUsersSubtitle =>
'Consenti al compagno di aggiungere automaticamente gli utenti scoperti.';
@override
String get contactsSettings_autoAddRepeatersTitle =>
'Aggiungere ripetitori automaticamente';
@override
String get contactsSettings_autoAddRepeatersSubtitle =>
'Consenti al compagno di aggiungere automaticamente i ripetitori scoperti.';
@override
String get contactsSettings_autoAddRoomServersTitle =>
'Aggiungere automaticamente i server delle stanze';
@override
String get contactsSettings_autoAddRoomServersSubtitle =>
'Consenti al compagno di aggiungere automaticamente i server delle stanze scoperte.';
@override
String get contactsSettings_autoAddSensorsTitle =>
'Aggiungere automaticamente i sensori';
@override
String get contactsSettings_autoAddSensorsSubtitle =>
'Consenti al compagno di aggiungere automaticamente i sensori scoperti';
@override
String get contactsSettings_overwriteOldestTitle =>
'Sostituisci il più vecchio';
@override
String get contactsSettings_overwriteOldestSubtitle =>
'Quando l\'elenco dei contatti è pieno, il contatto più vecchio non tra i preferiti verrà sostituito.';
@override
String get discoveredContacts_Title => 'Contatti scoperti';
@override
String get discoveredContacts_noMatching => 'Nessun contatto corrispondente';
@override
String get discoveredContacts_searchHint => 'Cerca contatti scoperti';
@override
String get discoveredContacts_contactAdded => 'Contatto aggiunto';
@override
String get discoveredContacts_addContact => 'Aggiungi contatto';
@override
String get discoveredContacts_copyContact => 'Copia contatto negli appunti';
@override
String get discoveredContacts_deleteContact => 'Elimina Contatto';
@override
String get discoveredContacts_deleteContactAll =>
'Eliminare tutti i contatti scoperti';
@override
String get discoveredContacts_deleteContactAllContent =>
'Sei sicuro di voler eliminare tutti i contatti scoperti?';
}

View file

@ -38,6 +38,9 @@ class AppLocalizationsNl extends AppLocalizations {
@override
String get common_delete => 'Verwijderen';
@override
String get common_deleteAll => 'Alles verwijderen';
@override
String get common_close => 'Sluiten';
@ -240,6 +243,13 @@ class AppLocalizationsNl extends AppLocalizations {
@override
String get settings_longitude => 'Lengtegraad';
@override
String get settings_contactSettings => 'Contactinstellingen';
@override
String get settings_contactSettingsSubtitle =>
'Instellingen voor het toevoegen van contacten';
@override
String get settings_privacyMode => 'Privacy Mode';
@ -3110,4 +3120,82 @@ class AppLocalizationsNl extends AppLocalizations {
@override
String get snrIndicator_lastSeen => 'Laatst gezien';
@override
String get contactsSettings_title => 'Instellingen voor contacten';
@override
String get contactsSettings_autoAddTitle => 'Automatische detectie';
@override
String get contactsSettings_otherTitle =>
'Andere instellingen voor contactgerelateerde zaken';
@override
String get contactsSettings_autoAddUsersTitle =>
'Gebruikers automatisch toevoegen';
@override
String get contactsSettings_autoAddUsersSubtitle =>
'Sta toe dat de companion automatisch ontdekte gebruikers toevoegt';
@override
String get contactsSettings_autoAddRepeatersTitle =>
'Automatisch herhalingstoestellen toevoegen';
@override
String get contactsSettings_autoAddRepeatersSubtitle =>
'Sta toe dat de companion automatisch ontdekte repeaters toevoegt';
@override
String get contactsSettings_autoAddRoomServersTitle =>
'Automatisch kamerservers toevoegen';
@override
String get contactsSettings_autoAddRoomServersSubtitle =>
'Sta toe dat de companion automatisch ontdekte kamer servers toevoegt.';
@override
String get contactsSettings_autoAddSensorsTitle =>
'Automatisch sensoren toevoegen';
@override
String get contactsSettings_autoAddSensorsSubtitle =>
'Sta toe dat de companion automatisch ontdekte sensoren toevoegt';
@override
String get contactsSettings_overwriteOldestTitle => 'Overschrijf Oudste';
@override
String get contactsSettings_overwriteOldestSubtitle =>
'Wanneer de contactenlijst vol is, wordt de oudste niet-favoriete contactpersoon vervangen.';
@override
String get discoveredContacts_Title => 'Ontdekte contacten';
@override
String get discoveredContacts_noMatching => 'Geen overeenkomende contacten';
@override
String get discoveredContacts_searchHint => 'Ontdekte contacten zoeken';
@override
String get discoveredContacts_contactAdded => 'Contact toegevoegd';
@override
String get discoveredContacts_addContact => 'Contact toevoegen';
@override
String get discoveredContacts_copyContact => 'Kopieer contact naar klembord';
@override
String get discoveredContacts_deleteContact => 'Contact verwijderen';
@override
String get discoveredContacts_deleteContactAll =>
'Verwijder alle ontdekte contacten';
@override
String get discoveredContacts_deleteContactAllContent =>
'Weet u zeker dat u alle ontdekte contacten wilt verwijderen?';
}

View file

@ -38,6 +38,9 @@ class AppLocalizationsPl extends AppLocalizations {
@override
String get common_delete => 'Usuń';
@override
String get common_deleteAll => 'Usuń wszystko';
@override
String get common_close => 'Zamknąć';
@ -242,6 +245,13 @@ class AppLocalizationsPl extends AppLocalizations {
@override
String get settings_longitude => 'Długość';
@override
String get settings_contactSettings => 'Ustawienia kontaktowe';
@override
String get settings_contactSettingsSubtitle =>
'Ustawienia dotyczące sposobu dodawania kontaktów';
@override
String get settings_privacyMode => 'Tryb Prywatny';
@ -3123,4 +3133,82 @@ class AppLocalizationsPl extends AppLocalizations {
@override
String get snrIndicator_lastSeen => 'Ostatnio widziany';
@override
String get contactsSettings_title => 'Ustawienia kontaktów';
@override
String get contactsSettings_autoAddTitle => 'Automatyczne odnajdywanie';
@override
String get contactsSettings_otherTitle =>
'Inne ustawienia związane z kontaktami';
@override
String get contactsSettings_autoAddUsersTitle =>
'Automatycznie dodaj użytkowników';
@override
String get contactsSettings_autoAddUsersSubtitle =>
'Pozwól towarzyszowi automatycznie dodawać znalezione użytkowników.';
@override
String get contactsSettings_autoAddRepeatersTitle =>
'Automatyczne dodawanie powtarzalników';
@override
String get contactsSettings_autoAddRepeatersSubtitle =>
'Zezwól na automatyczne dodawanie odkrytych repeaterów przez towarzysza.';
@override
String get contactsSettings_autoAddRoomServersTitle =>
'Automatycznie dodaj serwery pokojowe';
@override
String get contactsSettings_autoAddRoomServersSubtitle =>
'Zezwól towarzyszowi na automatyczne dodawanie znalezionych serwerów pokojowych.';
@override
String get contactsSettings_autoAddSensorsTitle =>
'Automatycznie dodaj czujniki';
@override
String get contactsSettings_autoAddSensorsSubtitle =>
'Zezwól towarzyszowi na automatyczne dodawanie wykrytych czujników.';
@override
String get contactsSettings_overwriteOldestTitle => 'Nadpisz najstarszy';
@override
String get contactsSettings_overwriteOldestSubtitle =>
'Gdy lista kontaktów jest pełna, najstarszy nieulubiony kontakt zostanie zastąpiony.';
@override
String get discoveredContacts_Title => 'Odkryte Kontakty';
@override
String get discoveredContacts_noMatching => 'Brak pasujących kontaktów';
@override
String get discoveredContacts_searchHint => 'Wyszukaj odkryte kontakty';
@override
String get discoveredContacts_contactAdded => 'Kontakt dodany';
@override
String get discoveredContacts_addContact => 'Dodaj kontakt';
@override
String get discoveredContacts_copyContact => 'Kopiuj kontakt do schowka';
@override
String get discoveredContacts_deleteContact => 'Usuń kontakt';
@override
String get discoveredContacts_deleteContactAll =>
'Usuń wszystkie odkryte kontakty';
@override
String get discoveredContacts_deleteContactAllContent =>
'Czy na pewno chcesz usunąć wszystkie znalezione kontakty?';
}

View file

@ -38,6 +38,9 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get common_delete => 'Excluir';
@override
String get common_deleteAll => 'Excluir Tudo';
@override
String get common_close => 'Fechar';
@ -241,6 +244,13 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get settings_longitude => 'Longitude';
@override
String get settings_contactSettings => 'Configurações de Contato';
@override
String get settings_contactSettingsSubtitle =>
'Configurações para como os contatos são adicionados';
@override
String get settings_privacyMode => 'Modo de Privacidade';
@ -3118,4 +3128,84 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get snrIndicator_lastSeen => 'Visto pela última vez';
@override
String get contactsSettings_title => 'Configurações de contatos';
@override
String get contactsSettings_autoAddTitle => 'Descoberta Automática';
@override
String get contactsSettings_otherTitle =>
'Outras configurações relacionadas a contatos';
@override
String get contactsSettings_autoAddUsersTitle =>
'Adicionar usuários automaticamente';
@override
String get contactsSettings_autoAddUsersSubtitle =>
'Permitir que o companheiro adicione automaticamente os usuários descobertos.';
@override
String get contactsSettings_autoAddRepeatersTitle =>
'Adicionar repetidores automaticamente';
@override
String get contactsSettings_autoAddRepeatersSubtitle =>
'Permitir que o companheiro adicione automaticamente os repetidores descobertos.';
@override
String get contactsSettings_autoAddRoomServersTitle =>
'Adicionar automaticamente servidores de sala';
@override
String get contactsSettings_autoAddRoomServersSubtitle =>
'Permitir que o companheiro adicione automaticamente os servidores de salas descobertos.';
@override
String get contactsSettings_autoAddSensorsTitle =>
'Adicionar sensores automaticamente';
@override
String get contactsSettings_autoAddSensorsSubtitle =>
'Permitir que o companheiro adicione automaticamente sensores descobertos.';
@override
String get contactsSettings_overwriteOldestTitle =>
'Sobrescrever o Mais Antigo';
@override
String get contactsSettings_overwriteOldestSubtitle =>
'Quando a lista de contatos estiver cheia, o contato mais antigo não favoritado será substituído.';
@override
String get discoveredContacts_Title => 'Contatos Descobertos';
@override
String get discoveredContacts_noMatching => 'Nenhum contato correspondente';
@override
String get discoveredContacts_searchHint => 'Pesquisar contatos descobertos';
@override
String get discoveredContacts_contactAdded => 'Contato adicionado';
@override
String get discoveredContacts_addContact => 'Adicionar Contato';
@override
String get discoveredContacts_copyContact =>
'Copiar Contato para a área de transferência';
@override
String get discoveredContacts_deleteContact => 'Excluir Contato';
@override
String get discoveredContacts_deleteContactAll =>
'Excluir Todos os Contatos Descobertos';
@override
String get discoveredContacts_deleteContactAllContent =>
'Tem certeza de que deseja excluir todos os contatos descobertos?';
}

View file

@ -38,6 +38,9 @@ class AppLocalizationsRu extends AppLocalizations {
@override
String get common_delete => 'Удалить';
@override
String get common_deleteAll => 'Удалить все';
@override
String get common_close => 'Закрыть';
@ -239,6 +242,13 @@ class AppLocalizationsRu extends AppLocalizations {
@override
String get settings_longitude => 'Долгота';
@override
String get settings_contactSettings => 'Настройки контактов';
@override
String get settings_contactSettingsSubtitle =>
'Настройки добавления контактов';
@override
String get settings_privacyMode => 'Режим конфиденциальности';
@ -3130,4 +3140,84 @@ class AppLocalizationsRu extends AppLocalizations {
@override
String get snrIndicator_lastSeen => 'Последний раз видели';
@override
String get contactsSettings_title => 'Настройки контактов';
@override
String get contactsSettings_autoAddTitle => 'Автоматическое обнаружение';
@override
String get contactsSettings_otherTitle =>
'Другие настройки, связанные с контактами';
@override
String get contactsSettings_autoAddUsersTitle =>
'Автоматически добавлять пользователей';
@override
String get contactsSettings_autoAddUsersSubtitle =>
'Разрешить компаньону автоматически добавлять обнаруженных пользователей';
@override
String get contactsSettings_autoAddRepeatersTitle =>
'Автоматически добавлять ретрансляторы';
@override
String get contactsSettings_autoAddRepeatersSubtitle =>
'Разрешить спутнику автоматически добавлять обнаруженные ретрансляторы';
@override
String get contactsSettings_autoAddRoomServersTitle =>
'Автоматически добавлять серверы комнат';
@override
String get contactsSettings_autoAddRoomServersSubtitle =>
'Разрешить компаньону автоматически добавлять обнаруженные сервера комнат.';
@override
String get contactsSettings_autoAddSensorsTitle =>
'Автоматически добавлять датчики';
@override
String get contactsSettings_autoAddSensorsSubtitle =>
'Разрешить компаньону автоматически добавлять обнаруженные датчики';
@override
String get contactsSettings_overwriteOldestTitle =>
'Перезаписать самое старое';
@override
String get contactsSettings_overwriteOldestSubtitle =>
'Когда список контактов заполнен, будет заменен самый старый контакт, который не находится в избранном.';
@override
String get discoveredContacts_Title => 'Обнаруженные контакты';
@override
String get discoveredContacts_noMatching => 'Нет совпадающих контактов';
@override
String get discoveredContacts_searchHint => 'Найденные контакты поиска';
@override
String get discoveredContacts_contactAdded => 'Контакт добавлен';
@override
String get discoveredContacts_addContact => 'Добавить контакт';
@override
String get discoveredContacts_copyContact =>
'Копировать контакт в буфер обмена';
@override
String get discoveredContacts_deleteContact => 'Удалить контакт';
@override
String get discoveredContacts_deleteContactAll =>
'Удалить Все Обнаруженные Контакты';
@override
String get discoveredContacts_deleteContactAllContent =>
'Вы уверены, что хотите удалить все обнаруженные контакты?';
}

View file

@ -38,6 +38,9 @@ class AppLocalizationsSk extends AppLocalizations {
@override
String get common_delete => 'Odstrániť';
@override
String get common_deleteAll => 'Zmazať všetko';
@override
String get common_close => 'Zavrieť';
@ -240,6 +243,13 @@ class AppLocalizationsSk extends AppLocalizations {
@override
String get settings_longitude => 'Dĺžka';
@override
String get settings_contactSettings => 'Nastavenia kontaktov';
@override
String get settings_contactSettingsSubtitle =>
'Nastavenia pre pridávanie kontaktov.';
@override
String get settings_privacyMode => 'Režim ochrany súkromia';
@ -3105,4 +3115,82 @@ class AppLocalizationsSk extends AppLocalizations {
@override
String get snrIndicator_lastSeen => 'Naposledy videný';
@override
String get contactsSettings_title => 'Nastavenia kontaktov';
@override
String get contactsSettings_autoAddTitle => 'Automatické zisťovanie';
@override
String get contactsSettings_otherTitle =>
'Ďalšie nastavenia súvisiace s kontaktami';
@override
String get contactsSettings_autoAddUsersTitle =>
'Automaticky pridávať užívateľov';
@override
String get contactsSettings_autoAddUsersSubtitle =>
'Povoliť spoločníkovi automaticky pridávať objavených užívateľov.';
@override
String get contactsSettings_autoAddRepeatersTitle =>
'Automaticky pridávať opakovače';
@override
String get contactsSettings_autoAddRepeatersSubtitle =>
'Povoliť spoločníkovi automaticky pridávať objavené repeater.';
@override
String get contactsSettings_autoAddRoomServersTitle =>
'Automaticky pridávať server miestnosti';
@override
String get contactsSettings_autoAddRoomServersSubtitle =>
'Povoliť spoločníkovi automaticky pridať objavené serverové miestnosti.';
@override
String get contactsSettings_autoAddSensorsTitle =>
'Automaticky pridávať senzory';
@override
String get contactsSettings_autoAddSensorsSubtitle =>
'Povoliť spoločníkovi automaticky pridávať objavené senzory.';
@override
String get contactsSettings_overwriteOldestTitle => 'Prepísať najstaršie';
@override
String get contactsSettings_overwriteOldestSubtitle =>
'Keď je zoznam kontaktov plný, bude nahradený najstarší neoznačený kontakt.';
@override
String get discoveredContacts_Title => 'Objavené kontakty';
@override
String get discoveredContacts_noMatching => 'Žiadne zhodné kontakty';
@override
String get discoveredContacts_searchHint => 'Vyhľadať objavené kontakty';
@override
String get discoveredContacts_contactAdded => 'Kontakt bol pridaný';
@override
String get discoveredContacts_addContact => 'Pridať kontakt';
@override
String get discoveredContacts_copyContact => 'Kopírovať kontakt do schránky';
@override
String get discoveredContacts_deleteContact => 'Zmazať kontakt';
@override
String get discoveredContacts_deleteContactAll =>
'Zmazať všetky objavené kontakty';
@override
String get discoveredContacts_deleteContactAllContent =>
'Ste si istí, že chcete zmazať všetky objavené kontakty?';
}

View file

@ -38,6 +38,9 @@ class AppLocalizationsSl extends AppLocalizations {
@override
String get common_delete => 'Izbrisati';
@override
String get common_deleteAll => 'Izbriši vse';
@override
String get common_close => 'Zapri';
@ -240,6 +243,13 @@ class AppLocalizationsSl extends AppLocalizations {
@override
String get settings_longitude => 'Dolžina';
@override
String get settings_contactSettings => 'Nastavitve stika';
@override
String get settings_contactSettingsSubtitle =>
'Nastavitve za dodajanje stikov.';
@override
String get settings_privacyMode => 'Zasebnost';
@ -3110,4 +3120,81 @@ class AppLocalizationsSl extends AppLocalizations {
@override
String get snrIndicator_lastSeen => 'Zadnjič videno';
@override
String get contactsSettings_title => 'Nastavitve stikov';
@override
String get contactsSettings_autoAddTitle => 'Avtomatsko odkrivanje';
@override
String get contactsSettings_otherTitle => 'Druge nastavitve v zvezi s stiki';
@override
String get contactsSettings_autoAddUsersTitle =>
'Avtomatsko dodaj uporabnike';
@override
String get contactsSettings_autoAddUsersSubtitle =>
'Dovoli spremljevalcu, da samodejno doda odkrite uporabnike.';
@override
String get contactsSettings_autoAddRepeatersTitle =>
'Avtomatsko dodaj ponovitelje';
@override
String get contactsSettings_autoAddRepeatersSubtitle =>
'Dovoli spremljevalcu, da samodejno doda odkrite ponovitelje.';
@override
String get contactsSettings_autoAddRoomServersTitle =>
'Avtomatsko dodaj strežnike sob';
@override
String get contactsSettings_autoAddRoomServersSubtitle =>
'Dovoli spremljevalcu, da samodejno doda odkrite strežnike sob.';
@override
String get contactsSettings_autoAddSensorsTitle =>
'Avtomatsko dodaj senzorje';
@override
String get contactsSettings_autoAddSensorsSubtitle =>
'Dovoli spremljevalcu, da samodejno doda odkrite senzorje.';
@override
String get contactsSettings_overwriteOldestTitle => 'Prepiši najstarejše';
@override
String get contactsSettings_overwriteOldestSubtitle =>
'Ko je seznam stikov poln, bo najstarejši nestarševski stik zamenjan.';
@override
String get discoveredContacts_Title => 'Odkriti stiki';
@override
String get discoveredContacts_noMatching => 'Ni ujemajočih stikov';
@override
String get discoveredContacts_searchHint => 'Najdeni stiki po iskanju';
@override
String get discoveredContacts_contactAdded => 'Kontakt dodan';
@override
String get discoveredContacts_addContact => 'Dodaj stik';
@override
String get discoveredContacts_copyContact => 'Kopiraj stik v odložišče';
@override
String get discoveredContacts_deleteContact => 'Izbriši stik';
@override
String get discoveredContacts_deleteContactAll =>
'Izbriši vse odkrite kontakte';
@override
String get discoveredContacts_deleteContactAllContent =>
'Ste prepričani, da želite izbrisati vse odkrite kontakte?';
}

View file

@ -38,6 +38,9 @@ class AppLocalizationsSv extends AppLocalizations {
@override
String get common_delete => 'Radera';
@override
String get common_deleteAll => 'Ta bort alla';
@override
String get common_close => 'Stänga';
@ -239,6 +242,13 @@ class AppLocalizationsSv extends AppLocalizations {
@override
String get settings_longitude => 'Längdgrad';
@override
String get settings_contactSettings => 'Kontaktinställningar';
@override
String get settings_contactSettingsSubtitle =>
'Inställningar för hur kontakter läggs till.';
@override
String get settings_privacyMode => 'Privatläge';
@ -3088,4 +3098,82 @@ class AppLocalizationsSv extends AppLocalizations {
@override
String get snrIndicator_lastSeen => 'Senast sedd';
@override
String get contactsSettings_title => 'Kontaktinställningar';
@override
String get contactsSettings_autoAddTitle => 'Automatisk upptäckt';
@override
String get contactsSettings_otherTitle =>
'Andra inställningar relaterade till kontakt';
@override
String get contactsSettings_autoAddUsersTitle =>
'Lägg till användare automatiskt';
@override
String get contactsSettings_autoAddUsersSubtitle =>
'Tillåt kompanjonen att automatiskt lägga till upptäckta användare';
@override
String get contactsSettings_autoAddRepeatersTitle =>
'Lägg till upprepande enheter automatiskt';
@override
String get contactsSettings_autoAddRepeatersSubtitle =>
'Tillåt kompanjonen att automatiskt lägga till upptäckta repeater.';
@override
String get contactsSettings_autoAddRoomServersTitle =>
'Lägg automatiskt till rumsservrar';
@override
String get contactsSettings_autoAddRoomServersSubtitle =>
'Tillåt kompanjonen att automatiskt lägga till upptäckta rumsservrar.';
@override
String get contactsSettings_autoAddSensorsTitle =>
'Lägg till sensorer automatiskt';
@override
String get contactsSettings_autoAddSensorsSubtitle =>
'Tillåt kompanjonen att automatiskt lägga till upptäckta sensorer.';
@override
String get contactsSettings_overwriteOldestTitle => 'Skriv över äldst';
@override
String get contactsSettings_overwriteOldestSubtitle =>
'När kontaktlistan är full ersätts den äldsta icke-favoriterade kontakten.';
@override
String get discoveredContacts_Title => 'Upptäckta kontakter';
@override
String get discoveredContacts_noMatching => 'Inga matchande kontakter';
@override
String get discoveredContacts_searchHint => 'Sök uppfunna kontakter';
@override
String get discoveredContacts_contactAdded => 'Kontakt tillagd';
@override
String get discoveredContacts_addContact => 'Lägg till kontakt';
@override
String get discoveredContacts_copyContact => 'Kopiera kontakt till urklipp';
@override
String get discoveredContacts_deleteContact => 'Ta bort kontakt';
@override
String get discoveredContacts_deleteContactAll =>
'Ta bort alla upptäckta kontakter';
@override
String get discoveredContacts_deleteContactAllContent =>
'Är du säker på att du vill ta bort alla upptäckta kontakter?';
}

View file

@ -38,6 +38,9 @@ class AppLocalizationsUk extends AppLocalizations {
@override
String get common_delete => 'Видалити';
@override
String get common_deleteAll => 'Видалити все';
@override
String get common_close => 'Закрити';
@ -239,6 +242,13 @@ class AppLocalizationsUk extends AppLocalizations {
@override
String get settings_longitude => 'Довгота';
@override
String get settings_contactSettings => 'Налаштування контактів';
@override
String get settings_contactSettingsSubtitle =>
'Налаштування для додавання контактів';
@override
String get settings_privacyMode => 'Режим приватності';
@ -3137,4 +3147,84 @@ class AppLocalizationsUk extends AppLocalizations {
@override
String get snrIndicator_lastSeen => 'Останній раз бачили';
@override
String get contactsSettings_title => 'Налаштування контактів';
@override
String get contactsSettings_autoAddTitle => 'Автоматичне виявлення';
@override
String get contactsSettings_otherTitle =>
'Інші налаштування, пов\'язані з контактами';
@override
String get contactsSettings_autoAddUsersTitle =>
'Автоматично додавати користувачів';
@override
String get contactsSettings_autoAddUsersSubtitle =>
'Дозволити супутникові автоматично додавати виявлених користувачів';
@override
String get contactsSettings_autoAddRepeatersTitle =>
'Автоматично додавати повторювачі';
@override
String get contactsSettings_autoAddRepeatersSubtitle =>
'Дозволити супутнику автоматично додавати виявлені ретранслятори';
@override
String get contactsSettings_autoAddRoomServersTitle =>
'Автоматично додавати сервери кімнат';
@override
String get contactsSettings_autoAddRoomServersSubtitle =>
'Дозволити супровіднику автоматично додавати виявлені сервери кімнат.';
@override
String get contactsSettings_autoAddSensorsTitle =>
'Автоматично додавати датчики';
@override
String get contactsSettings_autoAddSensorsSubtitle =>
'Дозволити супровіднику автоматично додавати виявлені сенсори';
@override
String get contactsSettings_overwriteOldestTitle => 'Перезаписати найстаріше';
@override
String get contactsSettings_overwriteOldestSubtitle =>
'Коли список контактів заповнений, найстарший контакт без позначки улюбленого буде замінений.';
@override
String get discoveredContacts_Title => 'Виявлені контакти';
@override
String get discoveredContacts_noMatching =>
'Відповідних контактів не знайдено';
@override
String get discoveredContacts_searchHint => 'Знайти виявлені контакти';
@override
String get discoveredContacts_contactAdded => 'Контакт додано';
@override
String get discoveredContacts_addContact => 'Додати контакт';
@override
String get discoveredContacts_copyContact =>
'Копіювати контакт у буфер обміну';
@override
String get discoveredContacts_deleteContact => 'Видалити контакт';
@override
String get discoveredContacts_deleteContactAll =>
'Видалити всі виявлені контакти';
@override
String get discoveredContacts_deleteContactAllContent =>
'Ви впевнені, що хочете видалити всі виявлені контакти?';
}

View file

@ -38,6 +38,9 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get common_delete => '删除';
@override
String get common_deleteAll => '删除全部';
@override
String get common_close => '关闭';
@ -233,6 +236,12 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get settings_longitude => '经度';
@override
String get settings_contactSettings => '联系人设置';
@override
String get settings_contactSettingsSubtitle => '添加联系人的设置';
@override
String get settings_privacyMode => '隐私模式';
@ -2897,4 +2906,71 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get snrIndicator_lastSeen => '最近访问';
@override
String get contactsSettings_title => '联系人设置';
@override
String get contactsSettings_autoAddTitle => '自动发现';
@override
String get contactsSettings_otherTitle => '其他联系人相关设置';
@override
String get contactsSettings_autoAddUsersTitle => '自动添加用户';
@override
String get contactsSettings_autoAddUsersSubtitle => '允许伴侣自动添加发现的用户';
@override
String get contactsSettings_autoAddRepeatersTitle => '自动添加重复器';
@override
String get contactsSettings_autoAddRepeatersSubtitle => '允许伴侣自动添加发现的重复器';
@override
String get contactsSettings_autoAddRoomServersTitle => '自动添加房间服务器';
@override
String get contactsSettings_autoAddRoomServersSubtitle => '允许伴侣自动添加发现的房间服务器';
@override
String get contactsSettings_autoAddSensorsTitle => '自动添加传感器';
@override
String get contactsSettings_autoAddSensorsSubtitle => '允许伴侣自动添加发现的传感器';
@override
String get contactsSettings_overwriteOldestTitle => '覆盖最旧的';
@override
String get contactsSettings_overwriteOldestSubtitle =>
'当联系人列表已满时,将替换最老的非收藏联系人。';
@override
String get discoveredContacts_Title => '已发现的联系人';
@override
String get discoveredContacts_noMatching => '没有匹配的联系人';
@override
String get discoveredContacts_searchHint => '搜索已发现的联系人';
@override
String get discoveredContacts_contactAdded => '联系人已添加';
@override
String get discoveredContacts_addContact => '添加联系人';
@override
String get discoveredContacts_copyContact => '复制联系人到剪贴板';
@override
String get discoveredContacts_deleteContact => '删除联系人';
@override
String get discoveredContacts_deleteContactAll => '删除所有发现的联系人';
@override
String get discoveredContacts_deleteContactAllContent => '您确定要删除所有发现的联系人吗?';
}

View file

@ -1801,5 +1801,30 @@
"contacts_searchContactsNoNumber": "Zoek contacten...",
"contacts_searchUsers": "Zoek {number}{str} gebruikers...",
"contacts_searchFavorites": "Zoek {number}{str} favorieten...",
"contacts_searchRoomServers": "Zoek {number}{str} Room servers..."
"contacts_searchRoomServers": "Zoek {number}{str} Room servers...",
"contactsSettings_autoAddUsersTitle": "Gebruikers automatisch toevoegen",
"contactsSettings_title": "Instellingen voor contacten",
"settings_contactSettings": "Contactinstellingen",
"contactsSettings_otherTitle": "Andere instellingen voor contactgerelateerde zaken",
"contactsSettings_autoAddRepeatersSubtitle": "Sta toe dat de companion automatisch ontdekte repeaters toevoegt",
"contactsSettings_autoAddRoomServersTitle": "Automatisch kamerservers toevoegen",
"contactsSettings_autoAddRoomServersSubtitle": "Sta toe dat de companion automatisch ontdekte kamer servers toevoegt.",
"contactsSettings_autoAddSensorsTitle": "Automatisch sensoren toevoegen",
"settings_contactSettingsSubtitle": "Instellingen voor het toevoegen van contacten",
"contactsSettings_autoAddTitle": "Automatische detectie",
"contactsSettings_autoAddSensorsSubtitle": "Sta toe dat de companion automatisch ontdekte sensoren toevoegt",
"contactsSettings_autoAddUsersSubtitle": "Sta toe dat de companion automatisch ontdekte gebruikers toevoegt",
"contactsSettings_autoAddRepeatersTitle": "Automatisch herhalingstoestellen toevoegen",
"contactsSettings_overwriteOldestTitle": "Overschrijf Oudste",
"discoveredContacts_noMatching": "Geen overeenkomende contacten",
"discoveredContacts_addContact": "Contact toevoegen",
"discoveredContacts_copyContact": "Kopieer contact naar klembord",
"discoveredContacts_deleteContact": "Contact verwijderen",
"discoveredContacts_Title": "Ontdekte contacten",
"discoveredContacts_contactAdded": "Contact toegevoegd",
"discoveredContacts_searchHint": "Ontdekte contacten zoeken",
"contactsSettings_overwriteOldestSubtitle": "Wanneer de contactenlijst vol is, wordt de oudste niet-favoriete contactpersoon vervangen.",
"common_deleteAll": "Alles verwijderen",
"discoveredContacts_deleteContactAll": "Verwijder alle ontdekte contacten",
"discoveredContacts_deleteContactAllContent": "Weet u zeker dat u alle ontdekte contacten wilt verwijderen?"
}

View file

@ -1801,5 +1801,30 @@
"contacts_searchFavorites": "Wyszukaj {number}{str} ulubione...",
"contacts_searchRoomServers": "Wyszukaj {number}{str} serwerów Room...",
"contacts_searchUsers": "Wyszukaj {number}{str} Użytkowników...",
"contacts_searchRepeaters": "Wyszukaj {number}{str} powtórników..."
"contacts_searchRepeaters": "Wyszukaj {number}{str} powtórników...",
"contactsSettings_title": "Ustawienia kontaktów",
"settings_contactSettingsSubtitle": "Ustawienia dotyczące sposobu dodawania kontaktów",
"contactsSettings_autoAddUsersSubtitle": "Pozwól towarzyszowi automatycznie dodawać znalezione użytkowników.",
"contactsSettings_autoAddRepeatersTitle": "Automatyczne dodawanie powtarzalników",
"contactsSettings_autoAddRepeatersSubtitle": "Zezwól na automatyczne dodawanie odkrytych repeaterów przez towarzysza.",
"contactsSettings_autoAddRoomServersTitle": "Automatycznie dodaj serwery pokojowe",
"contactsSettings_autoAddUsersTitle": "Automatycznie dodaj użytkowników",
"settings_contactSettings": "Ustawienia kontaktowe",
"contactsSettings_otherTitle": "Inne ustawienia związane z kontaktami",
"contactsSettings_autoAddTitle": "Automatyczne odnajdywanie",
"contactsSettings_autoAddRoomServersSubtitle": "Zezwól towarzyszowi na automatyczne dodawanie znalezionych serwerów pokojowych.",
"contactsSettings_autoAddSensorsTitle": "Automatycznie dodaj czujniki",
"discoveredContacts_searchHint": "Wyszukaj odkryte kontakty",
"discoveredContacts_contactAdded": "Kontakt dodany",
"discoveredContacts_addContact": "Dodaj kontakt",
"discoveredContacts_copyContact": "Kopiuj kontakt do schowka",
"contactsSettings_overwriteOldestTitle": "Nadpisz najstarszy",
"discoveredContacts_Title": "Odkryte Kontakty",
"contactsSettings_autoAddSensorsSubtitle": "Zezwól towarzyszowi na automatyczne dodawanie wykrytych czujników.",
"discoveredContacts_noMatching": "Brak pasujących kontaktów",
"discoveredContacts_deleteContact": "Usuń kontakt",
"contactsSettings_overwriteOldestSubtitle": "Gdy lista kontaktów jest pełna, najstarszy nieulubiony kontakt zostanie zastąpiony.",
"common_deleteAll": "Usuń wszystko",
"discoveredContacts_deleteContactAllContent": "Czy na pewno chcesz usunąć wszystkie znalezione kontakty?",
"discoveredContacts_deleteContactAll": "Usuń wszystkie odkryte kontakty"
}

View file

@ -1801,5 +1801,30 @@
"contacts_searchUsers": "Pesquisar {number}{str} Usuários...",
"contacts_searchContactsNoNumber": "Pesquisar Contatos...",
"contacts_unread": "Não lido",
"contacts_searchRoomServers": "Pesquisar {number}{str} servidores de sala..."
"contacts_searchRoomServers": "Pesquisar {number}{str} servidores de sala...",
"settings_contactSettings": "Configurações de Contato",
"contactsSettings_otherTitle": "Outras configurações relacionadas a contatos",
"contactsSettings_title": "Configurações de contatos",
"contactsSettings_autoAddTitle": "Descoberta Automática",
"settings_contactSettingsSubtitle": "Configurações para como os contatos são adicionados",
"contactsSettings_autoAddUsersTitle": "Adicionar usuários automaticamente",
"contactsSettings_autoAddRepeatersSubtitle": "Permitir que o companheiro adicione automaticamente os repetidores descobertos.",
"contactsSettings_autoAddRoomServersTitle": "Adicionar automaticamente servidores de sala",
"contactsSettings_overwriteOldestTitle": "Sobrescrever o Mais Antigo",
"contactsSettings_autoAddSensorsTitle": "Adicionar sensores automaticamente",
"discoveredContacts_Title": "Contatos Descobertos",
"contactsSettings_autoAddUsersSubtitle": "Permitir que o companheiro adicione automaticamente os usuários descobertos.",
"contactsSettings_autoAddRepeatersTitle": "Adicionar repetidores automaticamente",
"discoveredContacts_noMatching": "Nenhum contato correspondente",
"contactsSettings_autoAddRoomServersSubtitle": "Permitir que o companheiro adicione automaticamente os servidores de salas descobertos.",
"discoveredContacts_searchHint": "Pesquisar contatos descobertos",
"contactsSettings_autoAddSensorsSubtitle": "Permitir que o companheiro adicione automaticamente sensores descobertos.",
"discoveredContacts_copyContact": "Copiar Contato para a área de transferência",
"discoveredContacts_deleteContact": "Excluir Contato",
"discoveredContacts_contactAdded": "Contato adicionado",
"discoveredContacts_addContact": "Adicionar Contato",
"contactsSettings_overwriteOldestSubtitle": "Quando a lista de contatos estiver cheia, o contato mais antigo não favoritado será substituído.",
"common_deleteAll": "Excluir Tudo",
"discoveredContacts_deleteContactAll": "Excluir Todos os Contatos Descobertos",
"discoveredContacts_deleteContactAllContent": "Tem certeza de que deseja excluir todos os contatos descobertos?"
}

View file

@ -1041,5 +1041,30 @@
"contacts_unread": "Непрочитанное",
"contacts_searchRoomServers": "Поиск {number}{str} серверов комнат...",
"contacts_searchFavorites": "Поиск {number}{str} избранного...",
"contacts_searchUsers": "Поиск {number}{str} пользователей..."
"contacts_searchUsers": "Поиск {number}{str} пользователей...",
"settings_contactSettings": "Настройки контактов",
"settings_contactSettingsSubtitle": "Настройки добавления контактов",
"contactsSettings_autoAddTitle": "Автоматическое обнаружение",
"contactsSettings_title": "Настройки контактов",
"contactsSettings_otherTitle": "Другие настройки, связанные с контактами",
"contactsSettings_autoAddUsersSubtitle": "Разрешить компаньону автоматически добавлять обнаруженных пользователей",
"contactsSettings_autoAddRoomServersTitle": "Автоматически добавлять серверы комнат",
"contactsSettings_autoAddSensorsTitle": "Автоматически добавлять датчики",
"contactsSettings_autoAddSensorsSubtitle": "Разрешить компаньону автоматически добавлять обнаруженные датчики",
"contactsSettings_autoAddUsersTitle": "Автоматически добавлять пользователей",
"contactsSettings_overwriteOldestTitle": "Перезаписать самое старое",
"contactsSettings_autoAddRepeatersTitle": "Автоматически добавлять ретрансляторы",
"contactsSettings_autoAddRepeatersSubtitle": "Разрешить спутнику автоматически добавлять обнаруженные ретрансляторы",
"contactsSettings_autoAddRoomServersSubtitle": "Разрешить компаньону автоматически добавлять обнаруженные сервера комнат.",
"discoveredContacts_noMatching": "Нет совпадающих контактов",
"discoveredContacts_searchHint": "Найденные контакты поиска",
"discoveredContacts_contactAdded": "Контакт добавлен",
"discoveredContacts_copyContact": "Копировать контакт в буфер обмена",
"discoveredContacts_addContact": "Добавить контакт",
"discoveredContacts_Title": "Обнаруженные контакты",
"discoveredContacts_deleteContact": "Удалить контакт",
"contactsSettings_overwriteOldestSubtitle": "Когда список контактов заполнен, будет заменен самый старый контакт, который не находится в избранном.",
"common_deleteAll": "Удалить все",
"discoveredContacts_deleteContactAllContent": "Вы уверены, что хотите удалить все обнаруженные контакты?",
"discoveredContacts_deleteContactAll": "Удалить Все Обнаруженные Контакты"
}

View file

@ -1801,5 +1801,30 @@
"contacts_searchRepeaters": "Hľadať {number}{str} opakovače...",
"contacts_searchUsers": "Hľadať {number}{str} používateľov...",
"contacts_searchContactsNoNumber": "Hľadať kontakty...",
"contacts_unread": "Neprečítané"
"contacts_unread": "Neprečítané",
"settings_contactSettingsSubtitle": "Nastavenia pre pridávanie kontaktov.",
"contactsSettings_autoAddUsersTitle": "Automaticky pridávať užívateľov",
"contactsSettings_autoAddUsersSubtitle": "Povoliť spoločníkovi automaticky pridávať objavených užívateľov.",
"contactsSettings_autoAddRepeatersTitle": "Automaticky pridávať opakovače",
"contactsSettings_autoAddRoomServersTitle": "Automaticky pridávať server miestnosti",
"contactsSettings_autoAddRoomServersSubtitle": "Povoliť spoločníkovi automaticky pridať objavené serverové miestnosti.",
"contactsSettings_autoAddTitle": "Automatické zisťovanie",
"contactsSettings_title": "Nastavenia kontaktov",
"contactsSettings_otherTitle": "Ďalšie nastavenia súvisiace s kontaktami",
"settings_contactSettings": "Nastavenia kontaktov",
"contactsSettings_autoAddSensorsTitle": "Automaticky pridávať senzory",
"discoveredContacts_noMatching": "Žiadne zhodné kontakty",
"discoveredContacts_searchHint": "Vyhľadať objavené kontakty",
"contactsSettings_autoAddRepeatersSubtitle": "Povoliť spoločníkovi automaticky pridávať objavené repeater.",
"discoveredContacts_contactAdded": "Kontakt bol pridaný",
"discoveredContacts_copyContact": "Kopírovať kontakt do schránky",
"discoveredContacts_deleteContact": "Zmazať kontakt",
"contactsSettings_autoAddSensorsSubtitle": "Povoliť spoločníkovi automaticky pridávať objavené senzory.",
"discoveredContacts_Title": "Objavené kontakty",
"contactsSettings_overwriteOldestTitle": "Prepísať najstaršie",
"discoveredContacts_addContact": "Pridať kontakt",
"contactsSettings_overwriteOldestSubtitle": "Keď je zoznam kontaktov plný, bude nahradený najstarší neoznačený kontakt.",
"discoveredContacts_deleteContactAll": "Zmazať všetky objavené kontakty",
"common_deleteAll": "Zmazať všetko",
"discoveredContacts_deleteContactAllContent": "Ste si istí, že chcete zmazať všetky objavené kontakty?"
}

View file

@ -1801,5 +1801,30 @@
"contacts_searchRoomServers": "Išči {number}{str} strežnikov sob...",
"contacts_searchContactsNoNumber": "Iskanje stikov...",
"contacts_searchRepeaters": "Išči {number}{str} ponavljalnike...",
"contacts_searchUsers": "Išči {number}{str} uporabnikov..."
"contacts_searchUsers": "Išči {number}{str} uporabnikov...",
"settings_contactSettings": "Nastavitve stika",
"contactsSettings_autoAddTitle": "Avtomatsko odkrivanje",
"contactsSettings_autoAddUsersTitle": "Avtomatsko dodaj uporabnike",
"contactsSettings_autoAddRepeatersTitle": "Avtomatsko dodaj ponovitelje",
"contactsSettings_autoAddRepeatersSubtitle": "Dovoli spremljevalcu, da samodejno doda odkrite ponovitelje.",
"contactsSettings_autoAddRoomServersTitle": "Avtomatsko dodaj strežnike sob",
"contactsSettings_autoAddRoomServersSubtitle": "Dovoli spremljevalcu, da samodejno doda odkrite strežnike sob.",
"contactsSettings_otherTitle": "Druge nastavitve v zvezi s stiki",
"settings_contactSettingsSubtitle": "Nastavitve za dodajanje stikov.",
"contactsSettings_title": "Nastavitve stikov",
"contactsSettings_autoAddSensorsTitle": "Avtomatsko dodaj senzorje",
"contactsSettings_autoAddUsersSubtitle": "Dovoli spremljevalcu, da samodejno doda odkrite uporabnike.",
"discoveredContacts_noMatching": "Ni ujemajočih stikov",
"contactsSettings_autoAddSensorsSubtitle": "Dovoli spremljevalcu, da samodejno doda odkrite senzorje.",
"discoveredContacts_addContact": "Dodaj stik",
"discoveredContacts_contactAdded": "Kontakt dodan",
"discoveredContacts_copyContact": "Kopiraj stik v odložišče",
"contactsSettings_overwriteOldestTitle": "Prepiši najstarejše",
"discoveredContacts_Title": "Odkriti stiki",
"discoveredContacts_searchHint": "Najdeni stiki po iskanju",
"discoveredContacts_deleteContact": "Izbriši stik",
"contactsSettings_overwriteOldestSubtitle": "Ko je seznam stikov poln, bo najstarejši nestarševski stik zamenjan.",
"common_deleteAll": "Izbriši vse",
"discoveredContacts_deleteContactAllContent": "Ste prepričani, da želite izbrisati vse odkrite kontakte?",
"discoveredContacts_deleteContactAll": "Izbriši vse odkrite kontakte"
}

View file

@ -1801,5 +1801,30 @@
"contacts_searchRepeaters": "Sök {number}{str} upprepningsenheter...",
"contacts_searchFavorites": "Sök {number}{str} Favoriter...",
"contacts_searchUsers": "Sök {number}{str} användare...",
"contacts_searchRoomServers": "Sök {number}{str} Room-servrar..."
"contacts_searchRoomServers": "Sök {number}{str} Room-servrar...",
"settings_contactSettingsSubtitle": "Inställningar för hur kontakter läggs till.",
"settings_contactSettings": "Kontaktinställningar",
"contactsSettings_autoAddTitle": "Automatisk upptäckt",
"contactsSettings_otherTitle": "Andra inställningar relaterade till kontakt",
"contactsSettings_autoAddUsersSubtitle": "Tillåt kompanjonen att automatiskt lägga till upptäckta användare",
"contactsSettings_autoAddRepeatersTitle": "Lägg till upprepande enheter automatiskt",
"contactsSettings_autoAddRoomServersSubtitle": "Tillåt kompanjonen att automatiskt lägga till upptäckta rumsservrar.",
"contactsSettings_autoAddSensorsTitle": "Lägg till sensorer automatiskt",
"contactsSettings_autoAddUsersTitle": "Lägg till användare automatiskt",
"contactsSettings_title": "Kontaktinställningar",
"contactsSettings_autoAddSensorsSubtitle": "Tillåt kompanjonen att automatiskt lägga till upptäckta sensorer.",
"contactsSettings_overwriteOldestTitle": "Skriv över äldst",
"contactsSettings_autoAddRepeatersSubtitle": "Tillåt kompanjonen att automatiskt lägga till upptäckta repeater.",
"contactsSettings_autoAddRoomServersTitle": "Lägg automatiskt till rumsservrar",
"discoveredContacts_noMatching": "Inga matchande kontakter",
"discoveredContacts_searchHint": "Sök uppfunna kontakter",
"discoveredContacts_deleteContact": "Ta bort kontakt",
"discoveredContacts_Title": "Upptäckta kontakter",
"discoveredContacts_contactAdded": "Kontakt tillagd",
"discoveredContacts_addContact": "Lägg till kontakt",
"discoveredContacts_copyContact": "Kopiera kontakt till urklipp",
"contactsSettings_overwriteOldestSubtitle": "När kontaktlistan är full ersätts den äldsta icke-favoriterade kontakten.",
"common_deleteAll": "Ta bort alla",
"discoveredContacts_deleteContactAllContent": "Är du säker på att du vill ta bort alla upptäckta kontakter?",
"discoveredContacts_deleteContactAll": "Ta bort alla upptäckta kontakter"
}

View file

@ -1801,5 +1801,30 @@
"contacts_searchFavorites": "Пошук {number}{str} улюблених...",
"contacts_searchContactsNoNumber": "Пошук контактів...",
"contacts_searchRepeaters": "Пошук {number}{str} ретрансляторів...",
"contacts_unread": "Непрочитане"
"contacts_unread": "Непрочитане",
"settings_contactSettingsSubtitle": "Налаштування для додавання контактів",
"settings_contactSettings": "Налаштування контактів",
"contactsSettings_autoAddUsersSubtitle": "Дозволити супутникові автоматично додавати виявлених користувачів",
"contactsSettings_autoAddRepeatersTitle": "Автоматично додавати повторювачі",
"contactsSettings_autoAddRepeatersSubtitle": "Дозволити супутнику автоматично додавати виявлені ретранслятори",
"contactsSettings_autoAddRoomServersTitle": "Автоматично додавати сервери кімнат",
"contactsSettings_otherTitle": "Інші налаштування, пов'язані з контактами",
"contactsSettings_autoAddTitle": "Автоматичне виявлення",
"contactsSettings_autoAddUsersTitle": "Автоматично додавати користувачів",
"contactsSettings_title": "Налаштування контактів",
"contactsSettings_autoAddRoomServersSubtitle": "Дозволити супровіднику автоматично додавати виявлені сервери кімнат.",
"contactsSettings_autoAddSensorsTitle": "Автоматично додавати датчики",
"discoveredContacts_searchHint": "Знайти виявлені контакти",
"discoveredContacts_contactAdded": "Контакт додано",
"contactsSettings_autoAddSensorsSubtitle": "Дозволити супровіднику автоматично додавати виявлені сенсори",
"contactsSettings_overwriteOldestTitle": "Перезаписати найстаріше",
"discoveredContacts_Title": "Виявлені контакти",
"discoveredContacts_noMatching": "Відповідних контактів не знайдено",
"discoveredContacts_deleteContact": "Видалити контакт",
"discoveredContacts_copyContact": "Копіювати контакт у буфер обміну",
"discoveredContacts_addContact": "Додати контакт",
"contactsSettings_overwriteOldestSubtitle": "Коли список контактів заповнений, найстарший контакт без позначки улюбленого буде замінений.",
"common_deleteAll": "Видалити все",
"discoveredContacts_deleteContactAll": "Видалити всі виявлені контакти",
"discoveredContacts_deleteContactAllContent": "Ви впевнені, що хочете видалити всі виявлені контакти?"
}

View file

@ -1806,5 +1806,30 @@
"contacts_searchRepeaters": "搜索 {number}{str} 重复器...",
"contacts_searchContactsNoNumber": "搜索联系人...",
"contacts_searchRoomServers": "搜索 {number}{str} 房间服务器...",
"contacts_searchFavorites": "搜索 {number}{str} 收藏..."
"contacts_searchFavorites": "搜索 {number}{str} 收藏...",
"settings_contactSettings": "联系人设置",
"contactsSettings_title": "联系人设置",
"contactsSettings_autoAddUsersTitle": "自动添加用户",
"contactsSettings_otherTitle": "其他联系人相关设置",
"contactsSettings_autoAddUsersSubtitle": "允许伴侣自动添加发现的用户",
"contactsSettings_autoAddRepeatersSubtitle": "允许伴侣自动添加发现的重复器",
"contactsSettings_autoAddSensorsTitle": "自动添加传感器",
"contactsSettings_autoAddRoomServersSubtitle": "允许伴侣自动添加发现的房间服务器",
"contactsSettings_autoAddRepeatersTitle": "自动添加重复器",
"contactsSettings_autoAddTitle": "自动发现",
"settings_contactSettingsSubtitle": "添加联系人的设置",
"contactsSettings_overwriteOldestTitle": "覆盖最旧的",
"contactsSettings_autoAddSensorsSubtitle": "允许伴侣自动添加发现的传感器",
"discoveredContacts_searchHint": "搜索已发现的联系人",
"contactsSettings_autoAddRoomServersTitle": "自动添加房间服务器",
"discoveredContacts_contactAdded": "联系人已添加",
"discoveredContacts_deleteContact": "删除联系人",
"discoveredContacts_addContact": "添加联系人",
"discoveredContacts_noMatching": "没有匹配的联系人",
"discoveredContacts_Title": "已发现的联系人",
"discoveredContacts_copyContact": "复制联系人到剪贴板",
"contactsSettings_overwriteOldestSubtitle": "当联系人列表已满时,将替换最老的非收藏联系人。",
"common_deleteAll": "删除全部",
"discoveredContacts_deleteContactAllContent": "您确定要删除所有发现的联系人吗?",
"discoveredContacts_deleteContactAll": "删除所有发现的联系人"
}

View file

@ -183,7 +183,7 @@ class Contact {
)
: Uint8List(0);
final name = readCString(data, contactNameOffset, maxNameSize);
final lastmod = readUint32LE(data, contactLastmodOffset);
final lastmod = readUint32LE(data, contactLastModOffset);
double? lat, lon;
final latRaw = readInt32LE(data, contactLatOffset);

View file

@ -0,0 +1,105 @@
import 'dart:typed_data';
import '../connector/meshcore_protocol.dart';
class DiscoveryContact {
final Uint8List rawPacket;
final Uint8List publicKey;
final String name;
final int type;
final int pathLength; // -1 = flood, 0+ = direct hops (from device)
final Uint8List path; // Path bytes from device
final double? latitude;
final double? longitude;
final DateTime lastSeen;
DiscoveryContact({
required this.rawPacket,
required this.publicKey,
required this.name,
required this.type,
required this.pathLength,
required this.path,
this.latitude,
this.longitude,
required this.lastSeen,
});
String get publicKeyHex => pubKeyToHex(publicKey);
String get typeLabel {
switch (type) {
case advTypeChat:
return 'Chat';
case advTypeRepeater:
return 'Repeater';
case advTypeRoom:
return 'Room';
case advTypeSensor:
return 'Sensor';
default:
return 'Unknown';
}
}
String get pathLabel {
if (pathLength < 0) return 'Flood';
if (pathLength == 0) return 'Direct';
return '$pathLength hops';
}
bool get hasLocation => latitude != null && longitude != null;
DiscoveryContact copyWith({
Uint8List? rawPacket,
Uint8List? publicKey,
String? name,
int? type,
int? pathLength,
Uint8List? path,
double? latitude,
double? longitude,
DateTime? lastSeen,
}) {
return DiscoveryContact(
rawPacket: rawPacket ?? this.rawPacket,
publicKey: publicKey ?? this.publicKey,
name: name ?? this.name,
type: type ?? this.type,
pathLength: pathLength ?? this.pathLength,
path: path ?? this.path,
latitude: latitude ?? this.latitude,
longitude: longitude ?? this.longitude,
lastSeen: lastSeen ?? this.lastSeen,
);
}
String get pathIdList {
final pathBytes = path;
if (pathBytes.isEmpty) return '';
final parts = <String>[];
final groupSize = pathHashSize;
for (int i = 0; i < pathBytes.length; i += groupSize) {
final end = (i + groupSize) <= pathBytes.length
? (i + groupSize)
: pathBytes.length;
final chunk = pathBytes.sublist(i, end);
parts.add(
chunk
.map((b) => b.toRadixString(16).padLeft(2, '0').toUpperCase())
.join(),
);
}
return parts.join(',');
}
String get shortPubKeyHex {
return "<${publicKeyHex.substring(0, 8)}...${publicKeyHex.substring(publicKeyHex.length - 8)}>";
}
@override
bool operator ==(Object other) =>
other is DiscoveryContact && publicKeyHex == other.publicKeyHex;
@override
int get hashCode => publicKeyHex.hashCode;
}

View file

@ -26,6 +26,7 @@ import '../widgets/room_login_dialog.dart';
import '../widgets/unread_badge.dart';
import 'channels_screen.dart';
import 'chat_screen.dart';
import 'discovery_screen.dart';
import 'map_screen.dart';
import 'repeater_hub_screen.dart';
import 'settings_screen.dart';
@ -218,9 +219,10 @@ class _ContactsScreenState extends State<ContactsScreen>
}
final hexString = text.substring('meshcore://'.length);
try {
final importContactFrame = buildImportContactFrame(hexString);
final bytes = hex2Uint8List(hexString);
final importContactFrame = buildImportContactFrame(bytes);
_pendingOperations.add(ContactOperationType.import);
await connector.sendFrame(importContactFrame, expectsGenericAck: true);
connector.importContact(importContactFrame);
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
@ -318,6 +320,21 @@ class _ContactsScreenState extends State<ContactsScreen>
),
onTap: () => _disconnect(context, connector),
),
PopupMenuItem(
child: Row(
children: [
const Icon(Icons.person_add_rounded),
const SizedBox(width: 8),
Text("Discovered Contacts"),
],
),
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const DiscoveryScreen(),
),
),
),
PopupMenuItem(
child: Row(
children: [

View file

@ -0,0 +1,419 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import '../connector/meshcore_connector.dart';
import '../connector/meshcore_protocol.dart';
import '../l10n/l10n.dart';
import '../models/discovery_contact.dart';
import '../utils/contact_search.dart';
import '../widgets/app_bar.dart';
import '../widgets/list_filter_widget.dart';
enum DiscoverySortOption { lastSeen, name, type }
class DiscoveryScreen extends StatefulWidget {
const DiscoveryScreen({super.key});
@override
State<DiscoveryScreen> createState() => _DiscoveryScreenState();
}
class _DiscoveryScreenState extends State<DiscoveryScreen> {
final TextEditingController _searchController = TextEditingController();
String searchQuery = '';
ContactSortOption sortOption = ContactSortOption.lastSeen;
bool showUnreadOnly = false;
ContactTypeFilter typeFilter = ContactTypeFilter.all;
DiscoverySortOption discoverySortOption = DiscoverySortOption.lastSeen;
Timer? _searchDebounce;
@override
void dispose() {
_searchController.dispose();
_searchDebounce?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
final l10n = context.l10n;
final connector = context.watch<MeshCoreConnector>();
final discoveredContacts = connector.discoveredContacts;
final filteredAndSorted = _filterAndSortContacts(
discoveredContacts,
connector,
);
return Scaffold(
appBar: AppBar(
title: AppBarTitle(
l10n.discoveredContacts_Title,
indicators: false,
subtitle: false,
),
centerTitle: true,
actions: [
PopupMenuButton(
itemBuilder: (context) => [
PopupMenuItem(
child: Row(
children: [
const Icon(Icons.delete, color: Colors.red),
const SizedBox(width: 8),
Text(context.l10n.discoveredContacts_deleteContactAll),
],
),
onTap: () {
_deleteContacts(context, connector);
},
),
],
icon: const Icon(Icons.more_vert),
),
],
),
body: Column(
children: [
_buildFilters(filteredAndSorted, connector),
Expanded(
child: discoveredContacts.isEmpty
? Center(child: Text(l10n.contacts_noContacts))
: filteredAndSorted.isEmpty
? Center(child: Text(l10n.discoveredContacts_noMatching))
: ListView.builder(
itemCount: filteredAndSorted.length,
itemBuilder: (context, index) {
final contact = filteredAndSorted[index];
return ListTile(
leading: CircleAvatar(
backgroundColor: _getTypeColor(contact.type),
child: Icon(
_getTypeIcon(contact.type),
color: Colors.white,
size: 20,
),
),
title: Text(
contact.name,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
subtitle: Text(
contact.shortPubKeyHex,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
trailing: Text(
_formatLastSeen(context, contact.lastSeen),
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
),
),
onTap: () {
connector.importDiscoveredContact(contact);
},
onLongPress: () =>
_showContactContextMenu(contact, connector),
);
},
),
),
],
),
);
}
Future<void> _showContactContextMenu(
DiscoveryContact contact,
MeshCoreConnector connector,
) async {
final action = await showModalBottomSheet<String>(
context: context,
showDragHandle: true,
builder: (sheetContext) {
final l10n = context.l10n;
return SafeArea(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
leading: const Icon(Icons.add_reaction_sharp),
title: Text(l10n.discoveredContacts_addContact),
onTap: () => Navigator.of(sheetContext).pop('import_contact'),
),
ListTile(
leading: const Icon(Icons.copy),
title: Text(l10n.discoveredContacts_copyContact),
onTap: () => Navigator.of(sheetContext).pop('copy_contact'),
),
ListTile(
leading: const Icon(Icons.delete),
title: Text(l10n.discoveredContacts_deleteContact),
onTap: () => Navigator.of(sheetContext).pop('delete_contact'),
),
],
),
);
},
);
if (!mounted || action == null) return;
switch (action) {
case 'import_contact':
connector.importDiscoveredContact(contact);
break;
case 'copy_contact':
final hexString = pubKeyToHex(contact.rawPacket);
Clipboard.setData(ClipboardData(text: "meshcore://$hexString"));
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(context.l10n.contacts_contactAdvertCopied)),
);
break;
case 'delete_contact':
connector.removeDiscoveredContact(contact);
break;
}
}
void _deleteContacts(BuildContext context, MeshCoreConnector connector) {
final l10n = context.l10n;
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(l10n.common_deleteAll),
content: Text(l10n.discoveredContacts_deleteContactAllContent),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text(l10n.common_cancel),
),
TextButton(
onPressed: () async {
Navigator.pop(context);
connector.removeAllDiscoveredContacts();
},
child: Text(l10n.common_deleteAll),
),
],
),
);
}
Widget _buildFilters(
List<DiscoveryContact> filteredAndSorted,
MeshCoreConnector connector,
) {
String hintText = "";
switch (typeFilter) {
case ContactTypeFilter.all:
hintText = context.l10n.contacts_searchContacts(
filteredAndSorted.length,
showUnreadOnly ? " ${context.l10n.contacts_unread}" : "",
);
break;
case ContactTypeFilter.users:
hintText = context.l10n.contacts_searchUsers(
filteredAndSorted.length,
showUnreadOnly ? " ${context.l10n.contacts_unread}" : "",
);
break;
case ContactTypeFilter.repeaters:
hintText = context.l10n.contacts_searchRepeaters(
filteredAndSorted.length,
showUnreadOnly ? " ${context.l10n.contacts_unread}" : "",
);
break;
case ContactTypeFilter.rooms:
hintText = context.l10n.contacts_searchRoomServers(
filteredAndSorted.length,
showUnreadOnly ? " ${context.l10n.contacts_unread}" : "",
);
break;
case ContactTypeFilter.favorites:
hintText = context.l10n.contacts_searchFavorites(
filteredAndSorted.length,
showUnreadOnly ? " ${context.l10n.contacts_unread}" : "",
);
break;
}
return Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: _searchController,
decoration: InputDecoration(
hintText: hintText,
prefixIcon: const Icon(Icons.search),
suffixIcon: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (searchQuery.isNotEmpty)
IconButton(
icon: const Icon(Icons.clear),
onPressed: () {
_searchController.clear();
setState(() {
searchQuery = '';
});
},
),
_buildFilterButton(context, connector),
],
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
contentPadding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 12,
),
),
onChanged: (value) {
_searchDebounce?.cancel();
_searchDebounce = Timer(const Duration(milliseconds: 300), () {
if (!mounted) return;
setState(() {
searchQuery = value.toLowerCase();
});
});
},
),
),
],
);
}
Widget _buildFilterButton(BuildContext context, MeshCoreConnector connector) {
return DiscoveryContactsFilterMenu(
sortOption: sortOption,
typeFilter: typeFilter,
onSortChanged: (value) {
setState(() {
sortOption = value;
});
},
onTypeFilterChanged: (value) {
setState(() {
typeFilter = value;
});
},
);
}
List<DiscoveryContact> _filterAndSortContacts(
List<DiscoveryContact> contacts,
MeshCoreConnector connector,
) {
var filtered = contacts.where((contact) {
if (searchQuery.isEmpty) return true;
return matchesDiscoveryContactQuery(contact, searchQuery);
}).toList();
filtered = filtered.where((contact) {
return !connector.knownContactKeys.contains(contact.publicKeyHex);
}).toList();
// Filter out own node from the list
if (connector.selfPublicKey != null) {
final selfPubKeyHex = pubKeyToHex(connector.selfPublicKey!);
filtered = filtered.where((contact) {
return contact.publicKeyHex != selfPubKeyHex;
}).toList();
}
if (typeFilter != ContactTypeFilter.all) {
filtered = filtered.where(_matchesTypeFilter).toList();
}
switch (sortOption) {
case ContactSortOption.lastSeen:
filtered.sort((a, b) => b.lastSeen.compareTo(a.lastSeen));
break;
case ContactSortOption.name:
filtered.sort(
(a, b) => a.name.toLowerCase().compareTo(b.name.toLowerCase()),
);
break;
default:
break;
}
return filtered;
}
bool _matchesTypeFilter(DiscoveryContact contact) {
switch (typeFilter) {
case ContactTypeFilter.all:
return true;
case ContactTypeFilter.users:
return contact.type == advTypeChat;
case ContactTypeFilter.repeaters:
return contact.type == advTypeRepeater;
case ContactTypeFilter.rooms:
return contact.type == advTypeRoom;
default:
return false;
}
}
IconData _getTypeIcon(int type) {
switch (type) {
case advTypeChat:
return Icons.chat;
case advTypeRepeater:
return Icons.cell_tower;
case advTypeRoom:
return Icons.group;
case advTypeSensor:
return Icons.sensors;
default:
return Icons.device_unknown;
}
}
Color _getTypeColor(int type) {
switch (type) {
case advTypeChat:
return Colors.blue;
case advTypeRepeater:
return Colors.orange;
case advTypeRoom:
return Colors.purple;
case advTypeSensor:
return Colors.green;
default:
return Colors.grey;
}
}
String _formatLastSeen(BuildContext context, DateTime lastSeen) {
final now = DateTime.now();
final diff = now.difference(lastSeen);
if (diff.isNegative || diff.inMinutes < 5) {
return context.l10n.contacts_lastSeenNow;
}
if (diff.inMinutes < 60) {
return context.l10n.contacts_lastSeenMinsAgo(diff.inMinutes);
}
if (diff.inHours < 24) {
final hours = diff.inHours;
return hours == 1
? context.l10n.contacts_lastSeenHourAgo
: context.l10n.contacts_lastSeenHoursAgo(hours);
}
final days = diff.inDays;
return days == 1
? context.l10n.contacts_lastSeenDayAgo
: context.l10n.contacts_lastSeenDaysAgo(days);
}
}

View file

@ -8,7 +8,7 @@ import '../connector/meshcore_connector.dart';
import '../connector/meshcore_protocol.dart';
import '../l10n/l10n.dart';
import '../models/radio_settings.dart';
import '../widgets/adaptive_app_bar_title.dart';
import '../widgets/app_bar.dart';
import 'app_settings_screen.dart';
import 'app_debug_log_screen.dart';
import 'ble_debug_log_screen.dart';
@ -43,8 +43,11 @@ class _SettingsScreenState extends State<SettingsScreen> {
final l10n = context.l10n;
return Scaffold(
appBar: AppBar(
title: AdaptiveAppBarTitle(l10n.settings_title),
centerTitle: true,
title: AppBarTitle(
l10n.settings_title,
indicators: false,
subtitle: false,
),
),
body: SafeArea(
top: false,
@ -274,6 +277,14 @@ class _SettingsScreenState extends State<SettingsScreen> {
onTap: () => _editLocation(context, connector),
),
const Divider(height: 1),
ListTile(
leading: const Icon(Icons.group_add_outlined),
title: Text(l10n.settings_contactSettings),
subtitle: Text(l10n.settings_contactSettingsSubtitle),
trailing: const Icon(Icons.chevron_right),
onTap: () => _editAutoAddConfig(context, connector),
),
const Divider(height: 1),
ListTile(
leading: const Icon(Icons.visibility_off_outlined),
title: Text(l10n.settings_privacyMode),
@ -849,6 +860,121 @@ class _SettingsScreenState extends State<SettingsScreen> {
),
);
}
void _editAutoAddConfig(BuildContext context, MeshCoreConnector connector) {
final l10n = context.l10n;
bool autoAddChat = false;
bool autoAddRepeater = false;
bool autoAddRoomServer = false;
bool autoAddSensor = false;
bool overwriteOldest = false;
final connector = context.read<MeshCoreConnector>();
autoAddChat = connector.autoAddUsers ?? false;
autoAddRepeater = connector.autoAddRepeaters ?? false;
autoAddRoomServer = connector.autoAddRoomServers ?? false;
autoAddSensor = connector.autoAddSensors ?? false;
overwriteOldest = connector.autoAddOverwriteOldest ?? false;
showDialog(
context: context,
builder: (dialogContext) => StatefulBuilder(
builder: (context, setDialogState) => AlertDialog(
title: Text(l10n.contactsSettings_autoAddTitle),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
FeatureToggleRow(
title: l10n.contactsSettings_autoAddUsersTitle,
subtitle: l10n.contactsSettings_autoAddUsersSubtitle,
value: autoAddChat,
onChanged: (value) {
setDialogState(() => autoAddChat = value);
},
),
SizedBox(height: 8),
FeatureToggleRow(
title: l10n.contactsSettings_autoAddRepeatersTitle,
subtitle: l10n.contactsSettings_autoAddRepeatersSubtitle,
value: autoAddRepeater,
onChanged: (value) {
setDialogState(() => autoAddRepeater = value);
},
),
SizedBox(height: 8),
FeatureToggleRow(
title: l10n.contactsSettings_autoAddRoomServersTitle,
subtitle: l10n.contactsSettings_autoAddRoomServersSubtitle,
value: autoAddRoomServer,
onChanged: (value) {
setDialogState(() => autoAddRoomServer = value);
},
),
SizedBox(height: 8),
FeatureToggleRow(
title: l10n.contactsSettings_autoAddSensorsTitle,
subtitle: l10n.contactsSettings_autoAddSensorsSubtitle,
value: autoAddSensor,
onChanged: (value) {
setDialogState(() => autoAddSensor = value);
},
),
Divider(height: 4),
FeatureToggleRow(
title: l10n.contactsSettings_overwriteOldestTitle,
subtitle: l10n.contactsSettings_overwriteOldestSubtitle,
value: overwriteOldest,
onChanged: (value) {
setDialogState(() => overwriteOldest = value);
},
),
],
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text(l10n.common_cancel),
),
TextButton(
onPressed: () {
_sendSettings(
connector,
autoAddChat,
autoAddRepeater,
autoAddRoomServer,
autoAddSensor,
overwriteOldest,
);
Navigator.pop(context);
},
child: Text(l10n.common_save),
),
],
),
),
);
}
void _sendSettings(
MeshCoreConnector connector,
bool autoAddChat,
bool autoAddRepeater,
bool autoAddRoomServer,
bool autoAddSensor,
bool overwriteOldest,
) async {
final frame = buildSetAutoAddConfigFrame(
autoAddChat: autoAddChat,
autoAddRepeater: autoAddRepeater,
autoAddRoomServer: autoAddRoomServer,
autoAddSensor: autoAddSensor,
overwriteOldest: overwriteOldest,
);
await connector.sendFrame(frame);
await connector.sendFrame(buildGetAutoAddFlagsFrame());
}
}
class _RadioSettingsDialog extends StatefulWidget {

View file

@ -172,8 +172,6 @@ class BleDebugLogService extends ChangeNotifier {
return 'CMD_GET_CHANNEL';
case cmdSetChannel:
return 'CMD_SET_CHANNEL';
case cmdGetRadioSettings:
return 'CMD_GET_RADIO_SETTINGS';
case cmdSetCustomVar:
return 'CMD_SET_CUSTOM_VAR';
case cmdSendTracePath:
@ -215,8 +213,8 @@ class BleDebugLogService extends ChangeNotifier {
return 'RESP_CODE_CHANNEL_MSG_RECV_V3';
case respCodeChannelInfo:
return 'RESP_CODE_CHANNEL_INFO';
case respCodeRadioSettings:
return 'RESP_CODE_RADIO_SETTINGS';
case respCodeAutoAddConfig:
return 'RESP_CODE_AUTO_ADD_CONFIG';
case pushCodeTraceData:
return 'PUSH_CODE_TRACE_DATA';
default:

View file

@ -0,0 +1,61 @@
import 'dart:convert';
import 'dart:typed_data';
import '../models/discovery_contact.dart';
import 'prefs_manager.dart';
class ContactDiscoveryStore {
static const String _key = 'discovered_contacts';
Future<List<DiscoveryContact>> loadContacts() async {
final prefs = PrefsManager.instance;
final jsonStr = prefs.getString(_key);
if (jsonStr == null) return [];
try {
final jsonList = jsonDecode(jsonStr) as List<dynamic>;
return jsonList
.map((entry) => _fromJson(entry as Map<String, dynamic>))
.toList();
} catch (_) {
return [];
}
}
Future<void> saveContacts(List<DiscoveryContact> contacts) async {
final prefs = PrefsManager.instance;
final jsonList = contacts.map(_toJson).toList();
await prefs.setString(_key, jsonEncode(jsonList));
}
Map<String, dynamic> _toJson(DiscoveryContact contact) {
return {
'rawPacket': base64Encode(contact.rawPacket),
'publicKey': base64Encode(contact.publicKey),
'name': contact.name,
'type': contact.type,
'pathLength': contact.pathLength,
'path': base64Encode(contact.path),
'latitude': contact.latitude,
'longitude': contact.longitude,
'lastSeen': contact.lastSeen.millisecondsSinceEpoch,
};
}
DiscoveryContact _fromJson(Map<String, dynamic> json) {
final lastSeenMs = json['lastSeen'] as int? ?? 0;
return DiscoveryContact(
rawPacket: Uint8List.fromList(base64Decode(json['rawPacket'] as String)),
publicKey: Uint8List.fromList(base64Decode(json['publicKey'] as String)),
name: json['name'] as String? ?? 'Unknown',
type: json['type'] as int? ?? 0,
pathLength: json['pathLength'] as int? ?? -1,
path: json['path'] != null
? Uint8List.fromList(base64Decode(json['path'] as String))
: Uint8List(0),
latitude: (json['latitude'] as num?)?.toDouble(),
longitude: (json['longitude'] as num?)?.toDouble(),
lastSeen: DateTime.fromMillisecondsSinceEpoch(lastSeenMs),
);
}
}

View file

@ -1,3 +1,5 @@
import 'package:meshcore_open/models/discovery_contact.dart';
import '../models/contact.dart';
bool matchesContactQuery(Contact contact, String query) {
@ -14,6 +16,20 @@ bool matchesContactQuery(Contact contact, String query) {
return contact.publicKeyHex.toLowerCase().startsWith(hexPrefix);
}
bool matchesDiscoveryContactQuery(DiscoveryContact contact, String query) {
final normalizedQuery = query.trim().toLowerCase();
if (normalizedQuery.isEmpty) return true;
if (contact.name.toLowerCase().contains(normalizedQuery)) {
return true;
}
final hexPrefix = _extractHexPrefix(normalizedQuery);
if (hexPrefix == null) return false;
return contact.publicKeyHex.toLowerCase().startsWith(hexPrefix);
}
String? _extractHexPrefix(String query) {
var cleaned = query;
if (cleaned.startsWith('<')) {

View file

@ -9,7 +9,16 @@ class AppBarTitle extends StatelessWidget {
final String title;
final Widget? leading;
final Widget? trailing;
const AppBarTitle(this.title, {this.leading, this.trailing, super.key});
final bool indicators;
final bool subtitle;
const AppBarTitle(
this.title, {
this.leading,
this.trailing,
this.indicators = true,
this.subtitle = true,
super.key,
});
@override
Widget build(BuildContext context) {
@ -21,12 +30,12 @@ class AppBarTitle extends StatelessWidget {
final availableWidth = constraints.hasBoundedWidth
? constraints.maxWidth
: MediaQuery.sizeOf(context).width;
final compact = availableWidth < 240;
final compact = availableWidth < 170;
final showSubtitle =
!compact && connector.isConnected && selfName != null;
!compact && connector.isConnected && selfName != null && subtitle;
final showBattery = availableWidth >= 60;
final showSnr = availableWidth >= 110;
final showIndicators = showBattery || showSnr;
final showIndicators = (showBattery || showSnr) && indicators;
return Row(
mainAxisAlignment: MainAxisAlignment.start,

View file

@ -224,3 +224,93 @@ class ContactsFilterMenu extends StatelessWidget {
);
}
}
class DiscoveryContactsFilterMenu extends StatelessWidget {
final ContactSortOption sortOption;
final ContactTypeFilter typeFilter;
final ValueChanged<ContactSortOption> onSortChanged;
final ValueChanged<ContactTypeFilter> onTypeFilterChanged;
const DiscoveryContactsFilterMenu({
super.key,
required this.sortOption,
required this.typeFilter,
required this.onSortChanged,
required this.onTypeFilterChanged,
});
@override
Widget build(BuildContext context) {
final l10n = context.l10n;
return SortFilterMenu(
tooltip: l10n.listFilter_tooltip,
sections: [
SortFilterMenuSection(
title: l10n.listFilter_sortBy,
options: [
SortFilterMenuOption(
value: _actionSortLastSeen,
label: l10n.listFilter_heardRecently,
checked: sortOption == ContactSortOption.lastSeen,
),
SortFilterMenuOption(
value: _actionSortName,
label: l10n.listFilter_az,
checked: sortOption == ContactSortOption.name,
),
],
),
SortFilterMenuSection(
title: l10n.listFilter_filters,
options: [
SortFilterMenuOption(
value: _actionFilterAll,
label: l10n.listFilter_all,
checked: typeFilter == ContactTypeFilter.all,
),
SortFilterMenuOption(
value: _actionFilterUsers,
label: l10n.listFilter_users,
checked: typeFilter == ContactTypeFilter.users,
),
SortFilterMenuOption(
value: _actionFilterRepeaters,
label: l10n.listFilter_repeaters,
checked: typeFilter == ContactTypeFilter.repeaters,
),
SortFilterMenuOption(
value: _actionFilterRooms,
label: l10n.listFilter_roomServers,
checked: typeFilter == ContactTypeFilter.rooms,
),
],
),
],
onSelected: (action) {
switch (action) {
case _actionSortName:
onSortChanged(ContactSortOption.name);
break;
case _actionSortLastSeen:
onSortChanged(ContactSortOption.lastSeen);
break;
case _actionFilterAll:
onTypeFilterChanged(ContactTypeFilter.all);
break;
case _actionFilterUsers:
onTypeFilterChanged(ContactTypeFilter.users);
break;
case _actionFilterFavorites:
onTypeFilterChanged(ContactTypeFilter.favorites);
break;
case _actionFilterRepeaters:
onTypeFilterChanged(ContactTypeFilter.repeaters);
break;
case _actionFilterRooms:
onTypeFilterChanged(ContactTypeFilter.rooms);
break;
}
},
);
}
}