mirror of
https://github.com/zjs81/meshcore-open.git
synced 2026-07-12 03:27:06 +10:00
Multibyte: fixed path trace repeater icon and potential bugs
This commit is contained in:
@@ -6094,9 +6094,9 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
pathLength: pathBytes.isEmpty
|
pathLength: pathBytes.isEmpty
|
||||||
? -1
|
? -1
|
||||||
: (pathBytes.length ~/ pathHashWidth),
|
: (pathBytes.length ~/ pathHashWidth),
|
||||||
path: Uint8List.fromList(
|
// Store hop order reversed for easier outgoing messages; keep bytes
|
||||||
pathBytes.reversed.toList(),
|
// inside each multi-byte hop in their original order.
|
||||||
), // Store path in reverse for easier use in outgoing messages
|
path: _reversePathByHop(pathBytes, pathHashWidth),
|
||||||
latitude: latitude,
|
latitude: latitude,
|
||||||
longitude: longitude,
|
longitude: longitude,
|
||||||
lastSeen: DateTime.fromMillisecondsSinceEpoch(timestamp * 1000),
|
lastSeen: DateTime.fromMillisecondsSinceEpoch(timestamp * 1000),
|
||||||
@@ -6179,9 +6179,9 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
name: name,
|
name: name,
|
||||||
type: type,
|
type: type,
|
||||||
pathLength: path.isEmpty ? -1 : (path.length ~/ pathHashWidth),
|
pathLength: path.isEmpty ? -1 : (path.length ~/ pathHashWidth),
|
||||||
path: Uint8List.fromList(
|
// Store hop order reversed for easier outgoing messages; keep bytes
|
||||||
path.reversed.toList(),
|
// inside each multi-byte hop in their original order.
|
||||||
), // Store path in reverse for easier use in outgoing messages
|
path: _reversePathByHop(path, pathHashWidth),
|
||||||
latitude: latitude,
|
latitude: latitude,
|
||||||
longitude: longitude,
|
longitude: longitude,
|
||||||
lastSeen: DateTime.fromMillisecondsSinceEpoch(timestamp * 1000),
|
lastSeen: DateTime.fromMillisecondsSinceEpoch(timestamp * 1000),
|
||||||
@@ -6229,7 +6229,7 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
latitude: hasLocation ? latitude : existing.latitude,
|
latitude: hasLocation ? latitude : existing.latitude,
|
||||||
longitude: hasLocation ? longitude : existing.longitude,
|
longitude: hasLocation ? longitude : existing.longitude,
|
||||||
name: hasName ? name : existing.name,
|
name: hasName ? name : existing.name,
|
||||||
path: Uint8List.fromList(path.reversed.toList()),
|
path: _reversePathByHop(path, pathHashWidth),
|
||||||
pathLength: path.isEmpty ? -1 : (path.length ~/ pathHashWidth),
|
pathLength: path.isEmpty ? -1 : (path.length ~/ pathHashWidth),
|
||||||
lastMessageAt: mergedLastMessageAt,
|
lastMessageAt: mergedLastMessageAt,
|
||||||
lastSeen: DateTime.fromMillisecondsSinceEpoch(timestamp * 1000),
|
lastSeen: DateTime.fromMillisecondsSinceEpoch(timestamp * 1000),
|
||||||
@@ -6522,7 +6522,7 @@ const int _payloadTypeGroupText = 0x05;
|
|||||||
const int _cipherMacSize = 2;
|
const int _cipherMacSize = 2;
|
||||||
|
|
||||||
/// Decodes the firmware's encoded path_len byte into actual byte length.
|
/// Decodes the firmware's encoded path_len byte into actual byte length.
|
||||||
/// Bits 0-5: hash count (0-63), Bits 6-7: hash size code (0=1byte, 1=2bytes, 2=3bytes).
|
/// Bits 0-5: hash count (0-63), Bits 6-7: hash size code (0=1byte ... 3=4bytes).
|
||||||
int _decodePathByteLen(int pathLenRaw) {
|
int _decodePathByteLen(int pathLenRaw) {
|
||||||
if (pathLenRaw == 0xFF || pathLenRaw == 0) return 0;
|
if (pathLenRaw == 0xFF || pathLenRaw == 0) return 0;
|
||||||
final hashCount = pathLenRaw & 63;
|
final hashCount = pathLenRaw & 63;
|
||||||
@@ -6535,6 +6535,17 @@ int _decodePathHashWidth(int pathLenRaw) {
|
|||||||
return ((pathLenRaw >> 6) & 0x03) + 1;
|
return ((pathLenRaw >> 6) & 0x03) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Uint8List _reversePathByHop(Uint8List pathBytes, int pathHashWidth) {
|
||||||
|
if (pathBytes.isEmpty) return Uint8List(0);
|
||||||
|
final width = pathHashWidth.clamp(1, 4).toInt();
|
||||||
|
final reversed = <int>[];
|
||||||
|
for (var i = pathBytes.length; i > 0; i -= width) {
|
||||||
|
final start = (i - width).clamp(0, pathBytes.length).toInt();
|
||||||
|
reversed.addAll(pathBytes.sublist(start, i));
|
||||||
|
}
|
||||||
|
return Uint8List.fromList(reversed);
|
||||||
|
}
|
||||||
|
|
||||||
class _RawPacket {
|
class _RawPacket {
|
||||||
final int header;
|
final int header;
|
||||||
final int routeType;
|
final int routeType;
|
||||||
|
|||||||
@@ -9,6 +9,13 @@ import '../helpers/snack_bar_builder.dart';
|
|||||||
|
|
||||||
enum _BleLogView { frames, rawLogRx }
|
enum _BleLogView { frames, rawLogRx }
|
||||||
|
|
||||||
|
int _decodeRawPathByteLen(int pathLenRaw) {
|
||||||
|
if (pathLenRaw == 0xFF || pathLenRaw == 0) return 0;
|
||||||
|
final hashCount = pathLenRaw & 0x3F;
|
||||||
|
final hashWidth = ((pathLenRaw >> 6) & 0x03) + 1;
|
||||||
|
return hashCount * hashWidth;
|
||||||
|
}
|
||||||
|
|
||||||
class BleDebugLogScreen extends StatefulWidget {
|
class BleDebugLogScreen extends StatefulWidget {
|
||||||
const BleDebugLogScreen({super.key});
|
const BleDebugLogScreen({super.key});
|
||||||
|
|
||||||
@@ -208,16 +215,17 @@ class _BleDebugLogScreenState extends State<BleDebugLogScreen> {
|
|||||||
rawHex: _bytesToHex(raw),
|
rawHex: _bytesToHex(raw),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
final pathLen = raw[index++];
|
final pathLenRaw = raw[index++];
|
||||||
if (raw.length < index + pathLen) {
|
final pathByteLen = _decodeRawPathByteLen(pathLenRaw);
|
||||||
|
if (raw.length < index + pathByteLen) {
|
||||||
return _RawPacketInfo(
|
return _RawPacketInfo(
|
||||||
title: 'RX RAW_LOG_RX_DATA • ${_payloadTypeLabel(payloadType)}',
|
title: 'RX RAW_LOG_RX_DATA • ${_payloadTypeLabel(payloadType)}',
|
||||||
summary: 'Truncated path',
|
summary: 'Truncated path',
|
||||||
rawHex: _bytesToHex(raw),
|
rawHex: _bytesToHex(raw),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
final pathBytes = raw.sublist(index, index + pathLen);
|
final pathBytes = raw.sublist(index, index + pathByteLen);
|
||||||
index += pathLen;
|
index += pathByteLen;
|
||||||
if (raw.length <= index) {
|
if (raw.length <= index) {
|
||||||
return _RawPacketInfo(
|
return _RawPacketInfo(
|
||||||
title: 'RX RAW_LOG_RX_DATA • ${_payloadTypeLabel(payloadType)}',
|
title: 'RX RAW_LOG_RX_DATA • ${_payloadTypeLabel(payloadType)}',
|
||||||
@@ -230,7 +238,7 @@ class _BleDebugLogScreenState extends State<BleDebugLogScreen> {
|
|||||||
final title =
|
final title =
|
||||||
'RX ${_payloadTypeLabel(payloadType)} • ${_routeLabel(routeType)} • v$payloadVer';
|
'RX ${_payloadTypeLabel(payloadType)} • ${_routeLabel(routeType)} • v$payloadVer';
|
||||||
final summary = _decodePayloadSummary(payloadType, payload);
|
final summary = _decodePayloadSummary(payloadType, payload);
|
||||||
final pathSummary = pathLen > 0
|
final pathSummary = pathByteLen > 0
|
||||||
? 'Path=${_bytesToHex(pathBytes)}'
|
? 'Path=${_bytesToHex(pathBytes)}'
|
||||||
: 'Path=none';
|
: 'Path=none';
|
||||||
final detail = '$summary • $pathSummary • len=${raw.length}';
|
final detail = '$summary • $pathSummary • len=${raw.length}';
|
||||||
|
|||||||
+130
-41
@@ -57,6 +57,12 @@ Uint8List _lastHopChunk(Uint8List path, int hopWidth) {
|
|||||||
return Uint8List.fromList(path.sublist(path.length - width));
|
return Uint8List.fromList(path.sublist(path.length - width));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool _matchesHopPrefix(Uint8List a, Uint8List b) {
|
||||||
|
if (a.isEmpty || b.isEmpty) return false;
|
||||||
|
final width = min(a.length, b.length);
|
||||||
|
return listEquals(a.sublist(0, width), b.sublist(0, width));
|
||||||
|
}
|
||||||
|
|
||||||
class PathTraceMapScreen extends StatefulWidget {
|
class PathTraceMapScreen extends StatefulWidget {
|
||||||
final String title;
|
final String title;
|
||||||
final Uint8List path;
|
final Uint8List path;
|
||||||
@@ -114,13 +120,77 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
|
|||||||
Contact? _targetContact;
|
Contact? _targetContact;
|
||||||
Uint8List? _sentTagBytes;
|
Uint8List? _sentTagBytes;
|
||||||
|
|
||||||
String _formatPathPrefixes(Uint8List pathBytes) {
|
String _formatPathPrefixes(Uint8List pathBytes, [int? hashByteWidth]) {
|
||||||
return PathHelper.splitPathBytes(
|
return PathHelper.splitPathBytes(
|
||||||
pathBytes,
|
pathBytes,
|
||||||
widget.pathHashByteWidth,
|
hashByteWidth ?? widget.pathHashByteWidth,
|
||||||
).map(PathHelper.formatHopHex).join(',');
|
).map(PathHelper.formatHopHex).join(',');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int _traceHashByteWidth(int pathHashByteWidth) {
|
||||||
|
final width = pathHashByteWidth.clamp(1, pubKeySize).toInt();
|
||||||
|
if (width <= 1) return 1;
|
||||||
|
if (width == 2) return 2;
|
||||||
|
// Trace packets encode hash width as 1 << flags, so 3-byte path hashes
|
||||||
|
// must be traced with a 4-byte public-key prefix.
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _traceFlagsForHashWidth(int traceHashByteWidth) {
|
||||||
|
if (traceHashByteWidth <= 1) return 0;
|
||||||
|
if (traceHashByteWidth == 2) return 1;
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
Uint8List _reversePathByHop(Uint8List pathBytes, int hopWidth) {
|
||||||
|
final reversedHops = PathHelper.splitPathBytes(
|
||||||
|
pathBytes,
|
||||||
|
hopWidth,
|
||||||
|
).reversed;
|
||||||
|
final bytes = <int>[];
|
||||||
|
for (final hop in reversedHops) {
|
||||||
|
bytes.addAll(hop);
|
||||||
|
}
|
||||||
|
return Uint8List.fromList(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
Uint8List? _expandHopForTrace(
|
||||||
|
Uint8List hop,
|
||||||
|
int traceHashByteWidth,
|
||||||
|
MeshCoreConnector connector,
|
||||||
|
) {
|
||||||
|
if (hop.length == traceHashByteWidth) return hop;
|
||||||
|
|
||||||
|
final candidates = <Contact>[
|
||||||
|
...?widget.pathContacts,
|
||||||
|
if (widget.targetContact != null) widget.targetContact!,
|
||||||
|
...connector.allContactsUnfiltered,
|
||||||
|
];
|
||||||
|
for (final contact in candidates) {
|
||||||
|
if (contact.publicKey.length < traceHashByteWidth) continue;
|
||||||
|
if (!listEquals(contact.publicKey.sublist(0, hop.length), hop)) continue;
|
||||||
|
return Uint8List.fromList(
|
||||||
|
contact.publicKey.sublist(0, traceHashByteWidth),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Uint8List? _tracePathFromBytes(
|
||||||
|
Uint8List pathBytes,
|
||||||
|
int traceHashByteWidth,
|
||||||
|
MeshCoreConnector connector,
|
||||||
|
) {
|
||||||
|
final hops = PathHelper.splitPathBytes(pathBytes, widget.pathHashByteWidth);
|
||||||
|
final traceBytes = <int>[];
|
||||||
|
for (final hop in hops) {
|
||||||
|
final traceHop = _expandHopForTrace(hop, traceHashByteWidth, connector);
|
||||||
|
if (traceHop == null) return null;
|
||||||
|
traceBytes.addAll(traceHop);
|
||||||
|
}
|
||||||
|
return Uint8List.fromList(traceBytes);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@@ -198,12 +268,16 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Uint8List buildPath(Uint8List pathBytes) {
|
Uint8List? buildPath(
|
||||||
|
Uint8List pathBytes,
|
||||||
|
int traceHashByteWidth,
|
||||||
|
MeshCoreConnector connector,
|
||||||
|
) {
|
||||||
final pathHops = PathHelper.splitPathBytes(
|
final pathHops = PathHelper.splitPathBytes(
|
||||||
pathBytes,
|
pathBytes,
|
||||||
widget.pathHashByteWidth,
|
widget.pathHashByteWidth,
|
||||||
);
|
);
|
||||||
final hopWidth = widget.pathHashByteWidth.clamp(1, pubKeySize);
|
final hopWidth = traceHashByteWidth.clamp(1, pubKeySize).toInt();
|
||||||
|
|
||||||
// Compute targetPrefix if targetContact is provided
|
// Compute targetPrefix if targetContact is provided
|
||||||
Uint8List? targetPrefix;
|
Uint8List? targetPrefix;
|
||||||
@@ -215,12 +289,17 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final outboundHops = <Uint8List>[...pathHops];
|
final outboundHops = <Uint8List>[];
|
||||||
|
for (final hop in pathHops) {
|
||||||
|
final traceHop = _expandHopForTrace(hop, traceHashByteWidth, connector);
|
||||||
|
if (traceHop == null) return null;
|
||||||
|
outboundHops.add(traceHop);
|
||||||
|
}
|
||||||
if (targetPrefix != null) {
|
if (targetPrefix != null) {
|
||||||
// Check if targetPrefix is already the last hop in pathHops to avoid duplication
|
// Check if targetPrefix is already the last hop in pathHops to avoid duplication
|
||||||
bool alreadyEndedWithTarget = false;
|
bool alreadyEndedWithTarget = false;
|
||||||
if (pathHops.isNotEmpty) {
|
if (outboundHops.isNotEmpty) {
|
||||||
if (listEquals(pathHops.last, targetPrefix)) {
|
if (listEquals(outboundHops.last, targetPrefix)) {
|
||||||
alreadyEndedWithTarget = true;
|
alreadyEndedWithTarget = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -255,19 +334,32 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final connector = Provider.of<MeshCoreConnector>(context, listen: false);
|
||||||
|
final traceHashByteWidth = _traceHashByteWidth(widget.pathHashByteWidth);
|
||||||
final pathTmp = widget.reversePathAround
|
final pathTmp = widget.reversePathAround
|
||||||
? Uint8List.fromList(widget.path.reversed.toList())
|
? _reversePathByHop(widget.path, widget.pathHashByteWidth)
|
||||||
: widget.path;
|
: widget.path;
|
||||||
|
|
||||||
final path = widget.flipPathAround ? buildPath(pathTmp) : pathTmp;
|
final path = widget.flipPathAround
|
||||||
|
? buildPath(pathTmp, traceHashByteWidth, connector)
|
||||||
|
: _tracePathFromBytes(pathTmp, traceHashByteWidth, connector);
|
||||||
|
|
||||||
|
if (path == null) {
|
||||||
|
if (!mounted) return;
|
||||||
|
setState(() {
|
||||||
|
_isLoading = false;
|
||||||
|
_failed2Loaded = true;
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
appLogger.info(
|
appLogger.info(
|
||||||
'Initiating path trace with path: ${_formatPathPrefixes(path)}',
|
'Initiating path trace with path: '
|
||||||
|
'${_formatPathPrefixes(path, traceHashByteWidth)}',
|
||||||
tag: 'PathTraceMapScreen',
|
tag: 'PathTraceMapScreen',
|
||||||
noNotify: !mounted,
|
noNotify: !mounted,
|
||||||
);
|
);
|
||||||
|
|
||||||
final connector = Provider.of<MeshCoreConnector>(context, listen: false);
|
|
||||||
final sentTag = DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
final sentTag = DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
||||||
_sentTagBytes = Uint8List(4)
|
_sentTagBytes = Uint8List(4)
|
||||||
..[0] = sentTag & 0xFF
|
..[0] = sentTag & 0xFF
|
||||||
@@ -275,7 +367,7 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
|
|||||||
..[2] = (sentTag >> 16) & 0xFF
|
..[2] = (sentTag >> 16) & 0xFF
|
||||||
..[3] = (sentTag >> 24) & 0xFF;
|
..[3] = (sentTag >> 24) & 0xFF;
|
||||||
|
|
||||||
final flags = (widget.pathHashByteWidth - 1).clamp(0, 3);
|
final flags = _traceFlagsForHashWidth(traceHashByteWidth);
|
||||||
final tracePayload = path.isEmpty ? Uint8List.fromList([0x00]) : path;
|
final tracePayload = path.isEmpty ? Uint8List.fromList([0x00]) : path;
|
||||||
|
|
||||||
final frame = buildTraceReq(
|
final frame = buildTraceReq(
|
||||||
@@ -354,23 +446,19 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
|
|||||||
try {
|
try {
|
||||||
buffer.skipBytes(2); // Skip push code and reserved byte
|
buffer.skipBytes(2); // Skip push code and reserved byte
|
||||||
final pathLenByte = buffer.readUInt8();
|
final pathLenByte = buffer.readUInt8();
|
||||||
int hopCount = 0;
|
final flags = buffer.readUInt8();
|
||||||
int width = widget.pathHashByteWidth; // fallback
|
buffer.skipBytes(4); // Skip tag data
|
||||||
int pathLength = 0;
|
buffer.skipBytes(4); // Skip auth code
|
||||||
if (pathLenByte != 0xFF) {
|
var width = _traceHashByteWidth(1 << (flags & 0x03));
|
||||||
final mode = (pathLenByte & 0xC0) >> 6;
|
var pathLength = pathLenByte == 0xFF ? 0 : pathLenByte;
|
||||||
if (mode > 0) {
|
if (pathLength > buffer.remaining && (pathLenByte & 0xC0) != 0) {
|
||||||
hopCount = pathLenByte & 0x3F;
|
final packedWidth = ((pathLenByte & 0xC0) >> 6) + 1;
|
||||||
width = mode + 1;
|
final packedLength = (pathLenByte & 0x3F) * packedWidth;
|
||||||
pathLength = hopCount * width;
|
if (packedLength <= buffer.remaining) {
|
||||||
} else {
|
width = packedWidth;
|
||||||
width = widget.pathHashByteWidth;
|
pathLength = packedLength;
|
||||||
pathLength = pathLenByte;
|
|
||||||
hopCount = pathLength ~/ width;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
buffer.skipBytes(5); // Skip Flag byte and tag data
|
|
||||||
buffer.skipBytes(4); // Skip auth code
|
|
||||||
final pathBytes = buffer.readBytes(pathLength);
|
final pathBytes = buffer.readBytes(pathLength);
|
||||||
final pathData = PathHelper.splitPathBytes(pathBytes, width);
|
final pathData = PathHelper.splitPathBytes(pathBytes, width);
|
||||||
List<double> snrData = buffer
|
List<double> snrData = buffer
|
||||||
@@ -390,7 +478,7 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
|
|||||||
lastSeen: DateTime.now(),
|
lastSeen: DateTime.now(),
|
||||||
);
|
);
|
||||||
if (widget.pathContacts != null) {
|
if (widget.pathContacts != null) {
|
||||||
final hopWidth = widget.pathHashByteWidth.clamp(1, pubKeySize);
|
final hopWidth = width.clamp(1, pubKeySize).toInt();
|
||||||
pathContacts = {
|
pathContacts = {
|
||||||
for (var c in widget.pathContacts!)
|
for (var c in widget.pathContacts!)
|
||||||
if (c.publicKey.length >= hopWidth)
|
if (c.publicKey.length >= hopWidth)
|
||||||
@@ -436,8 +524,10 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
|
|||||||
(c) =>
|
(c) =>
|
||||||
c.hasLocation &&
|
c.hasLocation &&
|
||||||
c.path.isNotEmpty &&
|
c.path.isNotEmpty &&
|
||||||
_hopKey(_lastHopChunk(c.path, widget.pathHashByteWidth)) ==
|
_matchesHopPrefix(
|
||||||
hopKey,
|
_lastHopChunk(c.path, widget.pathHashByteWidth),
|
||||||
|
hop,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.toList();
|
.toList();
|
||||||
if (peers.isNotEmpty) {
|
if (peers.isNotEmpty) {
|
||||||
@@ -483,10 +573,10 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
|
|||||||
(c) =>
|
(c) =>
|
||||||
c.hasLocation &&
|
c.hasLocation &&
|
||||||
c.path.isNotEmpty &&
|
c.path.isNotEmpty &&
|
||||||
_hopKey(
|
_matchesHopPrefix(
|
||||||
_lastHopChunk(c.path, widget.pathHashByteWidth),
|
_lastHopChunk(c.path, widget.pathHashByteWidth),
|
||||||
) ==
|
lastHop,
|
||||||
lastHopKey,
|
),
|
||||||
)
|
)
|
||||||
.toList();
|
.toList();
|
||||||
if (peers.isNotEmpty) {
|
if (peers.isNotEmpty) {
|
||||||
@@ -697,6 +787,10 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
|
|||||||
? LatLng(contact.latitude!, contact.longitude!)
|
? LatLng(contact.latitude!, contact.longitude!)
|
||||||
: inferred!;
|
: inferred!;
|
||||||
final label = PathHelper.formatHopHex(hop);
|
final label = PathHelper.formatHopHex(hop);
|
||||||
|
final shortLabel = label.length > 2 ? label.substring(0, 2) : label;
|
||||||
|
final fullLabel = label.length > 2
|
||||||
|
? (contact?.name != null ? '$label: ${contact!.name}' : label)
|
||||||
|
: (contact?.name ?? label);
|
||||||
|
|
||||||
markers.add(
|
markers.add(
|
||||||
Marker(
|
Marker(
|
||||||
@@ -721,7 +815,7 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
|
|||||||
),
|
),
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: Text(
|
child: Text(
|
||||||
hasGps ? label : '~$label',
|
hasGps ? shortLabel : '~$shortLabel',
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
@@ -732,12 +826,7 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
if (showLabels) {
|
if (showLabels) {
|
||||||
markers.add(
|
markers.add(_buildNodeLabelMarker(point: point, label: fullLabel));
|
||||||
_buildNodeLabelMarker(
|
|
||||||
point: point,
|
|
||||||
label: contact?.name ?? '~$label',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
hopLastLast = hopLast;
|
hopLastLast = hopLast;
|
||||||
hopLast = hopKey;
|
hopLast = hopKey;
|
||||||
|
|||||||
Reference in New Issue
Block a user