mirror of
https://github.com/dz0ny/meshcore-sar.git
synced 2026-08-13 09:20:29 +00:00
feat: Add support for hash and private channels with corresponding UI updates
- Introduced new localization strings for channel types and their descriptions in Italian, German, Spanish, French, Croatian, Slovenian, and English. - Updated the `AppProvider` to handle channel info reception, including deletion of channels and updating the UI accordingly. - Enhanced `ChannelsProvider` to support channel removal and notify listeners. - Modified `ConnectionProvider` to include methods for checking empty channel slots and deleting channels. - Updated BLE service methods to handle channel deletion and verification. - Improved the `AddChannelDialog` to differentiate between hash and private channels, including dynamic UI elements based on channel type. - Added delete functionality for channels in the `ContactTile` widget with confirmation dialogs. - Adjusted permission request dialog behavior to allow dismissing by tapping outside.
This commit is contained in:
@@ -1043,7 +1043,19 @@ class BleResponseHandler {
|
||||
final flags = info['flags'] as int?;
|
||||
|
||||
debugPrint(' ✅ [ChannelInfo] Channel $channelIdx: "$channelName"');
|
||||
onChannelInfoReceived?.call(channelIdx, channelName, secret, flags);
|
||||
debugPrint(' Name length: ${channelName.length}');
|
||||
debugPrint(' Name bytes: ${channelName.codeUnits.map((c) => c.toRadixString(16).padLeft(2, '0')).join(' ')}');
|
||||
debugPrint(' Secret: ${secret.map((b) => b.toRadixString(16).padLeft(2, '0')).join('')}');
|
||||
debugPrint(' isEmpty: ${channelName.isEmpty}');
|
||||
debugPrint(' Callback exists: ${onChannelInfoReceived != null}');
|
||||
|
||||
if (onChannelInfoReceived != null) {
|
||||
debugPrint(' 🔔 Calling onChannelInfoReceived callback...');
|
||||
onChannelInfoReceived!(channelIdx, channelName, secret, flags);
|
||||
debugPrint(' ✅ Callback completed');
|
||||
} else {
|
||||
debugPrint(' ⚠️ No callback registered!');
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint(' ❌ [ChannelInfo] Parsing error: $e');
|
||||
|
||||
@@ -609,6 +609,9 @@ class MeshCoreBleService {
|
||||
///
|
||||
/// The secret must be exactly 16 bytes (128-bit encryption key).
|
||||
/// For the default public channel (channel 0), use [MeshCoreConstants.defaultPublicChannelSecret].
|
||||
///
|
||||
/// Note: Some firmware versions don't send ACK for SET_CHANNEL, so we use
|
||||
/// fire-and-forget and then verify with GET_CHANNEL.
|
||||
Future<void> setChannel({
|
||||
required int channelIdx,
|
||||
required String channelName,
|
||||
@@ -618,23 +621,58 @@ class MeshCoreBleService {
|
||||
debugPrint(' Channel index: $channelIdx');
|
||||
debugPrint(' Channel name: $channelName');
|
||||
debugPrint(' Secret length: ${secret.length} bytes');
|
||||
debugPrint(' Secret hex: ${secret.map((b) => b.toRadixString(16).padLeft(2, '0')).join('')}');
|
||||
|
||||
await _commandSender.writeDataAndWaitForAck(
|
||||
FrameBuilder.buildSetChannel(
|
||||
channelIdx: channelIdx,
|
||||
channelName: channelName,
|
||||
secret: secret,
|
||||
),
|
||||
// Send SET_CHANNEL command (fire-and-forget, no ACK expected)
|
||||
final setChannelData = FrameBuilder.buildSetChannel(
|
||||
channelIdx: channelIdx,
|
||||
channelName: channelName,
|
||||
secret: secret,
|
||||
);
|
||||
debugPrint('✅ [BLE] CMD_SET_CHANNEL sent successfully');
|
||||
debugPrint(' SET_CHANNEL data (${setChannelData.length} bytes): ${setChannelData.map((b) => b.toRadixString(16).padLeft(2, '0')).join(' ')}');
|
||||
|
||||
await _commandSender.writeData(setChannelData);
|
||||
debugPrint('✅ [BLE] CMD_SET_CHANNEL sent');
|
||||
|
||||
// Wait a bit for the device to process
|
||||
await Future.delayed(const Duration(milliseconds: 200));
|
||||
|
||||
// Verify the channel was set by reading it back
|
||||
debugPrint('🔍 [BLE] Verifying channel was set...');
|
||||
await getChannel(channelIdx);
|
||||
}
|
||||
|
||||
/// Sync all channels from the device (typically 0-39)
|
||||
/// This queries each channel to get its name and metadata
|
||||
Future<void> syncAllChannels({int maxChannels = 40}) async {
|
||||
debugPrint('📻 [Service] Syncing channels (0-${maxChannels - 1})...');
|
||||
/// Delete a channel by clearing its slot
|
||||
///
|
||||
/// This removes the channel from the device by setting it to an empty name and zeroed secret.
|
||||
/// The channel slot becomes available for reuse.
|
||||
///
|
||||
/// Note: Channel 0 (public channel) cannot be deleted.
|
||||
Future<void> deleteChannel(int channelIdx) async {
|
||||
if (channelIdx == 0) {
|
||||
throw ArgumentError('Cannot delete channel 0 (public channel)');
|
||||
}
|
||||
|
||||
for (int i = 0; i < maxChannels; i++) {
|
||||
debugPrint('🗑️ [BLE] Deleting channel $channelIdx...');
|
||||
|
||||
// Clear channel by setting empty name and zeroed secret
|
||||
await setChannel(
|
||||
channelIdx: channelIdx,
|
||||
channelName: '',
|
||||
secret: List.filled(16, 0),
|
||||
);
|
||||
|
||||
debugPrint('✅ [BLE] Channel $channelIdx deleted');
|
||||
}
|
||||
|
||||
/// Sync all channels from the device (channels 1-39)
|
||||
/// Skips channel 0 (public channel) which is implicit and not stored on device
|
||||
Future<void> syncAllChannels({int maxChannels = 40}) async {
|
||||
debugPrint('📻 [Service] Syncing channels (1-${maxChannels - 1})...');
|
||||
|
||||
// Start from 1 to skip channel 0 (public channel)
|
||||
// Channel 0 is implicit and handled separately via configurePublicChannel()
|
||||
for (int i = 1; i < maxChannels; i++) {
|
||||
await getChannel(i);
|
||||
// Small delay to avoid overwhelming the device
|
||||
await Future.delayed(const Duration(milliseconds: 50));
|
||||
|
||||
@@ -390,16 +390,21 @@ class FrameParser {
|
||||
|
||||
/// Parse ChannelInfo response
|
||||
static Map<String, dynamic> parseChannelInfo(BufferReader reader) {
|
||||
// Format: [channel_idx(1)][name(32)][secret(16)][flags(1)]
|
||||
// Minimum: 1 + 32 + 16 + 1 = 50 bytes
|
||||
if (reader.remainingBytesCount < 50) {
|
||||
// Format: [channel_idx(1)][name(32)][secret(16)][flags(1)?]
|
||||
// Minimum: 1 + 32 + 16 = 49 bytes (flags is optional)
|
||||
if (reader.remainingBytesCount < 49) {
|
||||
return {};
|
||||
}
|
||||
|
||||
final channelIdx = reader.readByte();
|
||||
final channelName = reader.readCString(32);
|
||||
final secret = reader.readBytes(16);
|
||||
final flags = reader.readByte();
|
||||
|
||||
// Flags field is optional (some firmware versions don't include it)
|
||||
int? flags;
|
||||
if (reader.remainingBytesCount >= 1) {
|
||||
flags = reader.readByte();
|
||||
}
|
||||
|
||||
return {
|
||||
'channelIdx': channelIdx,
|
||||
|
||||
Reference in New Issue
Block a user