mirror of
https://github.com/zjs81/meshcore-open.git
synced 2026-07-04 07:50:57 +10:00
Merge remote-tracking branch 'origin/dev' into gps-toggle-in-settings
# Conflicts: # lib/l10n/app_bg.arb # lib/l10n/app_de.arb # lib/l10n/app_es.arb # lib/l10n/app_fr.arb # lib/l10n/app_hu.arb # lib/l10n/app_it.arb # lib/l10n/app_ja.arb # lib/l10n/app_ko.arb # lib/l10n/app_localizations_es.dart # lib/l10n/app_localizations_it.dart # lib/l10n/app_localizations_nl.dart # lib/l10n/app_localizations_pt.dart # lib/l10n/app_localizations_sv.dart # lib/l10n/app_localizations_uk.dart # lib/l10n/app_nl.arb # lib/l10n/app_pl.arb # lib/l10n/app_pt.arb # lib/l10n/app_ru.arb # lib/l10n/app_sk.arb # lib/l10n/app_sl.arb # lib/l10n/app_sv.arb # lib/l10n/app_uk.arb # lib/l10n/app_zh.arb
This commit is contained in:
@@ -675,6 +675,27 @@ class MeshCoreConnector extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
void setContactUnreadCount(String contactKeyHex, int count) {
|
||||
_contactUnreadCount[contactKeyHex] = count;
|
||||
_unreadStore.saveContactUnreadCount(
|
||||
Map<String, int>.from(_contactUnreadCount),
|
||||
);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void setChannelUnreadCount(int channelIndex, int count) {
|
||||
final channel = _findChannelByIndex(channelIndex);
|
||||
if (channel != null) {
|
||||
channel.unreadCount = count;
|
||||
unawaited(
|
||||
_channelStore.saveChannels(
|
||||
_channels.isNotEmpty ? _channels : _cachedChannels,
|
||||
),
|
||||
);
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
void markChannelRead(int channelIndex) {
|
||||
final channel = _findChannelByIndex(channelIndex);
|
||||
if (channel != null && channel.unreadCount > 0) {
|
||||
@@ -2157,6 +2178,7 @@ class MeshCoreConnector extends ChangeNotifier {
|
||||
return;
|
||||
}
|
||||
_bleInitialSyncStarted = true;
|
||||
_pendingInitialContactsSync = true;
|
||||
|
||||
await _requestDeviceInfo();
|
||||
_startBatteryPolling();
|
||||
@@ -3030,13 +3052,7 @@ class MeshCoreConnector extends ChangeNotifier {
|
||||
_pendingChannelSentQueue.add(message.messageId);
|
||||
notifyListeners();
|
||||
|
||||
final trimmed = text.trim();
|
||||
final isStructuredPayload =
|
||||
trimmed.startsWith('g:') || trimmed.startsWith('m:');
|
||||
final outboundText =
|
||||
(isChannelSmazEnabled(channel.index) && !isStructuredPayload)
|
||||
? Smaz.encodeIfSmaller(text)
|
||||
: text;
|
||||
final outboundText = prepareChannelOutboundText(channel.index, text);
|
||||
await _waitForRadioQuiet(lastInboundRxTime: _lastChannelMsgRxTime);
|
||||
await sendFrame(
|
||||
buildSendChannelTextMsgFrame(channel.index, outboundText),
|
||||
@@ -4046,7 +4062,7 @@ class MeshCoreConnector extends ChangeNotifier {
|
||||
);
|
||||
} else {
|
||||
appLogger.info(
|
||||
"Discovered contact ${contact.name} (type ${contact.typeLabel}) not added due to auto-add settings",
|
||||
"Discovered contact ${contact.name} (type ${contact.typeLabelRaw}) not added due to auto-add settings",
|
||||
tag: 'Connector',
|
||||
);
|
||||
return;
|
||||
@@ -4068,7 +4084,7 @@ class MeshCoreConnector extends ChangeNotifier {
|
||||
if (settings.notificationsEnabled && settings.notifyOnNewAdvert) {
|
||||
_notificationService.showAdvertNotification(
|
||||
contactName: contact.name,
|
||||
contactType: contact.typeLabel,
|
||||
contactType: contact.typeLabelRaw,
|
||||
contactId: contact.publicKeyHex,
|
||||
);
|
||||
}
|
||||
@@ -4143,7 +4159,7 @@ class MeshCoreConnector extends ChangeNotifier {
|
||||
if (settings.notificationsEnabled && settings.notifyOnNewAdvert) {
|
||||
_notificationService.showAdvertNotification(
|
||||
contactName: contact.name,
|
||||
contactType: contact.typeLabel,
|
||||
contactType: contact.typeLabelRaw,
|
||||
contactId: contact.publicKeyHex,
|
||||
);
|
||||
}
|
||||
@@ -4166,7 +4182,9 @@ class MeshCoreConnector extends ChangeNotifier {
|
||||
if (_contacts.isEmpty) return 0;
|
||||
var latest = 0;
|
||||
for (final contact in _contacts) {
|
||||
final seconds = contact.lastSeen.millisecondsSinceEpoch ~/ 1000;
|
||||
// prefer lastmod per spec, fallback to lastseen
|
||||
final source = contact.lastModified ?? contact.lastSeen;
|
||||
final seconds = source.millisecondsSinceEpoch ~/ 1000;
|
||||
if (seconds > latest) {
|
||||
latest = seconds;
|
||||
}
|
||||
@@ -4495,6 +4513,16 @@ class MeshCoreConnector extends ChangeNotifier {
|
||||
return text;
|
||||
}
|
||||
|
||||
String prepareChannelOutboundText(int channelIndex, String text) {
|
||||
final trimmed = text.trim();
|
||||
final isStructuredPayload =
|
||||
trimmed.startsWith('g:') || trimmed.startsWith('m:');
|
||||
if (!isStructuredPayload && isChannelSmazEnabled(channelIndex)) {
|
||||
return Smaz.encodeIfSmaller(text);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
String _channelDisplayName(int channelIndex) {
|
||||
for (final channel in _channels) {
|
||||
if (channel.index != channelIndex) continue;
|
||||
@@ -6090,7 +6118,7 @@ class MeshCoreConnector extends ChangeNotifier {
|
||||
if (settings.notificationsEnabled && settings.notifyOnNewAdvert) {
|
||||
_notificationService.showAdvertNotification(
|
||||
contactName: contact.name,
|
||||
contactType: contact.typeLabel,
|
||||
contactType: contact.typeLabelRaw,
|
||||
contactId: contact.publicKeyHex,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -320,7 +320,7 @@ const int maxPathSize = 64;
|
||||
const int pathHashSize = 1;
|
||||
const int maxNameSize = 32;
|
||||
const int maxFrameSize = 172;
|
||||
const int appProtocolVersion = 3;
|
||||
const int appProtocolVersion = 4;
|
||||
// Matches firmware MAX_TEXT_LEN (10 * CIPHER_BLOCK_SIZE).
|
||||
const int maxTextPayloadBytes = 160;
|
||||
const int _sendTextMsgOverheadBytes =
|
||||
@@ -720,25 +720,19 @@ Uint8List buildUpdateContactPathFrame(
|
||||
final timestamp = DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
||||
writer.writeUInt32LE(timestamp);
|
||||
|
||||
if ((lat == null || lon == null) && lastModified != null) {
|
||||
// If lat/lon not provided, write zeros
|
||||
writer.writeInt32LE(0);
|
||||
writer.writeInt32LE(0);
|
||||
} else {
|
||||
// Latitude and Longitude are expected in degrees, convert to int by multiplying by 1e6
|
||||
// Latitude
|
||||
final latitude = lat ?? 0.0;
|
||||
writer.writeInt32LE((latitude * 1e6).round());
|
||||
|
||||
// Longitude
|
||||
final longitude = lon ?? 0.0;
|
||||
writer.writeInt32LE((longitude * 1e6).round());
|
||||
}
|
||||
|
||||
if (lastModified != null) {
|
||||
// Last modified
|
||||
final lastModifiedTimestamp = lastModified.millisecondsSinceEpoch ~/ 1000;
|
||||
writer.writeUInt32LE(lastModifiedTimestamp);
|
||||
// Optional [Lat x4, Lon x4][timestamp x4] tail per the doc comment above.
|
||||
// Emit 8 bytes of position (zero-filled when only lastModified is provided)
|
||||
// followed by an optional 4-byte timestamp. Earlier code emitted the
|
||||
// position block twice, which corrupted the tail and caused the firmware
|
||||
// to parse the second lat as the timestamp. See #427.
|
||||
final hasLocation = lat != null && lon != null;
|
||||
if (hasLocation || lastModified != null) {
|
||||
writer.writeInt32LE(hasLocation ? (lat * 1e6).round() : 0);
|
||||
writer.writeInt32LE(hasLocation ? (lon * 1e6).round() : 0);
|
||||
if (lastModified != null) {
|
||||
final lastModifiedTimestamp = lastModified.millisecondsSinceEpoch ~/ 1000;
|
||||
writer.writeUInt32LE(lastModifiedTimestamp);
|
||||
}
|
||||
}
|
||||
|
||||
return writer.toBytes();
|
||||
|
||||
Reference in New Issue
Block a user