mirror of
https://github.com/zjs81/meshcore-open.git
synced 2026-07-24 01:29:03 +10:00
Add contact settings and discovery features
- Implemented contact settings in localization files for Swedish, Ukrainian, and Chinese. - Added new DiscoveryContact model to handle discovered contacts. - Created DiscoveryScreen to display discovered contacts with filtering and sorting options. - Integrated contact discovery storage to persist discovered contacts. - Updated settings screen to include options for automatic contact addition. - Enhanced app bar and list filter widgets for better user experience. - Fixed variable naming inconsistencies in contact model.
This commit is contained in:
@@ -2,6 +2,7 @@ import 'dart:async';
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:crypto/crypto.dart' as crypto;
|
import 'package:crypto/crypto.dart' as crypto;
|
||||||
|
import 'package:meshcore_open/models/discovery_contact.dart';
|
||||||
import 'package:pointycastle/export.dart';
|
import 'package:pointycastle/export.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
|
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
|
||||||
@@ -24,6 +25,7 @@ import '../storage/channel_message_store.dart';
|
|||||||
import '../storage/channel_order_store.dart';
|
import '../storage/channel_order_store.dart';
|
||||||
import '../storage/channel_settings_store.dart';
|
import '../storage/channel_settings_store.dart';
|
||||||
import '../storage/channel_store.dart';
|
import '../storage/channel_store.dart';
|
||||||
|
import '../storage/contact_discovery_store.dart';
|
||||||
import '../storage/contact_settings_store.dart';
|
import '../storage/contact_settings_store.dart';
|
||||||
import '../storage/contact_store.dart';
|
import '../storage/contact_store.dart';
|
||||||
import '../storage/message_store.dart';
|
import '../storage/message_store.dart';
|
||||||
@@ -111,6 +113,7 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
|
|
||||||
final List<ScanResult> _scanResults = [];
|
final List<ScanResult> _scanResults = [];
|
||||||
final List<Contact> _contacts = [];
|
final List<Contact> _contacts = [];
|
||||||
|
final List<DiscoveryContact> _discoveredContacts = [];
|
||||||
final List<Channel> _channels = [];
|
final List<Channel> _channels = [];
|
||||||
final Map<String, List<Message>> _conversations = {};
|
final Map<String, List<Message>> _conversations = {};
|
||||||
final Map<int, List<ChannelMessage>> _channelMessages = {};
|
final Map<int, List<ChannelMessage>> _channelMessages = {};
|
||||||
@@ -155,6 +158,12 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
bool _batteryRequested = false;
|
bool _batteryRequested = false;
|
||||||
bool _awaitingSelfInfo = false;
|
bool _awaitingSelfInfo = false;
|
||||||
bool _preserveContactsOnRefresh = false;
|
bool _preserveContactsOnRefresh = false;
|
||||||
|
bool _autoAddUsers = false;
|
||||||
|
bool _autoAddRepeaters = false;
|
||||||
|
bool _autoAddRoomServers = false;
|
||||||
|
bool _autoAddSensors = false;
|
||||||
|
bool _overwriteOldest = false;
|
||||||
|
|
||||||
static const int _defaultMaxContacts = 32;
|
static const int _defaultMaxContacts = 32;
|
||||||
static const int _defaultMaxChannels = 8;
|
static const int _defaultMaxChannels = 8;
|
||||||
int _maxContacts = _defaultMaxContacts;
|
int _maxContacts = _defaultMaxContacts;
|
||||||
@@ -195,6 +204,7 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
final ChannelSettingsStore _channelSettingsStore = ChannelSettingsStore();
|
final ChannelSettingsStore _channelSettingsStore = ChannelSettingsStore();
|
||||||
final ContactSettingsStore _contactSettingsStore = ContactSettingsStore();
|
final ContactSettingsStore _contactSettingsStore = ContactSettingsStore();
|
||||||
final ContactStore _contactStore = ContactStore();
|
final ContactStore _contactStore = ContactStore();
|
||||||
|
final ContactDiscoveryStore _discoveryContactStore = ContactDiscoveryStore();
|
||||||
final ChannelStore _channelStore = ChannelStore();
|
final ChannelStore _channelStore = ChannelStore();
|
||||||
final UnreadStore _unreadStore = UnreadStore();
|
final UnreadStore _unreadStore = UnreadStore();
|
||||||
List<Channel> _cachedChannels = [];
|
List<Channel> _cachedChannels = [];
|
||||||
@@ -242,6 +252,10 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<DiscoveryContact> get discoveredContacts {
|
||||||
|
return List.unmodifiable(_discoveredContacts);
|
||||||
|
}
|
||||||
|
|
||||||
List<Channel> get channels => List.unmodifiable(_channels);
|
List<Channel> get channels => List.unmodifiable(_channels);
|
||||||
bool get isConnected => _state == MeshCoreConnectionState.connected;
|
bool get isConnected => _state == MeshCoreConnectionState.connected;
|
||||||
bool get isLoadingContacts => _isLoadingContacts;
|
bool get isLoadingContacts => _isLoadingContacts;
|
||||||
@@ -258,6 +272,11 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
int? get currentBwHz => _currentBwHz;
|
int? get currentBwHz => _currentBwHz;
|
||||||
int? get currentSf => _currentSf;
|
int? get currentSf => _currentSf;
|
||||||
int? get currentCr => _currentCr;
|
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;
|
bool? get clientRepeat => _clientRepeat;
|
||||||
int? get firmwareVerCode => _firmwareVerCode;
|
int? get firmwareVerCode => _firmwareVerCode;
|
||||||
Map<String, String>? get currentCustomVars => _currentCustomVars;
|
Map<String, String>? get currentCustomVars => _currentCustomVars;
|
||||||
@@ -602,6 +621,13 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> loadDiscoveredContactCache() async {
|
||||||
|
final cached = await _discoveryContactStore.loadContacts();
|
||||||
|
_discoveredContacts
|
||||||
|
..clear()
|
||||||
|
..addAll(cached);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> loadChannelSettings({int? maxChannels}) async {
|
Future<void> loadChannelSettings({int? maxChannels}) async {
|
||||||
_channelSmazEnabled.clear();
|
_channelSmazEnabled.clear();
|
||||||
final channelCount = maxChannels ?? _maxChannels;
|
final channelCount = maxChannels ?? _maxChannels;
|
||||||
@@ -852,6 +878,9 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
|
|
||||||
// Fetch channels so we can track unread counts for incoming messages
|
// Fetch channels so we can track unread counts for incoming messages
|
||||||
unawaited(getChannels());
|
unawaited(getChannels());
|
||||||
|
|
||||||
|
// Load discovered contacts from storage
|
||||||
|
unawaited(loadDiscoveredContactCache());
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint("Connection error: $e");
|
debugPrint("Connection error: $e");
|
||||||
await disconnect(manual: false);
|
await disconnect(manual: false);
|
||||||
@@ -972,6 +1001,7 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
_deviceDisplayName = null;
|
_deviceDisplayName = null;
|
||||||
_deviceId = null;
|
_deviceId = null;
|
||||||
_contacts.clear();
|
_contacts.clear();
|
||||||
|
_discoveredContacts.clear();
|
||||||
_conversations.clear();
|
_conversations.clear();
|
||||||
_loadedConversationKeys.clear();
|
_loadedConversationKeys.clear();
|
||||||
_selfPublicKey = null;
|
_selfPublicKey = null;
|
||||||
@@ -1064,6 +1094,7 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
await requestBatteryStatus(force: true);
|
await requestBatteryStatus(force: true);
|
||||||
await sendFrame(buildGetRadioSettingsFrame());
|
await sendFrame(buildGetRadioSettingsFrame());
|
||||||
await sendFrame(buildGetCustomVarsFrame());
|
await sendFrame(buildGetCustomVarsFrame());
|
||||||
|
await sendFrame(buildGetAutoAddFlagsFrame());
|
||||||
_scheduleSelfInfoRetry();
|
_scheduleSelfInfoRetry();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1074,7 +1105,7 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
await sendFrame(buildAppStartFrame());
|
await sendFrame(buildAppStartFrame());
|
||||||
await sendFrame(buildGetCustomVarsFrame());
|
await sendFrame(buildGetCustomVarsFrame());
|
||||||
await requestBatteryStatus();
|
await requestBatteryStatus();
|
||||||
|
await sendFrame(buildGetAutoAddFlagsFrame());
|
||||||
_scheduleSelfInfoRetry();
|
_scheduleSelfInfoRetry();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1903,8 +1934,8 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
case respCodeChannelInfo:
|
case respCodeChannelInfo:
|
||||||
_handleChannelInfo(frame);
|
_handleChannelInfo(frame);
|
||||||
break;
|
break;
|
||||||
case respCodeRadioSettings:
|
case respCodeAutoAddConfig:
|
||||||
_handleRadioSettings(frame);
|
_handleAutoAddConfig(frame);
|
||||||
break;
|
break;
|
||||||
case respCodeBattAndStorage:
|
case respCodeBattAndStorage:
|
||||||
_handleBatteryAndStorage(frame);
|
_handleBatteryAndStorage(frame);
|
||||||
@@ -1985,6 +2016,10 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
_selfLatitude = readInt32LE(frame, 36) / 1000000.0;
|
_selfLatitude = readInt32LE(frame, 36) / 1000000.0;
|
||||||
_selfLongitude = readInt32LE(frame, 40) / 1000000.0;
|
_selfLongitude = readInt32LE(frame, 40) / 1000000.0;
|
||||||
|
|
||||||
|
if (frame.length >= 47 && frame[47] == 0x00) {
|
||||||
|
sendFrame(buildSetOtherParamsFrame(0, 0, 0));
|
||||||
|
}
|
||||||
|
|
||||||
// Radio settings (if frame is long enough)
|
// Radio settings (if frame is long enough)
|
||||||
if (frame.length >= 58) {
|
if (frame.length >= 58) {
|
||||||
_currentFreqHz = readUint32LE(frame, 48);
|
_currentFreqHz = readUint32LE(frame, 48);
|
||||||
@@ -1992,7 +2027,6 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
_currentSf = frame[56];
|
_currentSf = frame[56];
|
||||||
_currentCr = frame[57];
|
_currentCr = frame[57];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Node name starts at offset 58 if frame is long enough
|
// Node name starts at offset 58 if frame is long enough
|
||||||
if (frame.length > 58) {
|
if (frame.length > 58) {
|
||||||
_selfName = readCString(frame, 58, frame.length - 58);
|
_selfName = readCString(frame, 58, frame.length - 58);
|
||||||
@@ -2056,25 +2090,6 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
unawaited(_requestNextQueuedMessage());
|
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) {
|
void _handleBatteryAndStorage(Uint8List frame) {
|
||||||
// Frame format from C++:
|
// Frame format from C++:
|
||||||
// [0] = RESP_CODE_BATT_AND_STORAGE
|
// [0] = RESP_CODE_BATT_AND_STORAGE
|
||||||
@@ -2275,6 +2290,10 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
await _contactStore.saveContacts(_contacts);
|
await _contactStore.saveContacts(_contacts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _persistDiscoveredContacts() async {
|
||||||
|
await _discoveryContactStore.saveContacts(_discoveredContacts);
|
||||||
|
}
|
||||||
|
|
||||||
int _latestContactLastmod() {
|
int _latestContactLastmod() {
|
||||||
if (_contacts.isEmpty) return 0;
|
if (_contacts.isEmpty) return 0;
|
||||||
var latest = 0;
|
var latest = 0;
|
||||||
@@ -3739,6 +3758,7 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//We ignore our own adverts
|
||||||
if (listEquals(publicKey, _selfPublicKey)) {
|
if (listEquals(publicKey, _selfPublicKey)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -3759,7 +3779,14 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
longitude: longitude,
|
longitude: longitude,
|
||||||
lastSeen: DateTime.fromMillisecondsSinceEpoch(timestamp * 1000),
|
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);
|
||||||
|
}
|
||||||
_updateDirectRepeater(newContact, snr, path);
|
_updateDirectRepeater(newContact, snr, path);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -3847,6 +3874,50 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
notifyListeners();
|
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) {
|
||||||
|
debugPrint('Discovered new contact: ${contact.name}');
|
||||||
|
final disContact = DiscoveryContact(
|
||||||
|
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) {
|
||||||
|
final settings = _appSettingsService!.settings;
|
||||||
|
if (settings.notificationsEnabled && settings.notifyOnNewAdvert) {
|
||||||
|
_notificationService.showAdvertNotification(
|
||||||
|
contactName: contact.name,
|
||||||
|
contactType: contact.typeLabel,
|
||||||
|
contactId: contact.publicKeyHex,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const int _phRouteMask = 0x03;
|
const int _phRouteMask = 0x03;
|
||||||
|
|||||||
@@ -167,6 +167,8 @@ const int cmdGetTelemetryReq = 39;
|
|||||||
const int cmdGetCustomVar = 40;
|
const int cmdGetCustomVar = 40;
|
||||||
const int cmdSetCustomVar = 41;
|
const int cmdSetCustomVar = 41;
|
||||||
const int cmdSendBinaryReq = 50;
|
const int cmdSendBinaryReq = 50;
|
||||||
|
const int cmdSetAutoAddConfig = 58;
|
||||||
|
const int cmdGetAutoAddConfig = 59;
|
||||||
|
|
||||||
// Text message types
|
// Text message types
|
||||||
const int txtTypePlain = 0;
|
const int txtTypePlain = 0;
|
||||||
@@ -200,8 +202,8 @@ const int respCodeDeviceInfo = 13;
|
|||||||
const int respCodeContactMsgRecvV3 = 16;
|
const int respCodeContactMsgRecvV3 = 16;
|
||||||
const int respCodeChannelMsgRecvV3 = 17;
|
const int respCodeChannelMsgRecvV3 = 17;
|
||||||
const int respCodeChannelInfo = 18;
|
const int respCodeChannelInfo = 18;
|
||||||
const int respCodeRadioSettings = 25;
|
|
||||||
const int respCodeCustomVars = 21;
|
const int respCodeCustomVars = 21;
|
||||||
|
const int respCodeAutoAddConfig = 25;
|
||||||
|
|
||||||
// Push codes (async from device)
|
// Push codes (async from device)
|
||||||
const int pushCodeAdvert = 0x80;
|
const int pushCodeAdvert = 0x80;
|
||||||
@@ -247,6 +249,18 @@ const int payloadTypeCONTROL = 0x0B; // a control/discovery packet
|
|||||||
const int payloadTypeRawCustom =
|
const int payloadTypeRawCustom =
|
||||||
0x0F; // custom packet as raw bytes, for applications with custom encryption, payloads, etc
|
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
|
// Sizes
|
||||||
const int pubKeySize = 32;
|
const int pubKeySize = 32;
|
||||||
const int maxPathSize = 64;
|
const int maxPathSize = 64;
|
||||||
@@ -297,7 +311,7 @@ const int contactNameOffset = 100;
|
|||||||
const int contactTimestampOffset = 132;
|
const int contactTimestampOffset = 132;
|
||||||
const int contactLatOffset = 136;
|
const int contactLatOffset = 136;
|
||||||
const int contactLonOffset = 140;
|
const int contactLonOffset = 140;
|
||||||
const int contactLastmodOffset = 144;
|
const int contactLastModOffset = 144;
|
||||||
const int contactFrameSize = 148;
|
const int contactFrameSize = 148;
|
||||||
|
|
||||||
// Message frame offsets
|
// Message frame offsets
|
||||||
@@ -685,6 +699,10 @@ Uint8List buildGetCustomVarsFrame() {
|
|||||||
return Uint8List.fromList([cmdGetCustomVar]);
|
return Uint8List.fromList([cmdGetCustomVar]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Uint8List buildGetAutoAddFlagsFrame() {
|
||||||
|
return Uint8List.fromList([cmdGetAutoAddConfig]);
|
||||||
|
}
|
||||||
|
|
||||||
// Calculate LoRa airtime for a packet
|
// Calculate LoRa airtime for a packet
|
||||||
// Based on Semtech SX127x datasheet formula
|
// Based on Semtech SX127x datasheet formula
|
||||||
// Returns airtime in milliseconds
|
// Returns airtime in milliseconds
|
||||||
@@ -826,20 +844,40 @@ Uint8List buildZeroHopContact(Uint8List pubKey) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Build CMD_SET_OTHER_PARAMS frame
|
// Build CMD_SET_OTHER_PARAMS frame
|
||||||
// Format: [cmd][allowAutoAddContacts][allowTelemetryFlags][advertLocationPolicy][multiAcks]
|
// Format: [cmd][allowTelemetryFlags][advertLocationPolicy][multiAcks]
|
||||||
Uint8List buildSetOtherParamsFrame(
|
Uint8List buildSetOtherParamsFrame(
|
||||||
bool allowAutoAddContacts,
|
|
||||||
int allowTelemetryFlags,
|
int allowTelemetryFlags,
|
||||||
int advertLocationPolicy,
|
int advertLocationPolicy,
|
||||||
int multiAcks,
|
int multiAcks,
|
||||||
) {
|
) {
|
||||||
final writer = BufferWriter();
|
final writer = BufferWriter();
|
||||||
writer.writeByte(cmdSetOtherParams);
|
writer.writeByte(cmdSetOtherParams);
|
||||||
writer.writeByte(
|
//Going forward the app will just set Auto Add Contacts to disabled, and use the filter flags
|
||||||
allowAutoAddContacts ? 0x00 : 0x01,
|
//Allow Auto Add Contacts use inverted logic (0x01 = disabled, 0x00 = enabled).
|
||||||
); // Allow Auto Add Contacts
|
writer.writeByte(0x01);
|
||||||
writer.writeByte(allowTelemetryFlags); // Allow Telemetry Flags
|
writer.writeByte(allowTelemetryFlags); // Allow Telemetry Flags
|
||||||
writer.writeByte(advertLocationPolicy); // Advertisement Location Policy
|
writer.writeByte(advertLocationPolicy); // Advertisement Location Policy
|
||||||
writer.writeByte(multiAcks); // Multi Acknowledgements
|
writer.writeByte(multiAcks); // Multi Acknowledgements
|
||||||
return writer.toBytes();
|
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();
|
||||||
|
}
|
||||||
|
|||||||
+19
-1
@@ -98,6 +98,8 @@
|
|||||||
"settings_locationIntervalInvalid": "Interval must be at least 60 seconds, and less than 86400 seconds.",
|
"settings_locationIntervalInvalid": "Interval must be at least 60 seconds, and less than 86400 seconds.",
|
||||||
"settings_latitude": "Latitude",
|
"settings_latitude": "Latitude",
|
||||||
"settings_longitude": "Longitude",
|
"settings_longitude": "Longitude",
|
||||||
|
"settings_contactSettings": "Contact Settings",
|
||||||
|
"settings_contactSettingsSubtitle": "Settings for how contacts are added.",
|
||||||
"settings_privacyMode": "Privacy Mode",
|
"settings_privacyMode": "Privacy Mode",
|
||||||
"settings_privacyModeSubtitle": "Hide name/location in advertisements",
|
"settings_privacyModeSubtitle": "Hide name/location in advertisements",
|
||||||
"settings_privacyModeToggle": "Toggle privacy mode to hide your name and location in advertisements.",
|
"settings_privacyModeToggle": "Toggle privacy mode to hide your name and location in advertisements.",
|
||||||
@@ -1837,5 +1839,21 @@
|
|||||||
"settings_gpxExportShareText": "Map data exported from meshcore-open",
|
"settings_gpxExportShareText": "Map data exported from meshcore-open",
|
||||||
"settings_gpxExportShareSubject": "meshcore-open GPX map data export",
|
"settings_gpxExportShareSubject": "meshcore-open GPX map data export",
|
||||||
"snrIndicator_nearByRepeaters": "Nearby Repeaters",
|
"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 enabled, the companion will overwrite the oldest contact not favoriteited when the contact list is full.",
|
||||||
|
"discoveredContacts_Title": "Discovered Contacts",
|
||||||
|
"discoveredContacts_noMatching": "No matching contacts",
|
||||||
|
"discoveredContacts_searchHint": "Search discovered contacts"
|
||||||
}
|
}
|
||||||
@@ -544,6 +544,18 @@ abstract class AppLocalizations {
|
|||||||
/// **'Longitude'**
|
/// **'Longitude'**
|
||||||
String get settings_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.
|
/// No description provided for @settings_privacyMode.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
@@ -5380,6 +5392,102 @@ abstract class AppLocalizations {
|
|||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Last seen'**
|
/// **'Last seen'**
|
||||||
String get snrIndicator_lastSeen;
|
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 enabled, the companion will overwrite the oldest contact not favoriteited when the contact list is full.'**
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
class _AppLocalizationsDelegate
|
class _AppLocalizationsDelegate
|
||||||
|
|||||||
@@ -234,6 +234,13 @@ class AppLocalizationsBg extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get settings_longitude => 'Дължина';
|
String get settings_longitude => 'Дължина';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_contactSettings => 'Contact Settings';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_contactSettingsSubtitle =>
|
||||||
|
'Settings for how contacts are added.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_privacyMode => 'Режим на поверителност';
|
String get settings_privacyMode => 'Режим на поверителност';
|
||||||
|
|
||||||
@@ -3112,4 +3119,58 @@ class AppLocalizationsBg extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get snrIndicator_lastSeen => 'Последно видян';
|
String get snrIndicator_lastSeen => 'Последно видян';
|
||||||
|
|
||||||
|
@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 enabled, the companion will overwrite the oldest contact not favoriteited when the contact list is full.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_Title => 'Discovered Contacts';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_noMatching => 'No matching contacts';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_searchHint => 'Search discovered contacts';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -233,6 +233,13 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get settings_longitude => 'Längengrad';
|
String get settings_longitude => 'Längengrad';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_contactSettings => 'Contact Settings';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_contactSettingsSubtitle =>
|
||||||
|
'Settings for how contacts are added.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_privacyMode => 'Privatsphäreeinstellung';
|
String get settings_privacyMode => 'Privatsphäreeinstellung';
|
||||||
|
|
||||||
@@ -3121,4 +3128,58 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get snrIndicator_lastSeen => 'Zuletzt gesehen';
|
String get snrIndicator_lastSeen => 'Zuletzt gesehen';
|
||||||
|
|
||||||
|
@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 enabled, the companion will overwrite the oldest contact not favoriteited when the contact list is full.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_Title => 'Discovered Contacts';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_noMatching => 'No matching contacts';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_searchHint => 'Search discovered contacts';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -232,6 +232,13 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get settings_longitude => 'Longitude';
|
String get settings_longitude => 'Longitude';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_contactSettings => 'Contact Settings';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_contactSettingsSubtitle =>
|
||||||
|
'Settings for how contacts are added.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_privacyMode => 'Privacy Mode';
|
String get settings_privacyMode => 'Privacy Mode';
|
||||||
|
|
||||||
@@ -3065,4 +3072,58 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get snrIndicator_lastSeen => 'Last seen';
|
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 enabled, the companion will overwrite the oldest contact not favoriteited when the contact list is full.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_Title => 'Discovered Contacts';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_noMatching => 'No matching contacts';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_searchHint => 'Search discovered contacts';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -233,6 +233,13 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get settings_longitude => 'Longitud';
|
String get settings_longitude => 'Longitud';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_contactSettings => 'Contact Settings';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_contactSettingsSubtitle =>
|
||||||
|
'Settings for how contacts are added.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_privacyMode => 'Modo Privacidad';
|
String get settings_privacyMode => 'Modo Privacidad';
|
||||||
|
|
||||||
@@ -3113,4 +3120,58 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get snrIndicator_lastSeen => 'Visto por última vez';
|
String get snrIndicator_lastSeen => 'Visto por última vez';
|
||||||
|
|
||||||
|
@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 enabled, the companion will overwrite the oldest contact not favoriteited when the contact list is full.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_Title => 'Discovered Contacts';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_noMatching => 'No matching contacts';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_searchHint => 'Search discovered contacts';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -234,6 +234,13 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get settings_longitude => 'Longitude';
|
String get settings_longitude => 'Longitude';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_contactSettings => 'Contact Settings';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_contactSettingsSubtitle =>
|
||||||
|
'Settings for how contacts are added.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_privacyMode => 'Mode de confidentialité';
|
String get settings_privacyMode => 'Mode de confidentialité';
|
||||||
|
|
||||||
@@ -3135,4 +3142,58 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get snrIndicator_lastSeen => 'Dernière fois vu';
|
String get snrIndicator_lastSeen => 'Dernière fois vu';
|
||||||
|
|
||||||
|
@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 enabled, the companion will overwrite the oldest contact not favoriteited when the contact list is full.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_Title => 'Discovered Contacts';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_noMatching => 'No matching contacts';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_searchHint => 'Search discovered contacts';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -233,6 +233,13 @@ class AppLocalizationsIt extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get settings_longitude => 'Longitudine';
|
String get settings_longitude => 'Longitudine';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_contactSettings => 'Contact Settings';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_contactSettingsSubtitle =>
|
||||||
|
'Settings for how contacts are added.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_privacyMode => 'Modalità Privacy';
|
String get settings_privacyMode => 'Modalità Privacy';
|
||||||
|
|
||||||
@@ -3116,4 +3123,58 @@ class AppLocalizationsIt extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get snrIndicator_lastSeen => 'Ultimo accesso';
|
String get snrIndicator_lastSeen => 'Ultimo accesso';
|
||||||
|
|
||||||
|
@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 enabled, the companion will overwrite the oldest contact not favoriteited when the contact list is full.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_Title => 'Discovered Contacts';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_noMatching => 'No matching contacts';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_searchHint => 'Search discovered contacts';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -233,6 +233,13 @@ class AppLocalizationsNl extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get settings_longitude => 'Lengtegraad';
|
String get settings_longitude => 'Lengtegraad';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_contactSettings => 'Contact Settings';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_contactSettingsSubtitle =>
|
||||||
|
'Settings for how contacts are added.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_privacyMode => 'Privacy Mode';
|
String get settings_privacyMode => 'Privacy Mode';
|
||||||
|
|
||||||
@@ -3103,4 +3110,58 @@ class AppLocalizationsNl extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get snrIndicator_lastSeen => 'Laatst gezien';
|
String get snrIndicator_lastSeen => 'Laatst gezien';
|
||||||
|
|
||||||
|
@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 enabled, the companion will overwrite the oldest contact not favoriteited when the contact list is full.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_Title => 'Discovered Contacts';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_noMatching => 'No matching contacts';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_searchHint => 'Search discovered contacts';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -235,6 +235,13 @@ class AppLocalizationsPl extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get settings_longitude => 'Długość';
|
String get settings_longitude => 'Długość';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_contactSettings => 'Contact Settings';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_contactSettingsSubtitle =>
|
||||||
|
'Settings for how contacts are added.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_privacyMode => 'Tryb Prywatny';
|
String get settings_privacyMode => 'Tryb Prywatny';
|
||||||
|
|
||||||
@@ -3116,4 +3123,58 @@ class AppLocalizationsPl extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get snrIndicator_lastSeen => 'Ostatnio widziany';
|
String get snrIndicator_lastSeen => 'Ostatnio widziany';
|
||||||
|
|
||||||
|
@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 enabled, the companion will overwrite the oldest contact not favoriteited when the contact list is full.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_Title => 'Discovered Contacts';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_noMatching => 'No matching contacts';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_searchHint => 'Search discovered contacts';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -234,6 +234,13 @@ class AppLocalizationsPt extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get settings_longitude => 'Longitude';
|
String get settings_longitude => 'Longitude';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_contactSettings => 'Contact Settings';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_contactSettingsSubtitle =>
|
||||||
|
'Settings for how contacts are added.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_privacyMode => 'Modo de Privacidade';
|
String get settings_privacyMode => 'Modo de Privacidade';
|
||||||
|
|
||||||
@@ -3111,4 +3118,58 @@ class AppLocalizationsPt extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get snrIndicator_lastSeen => 'Visto pela última vez';
|
String get snrIndicator_lastSeen => 'Visto pela última vez';
|
||||||
|
|
||||||
|
@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 enabled, the companion will overwrite the oldest contact not favoriteited when the contact list is full.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_Title => 'Discovered Contacts';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_noMatching => 'No matching contacts';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_searchHint => 'Search discovered contacts';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -232,6 +232,13 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get settings_longitude => 'Долгота';
|
String get settings_longitude => 'Долгота';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_contactSettings => 'Contact Settings';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_contactSettingsSubtitle =>
|
||||||
|
'Settings for how contacts are added.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_privacyMode => 'Режим конфиденциальности';
|
String get settings_privacyMode => 'Режим конфиденциальности';
|
||||||
|
|
||||||
@@ -3123,4 +3130,58 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get snrIndicator_lastSeen => 'Последний раз видели';
|
String get snrIndicator_lastSeen => 'Последний раз видели';
|
||||||
|
|
||||||
|
@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 enabled, the companion will overwrite the oldest contact not favoriteited when the contact list is full.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_Title => 'Discovered Contacts';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_noMatching => 'No matching contacts';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_searchHint => 'Search discovered contacts';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -233,6 +233,13 @@ class AppLocalizationsSk extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get settings_longitude => 'Dĺžka';
|
String get settings_longitude => 'Dĺžka';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_contactSettings => 'Contact Settings';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_contactSettingsSubtitle =>
|
||||||
|
'Settings for how contacts are added.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_privacyMode => 'Režim ochrany súkromia';
|
String get settings_privacyMode => 'Režim ochrany súkromia';
|
||||||
|
|
||||||
@@ -3098,4 +3105,58 @@ class AppLocalizationsSk extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get snrIndicator_lastSeen => 'Naposledy videný';
|
String get snrIndicator_lastSeen => 'Naposledy videný';
|
||||||
|
|
||||||
|
@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 enabled, the companion will overwrite the oldest contact not favoriteited when the contact list is full.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_Title => 'Discovered Contacts';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_noMatching => 'No matching contacts';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_searchHint => 'Search discovered contacts';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -233,6 +233,13 @@ class AppLocalizationsSl extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get settings_longitude => 'Dolžina';
|
String get settings_longitude => 'Dolžina';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_contactSettings => 'Contact Settings';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_contactSettingsSubtitle =>
|
||||||
|
'Settings for how contacts are added.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_privacyMode => 'Zasebnost';
|
String get settings_privacyMode => 'Zasebnost';
|
||||||
|
|
||||||
@@ -3103,4 +3110,58 @@ class AppLocalizationsSl extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get snrIndicator_lastSeen => 'Zadnjič videno';
|
String get snrIndicator_lastSeen => 'Zadnjič videno';
|
||||||
|
|
||||||
|
@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 enabled, the companion will overwrite the oldest contact not favoriteited when the contact list is full.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_Title => 'Discovered Contacts';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_noMatching => 'No matching contacts';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_searchHint => 'Search discovered contacts';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -232,6 +232,13 @@ class AppLocalizationsSv extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get settings_longitude => 'Längdgrad';
|
String get settings_longitude => 'Längdgrad';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_contactSettings => 'Contact Settings';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_contactSettingsSubtitle =>
|
||||||
|
'Settings for how contacts are added.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_privacyMode => 'Privatläge';
|
String get settings_privacyMode => 'Privatläge';
|
||||||
|
|
||||||
@@ -3081,4 +3088,58 @@ class AppLocalizationsSv extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get snrIndicator_lastSeen => 'Senast sedd';
|
String get snrIndicator_lastSeen => 'Senast sedd';
|
||||||
|
|
||||||
|
@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 enabled, the companion will overwrite the oldest contact not favoriteited when the contact list is full.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_Title => 'Discovered Contacts';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_noMatching => 'No matching contacts';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_searchHint => 'Search discovered contacts';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -232,6 +232,13 @@ class AppLocalizationsUk extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get settings_longitude => 'Довгота';
|
String get settings_longitude => 'Довгота';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_contactSettings => 'Contact Settings';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_contactSettingsSubtitle =>
|
||||||
|
'Settings for how contacts are added.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_privacyMode => 'Режим приватності';
|
String get settings_privacyMode => 'Режим приватності';
|
||||||
|
|
||||||
@@ -3130,4 +3137,58 @@ class AppLocalizationsUk extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get snrIndicator_lastSeen => 'Останній раз бачили';
|
String get snrIndicator_lastSeen => 'Останній раз бачили';
|
||||||
|
|
||||||
|
@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 enabled, the companion will overwrite the oldest contact not favoriteited when the contact list is full.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_Title => 'Discovered Contacts';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_noMatching => 'No matching contacts';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_searchHint => 'Search discovered contacts';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -226,6 +226,13 @@ class AppLocalizationsZh extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get settings_longitude => '经度';
|
String get settings_longitude => '经度';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_contactSettings => 'Contact Settings';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_contactSettingsSubtitle =>
|
||||||
|
'Settings for how contacts are added.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_privacyMode => '隐私模式';
|
String get settings_privacyMode => '隐私模式';
|
||||||
|
|
||||||
@@ -2890,4 +2897,58 @@ class AppLocalizationsZh extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get snrIndicator_lastSeen => '最近访问';
|
String get snrIndicator_lastSeen => '最近访问';
|
||||||
|
|
||||||
|
@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 enabled, the companion will overwrite the oldest contact not favoriteited when the contact list is full.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_Title => 'Discovered Contacts';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_noMatching => 'No matching contacts';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get discoveredContacts_searchHint => 'Search discovered contacts';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ class Contact {
|
|||||||
)
|
)
|
||||||
: Uint8List(0);
|
: Uint8List(0);
|
||||||
final name = readCString(data, contactNameOffset, maxNameSize);
|
final name = readCString(data, contactNameOffset, maxNameSize);
|
||||||
final lastmod = readUint32LE(data, contactLastmodOffset);
|
final lastmod = readUint32LE(data, contactLastModOffset);
|
||||||
|
|
||||||
double? lat, lon;
|
double? lat, lon;
|
||||||
final latRaw = readInt32LE(data, contactLatOffset);
|
final latRaw = readInt32LE(data, contactLatOffset);
|
||||||
|
|||||||
@@ -0,0 +1,137 @@
|
|||||||
|
import 'dart:typed_data';
|
||||||
|
import '../connector/meshcore_protocol.dart';
|
||||||
|
|
||||||
|
class DiscoveryContact {
|
||||||
|
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.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? publicKey,
|
||||||
|
String? name,
|
||||||
|
int? type,
|
||||||
|
int? pathLength,
|
||||||
|
Uint8List? path,
|
||||||
|
double? latitude,
|
||||||
|
double? longitude,
|
||||||
|
DateTime? lastSeen,
|
||||||
|
}) {
|
||||||
|
return DiscoveryContact(
|
||||||
|
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)}>";
|
||||||
|
}
|
||||||
|
|
||||||
|
Uint8List? get traceRouteBytes {
|
||||||
|
final pathBytes = path;
|
||||||
|
Uint8List? traceBytes;
|
||||||
|
|
||||||
|
if (pathBytes.isEmpty) {
|
||||||
|
traceBytes = Uint8List(1);
|
||||||
|
traceBytes[0] = publicKey[0];
|
||||||
|
return traceBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == advTypeRepeater || type == advTypeRoom) {
|
||||||
|
final len = (pathBytes.length + pathBytes.length + 1);
|
||||||
|
traceBytes = Uint8List(len);
|
||||||
|
traceBytes[pathBytes.length] = publicKey[0];
|
||||||
|
for (int i = 0; i < pathBytes.length; i++) {
|
||||||
|
traceBytes[i] = pathBytes[i];
|
||||||
|
if (i < pathBytes.length) {
|
||||||
|
traceBytes[len - 1 - i] = pathBytes[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (pathBytes.length < 2) {
|
||||||
|
return pathBytes[0] == 0 ? null : pathBytes;
|
||||||
|
}
|
||||||
|
final len = (pathBytes.length + pathBytes.length - 1);
|
||||||
|
traceBytes = Uint8List(len);
|
||||||
|
for (int i = 0; i < pathBytes.length; i++) {
|
||||||
|
traceBytes[i] = pathBytes[i];
|
||||||
|
if (i < pathBytes.length - 1) {
|
||||||
|
traceBytes[len - 1 - i] = pathBytes[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return traceBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) =>
|
||||||
|
other is DiscoveryContact && publicKeyHex == other.publicKeyHex;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => publicKeyHex.hashCode;
|
||||||
|
}
|
||||||
@@ -26,6 +26,7 @@ import '../widgets/room_login_dialog.dart';
|
|||||||
import '../widgets/unread_badge.dart';
|
import '../widgets/unread_badge.dart';
|
||||||
import 'channels_screen.dart';
|
import 'channels_screen.dart';
|
||||||
import 'chat_screen.dart';
|
import 'chat_screen.dart';
|
||||||
|
import 'discovery_screen.dart';
|
||||||
import 'map_screen.dart';
|
import 'map_screen.dart';
|
||||||
import 'repeater_hub_screen.dart';
|
import 'repeater_hub_screen.dart';
|
||||||
import 'settings_screen.dart';
|
import 'settings_screen.dart';
|
||||||
@@ -318,6 +319,21 @@ class _ContactsScreenState extends State<ContactsScreen>
|
|||||||
),
|
),
|
||||||
onTap: () => _disconnect(context, connector),
|
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(
|
PopupMenuItem(
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
|
|||||||
@@ -0,0 +1,347 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:meshcore_open/models/contact.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,
|
||||||
|
),
|
||||||
|
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],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildFilters(filteredAndSorted, connector) {
|
||||||
|
final l10n = context.l10n;
|
||||||
|
|
||||||
|
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 matchesContactQuery(
|
||||||
|
Contact(
|
||||||
|
publicKey: contact.publicKey,
|
||||||
|
name: contact.name,
|
||||||
|
type: contact.type,
|
||||||
|
pathLength: contact.pathLength,
|
||||||
|
path: contact.path,
|
||||||
|
lastSeen: contact.lastSeen,
|
||||||
|
),
|
||||||
|
searchQuery,
|
||||||
|
);
|
||||||
|
}).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();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showUnreadOnly) {
|
||||||
|
filtered = filtered.where((contact) {
|
||||||
|
return connector.getUnreadCountForContact(
|
||||||
|
Contact(
|
||||||
|
publicKey: contact.publicKey,
|
||||||
|
name: contact.name,
|
||||||
|
type: contact.type,
|
||||||
|
pathLength: contact.pathLength,
|
||||||
|
path: contact.path,
|
||||||
|
lastSeen: contact.lastSeen,
|
||||||
|
),
|
||||||
|
) >
|
||||||
|
0;
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (sortOption) {
|
||||||
|
case ContactSortOption.lastSeen:
|
||||||
|
filtered.sort(
|
||||||
|
(a, b) => _resolveLastSeen(b).compareTo(_resolveLastSeen(a)),
|
||||||
|
);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTime _resolveLastSeen(DiscoveryContact contact) {
|
||||||
|
if (contact.type != advTypeChat) return contact.lastSeen;
|
||||||
|
return contact.lastSeen.isAfter(contact.lastSeen)
|
||||||
|
? contact.lastSeen
|
||||||
|
: contact.lastSeen;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ import '../connector/meshcore_protocol.dart';
|
|||||||
import '../l10n/l10n.dart';
|
import '../l10n/l10n.dart';
|
||||||
import '../models/radio_settings.dart';
|
import '../models/radio_settings.dart';
|
||||||
import '../widgets/adaptive_app_bar_title.dart';
|
import '../widgets/adaptive_app_bar_title.dart';
|
||||||
|
import '../widgets/app_bar.dart';
|
||||||
import 'app_settings_screen.dart';
|
import 'app_settings_screen.dart';
|
||||||
import 'app_debug_log_screen.dart';
|
import 'app_debug_log_screen.dart';
|
||||||
import 'ble_debug_log_screen.dart';
|
import 'ble_debug_log_screen.dart';
|
||||||
@@ -43,8 +44,11 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
final l10n = context.l10n;
|
final l10n = context.l10n;
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: AdaptiveAppBarTitle(l10n.settings_title),
|
title: AppBarTitle(
|
||||||
centerTitle: true,
|
l10n.settings_title,
|
||||||
|
indicators: false,
|
||||||
|
subtitle: false,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
top: false,
|
top: false,
|
||||||
@@ -274,6 +278,14 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
onTap: () => _editLocation(context, connector),
|
onTap: () => _editLocation(context, connector),
|
||||||
),
|
),
|
||||||
const Divider(height: 1),
|
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(
|
ListTile(
|
||||||
leading: const Icon(Icons.visibility_off_outlined),
|
leading: const Icon(Icons.visibility_off_outlined),
|
||||||
title: Text(l10n.settings_privacyMode),
|
title: Text(l10n.settings_privacyMode),
|
||||||
@@ -849,6 +861,103 @@ 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: () async {
|
||||||
|
final frame = buildSetAutoAddConfigFrame(
|
||||||
|
autoAddChat: autoAddChat,
|
||||||
|
autoAddRepeater: autoAddRepeater,
|
||||||
|
autoAddRoomServer: autoAddRoomServer,
|
||||||
|
autoAddSensor: autoAddSensor,
|
||||||
|
overwriteOldest: overwriteOldest,
|
||||||
|
);
|
||||||
|
await connector.sendFrame(frame);
|
||||||
|
await connector.sendFrame(buildGetAutoAddFlagsFrame());
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Text(l10n.common_save),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _RadioSettingsDialog extends StatefulWidget {
|
class _RadioSettingsDialog extends StatefulWidget {
|
||||||
|
|||||||
@@ -215,8 +215,8 @@ class BleDebugLogService extends ChangeNotifier {
|
|||||||
return 'RESP_CODE_CHANNEL_MSG_RECV_V3';
|
return 'RESP_CODE_CHANNEL_MSG_RECV_V3';
|
||||||
case respCodeChannelInfo:
|
case respCodeChannelInfo:
|
||||||
return 'RESP_CODE_CHANNEL_INFO';
|
return 'RESP_CODE_CHANNEL_INFO';
|
||||||
case respCodeRadioSettings:
|
case respCodeAutoAddConfig:
|
||||||
return 'RESP_CODE_RADIO_SETTINGS';
|
return 'RESP_CODE_AUTO_ADD_CONFIG';
|
||||||
case pushCodeTraceData:
|
case pushCodeTraceData:
|
||||||
return 'PUSH_CODE_TRACE_DATA';
|
return 'PUSH_CODE_TRACE_DATA';
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
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 {
|
||||||
|
'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(
|
||||||
|
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),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,7 +9,16 @@ class AppBarTitle extends StatelessWidget {
|
|||||||
final String title;
|
final String title;
|
||||||
final Widget? leading;
|
final Widget? leading;
|
||||||
final Widget? trailing;
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -23,10 +32,10 @@ class AppBarTitle extends StatelessWidget {
|
|||||||
: MediaQuery.sizeOf(context).width;
|
: MediaQuery.sizeOf(context).width;
|
||||||
final compact = availableWidth < 240;
|
final compact = availableWidth < 240;
|
||||||
final showSubtitle =
|
final showSubtitle =
|
||||||
!compact && connector.isConnected && selfName != null;
|
!compact && connector.isConnected && selfName != null && subtitle;
|
||||||
final showBattery = availableWidth >= 60;
|
final showBattery = availableWidth >= 60;
|
||||||
final showSnr = availableWidth >= 110;
|
final showSnr = availableWidth >= 110;
|
||||||
final showIndicators = showBattery || showSnr;
|
final showIndicators = (showBattery || showSnr) && indicators;
|
||||||
|
|
||||||
return Row(
|
return Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user