mirror of
https://github.com/dz0ny/meshcore-sar.git
synced 2026-08-12 00:40:28 +00:00
feat: Add display name getter to DeviceInfo and update usage in HomeScreen; refactor contact type handling in MapMarkers
This commit is contained in:
@@ -114,6 +114,15 @@ class DeviceInfo {
|
|||||||
.join('');
|
.join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get display name with "MeshCore-" prefix removed
|
||||||
|
String? get displayName {
|
||||||
|
if (deviceName == null) return null;
|
||||||
|
if (deviceName!.startsWith('MeshCore-')) {
|
||||||
|
return deviceName!.substring(9); // Remove "MeshCore-" (9 characters)
|
||||||
|
}
|
||||||
|
return deviceName;
|
||||||
|
}
|
||||||
|
|
||||||
DeviceInfo copyWith({
|
DeviceInfo copyWith({
|
||||||
String? deviceId,
|
String? deviceId,
|
||||||
String? deviceName,
|
String? deviceName,
|
||||||
|
|||||||
@@ -104,6 +104,31 @@ class ConnectionProvider with ChangeNotifier {
|
|||||||
onTelemetryReceived?.call(publicKey, lppData);
|
onTelemetryReceived?.call(publicKey, lppData);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_bleService.onSelfInfoReceived = (selfInfo) {
|
||||||
|
print('📥 [Provider] Received SelfInfo:');
|
||||||
|
print(' TX Power: ${selfInfo['txPower']} / ${selfInfo['maxTxPower']} dBm');
|
||||||
|
print(' Radio: freq=${selfInfo['radioFreq']}, bw=${selfInfo['radioBw']}, sf=${selfInfo['radioSf']}, cr=${selfInfo['radioCr']}');
|
||||||
|
print(' Position: ${selfInfo['advLat'] / 1000000.0}, ${selfInfo['advLon'] / 1000000.0}');
|
||||||
|
print(' Self Name: ${selfInfo['selfName']}');
|
||||||
|
|
||||||
|
_deviceInfo = _deviceInfo.copyWith(
|
||||||
|
deviceType: selfInfo['deviceType'] as int?,
|
||||||
|
txPower: selfInfo['txPower'] as int?,
|
||||||
|
maxTxPower: selfInfo['maxTxPower'] as int?,
|
||||||
|
publicKey: selfInfo['publicKey'] as Uint8List?,
|
||||||
|
advLat: selfInfo['advLat'] as int?,
|
||||||
|
advLon: selfInfo['advLon'] as int?,
|
||||||
|
manualAddContacts: selfInfo['manualAddContacts'] as bool?,
|
||||||
|
radioFreq: selfInfo['radioFreq'] as int?,
|
||||||
|
radioBw: selfInfo['radioBw'] as int?,
|
||||||
|
radioSf: selfInfo['radioSf'] as int?,
|
||||||
|
radioCr: selfInfo['radioCr'] as int?,
|
||||||
|
selfName: selfInfo['selfName'] as String?,
|
||||||
|
);
|
||||||
|
notifyListeners();
|
||||||
|
print('✅ [Provider] Device info updated with SelfInfo');
|
||||||
|
};
|
||||||
|
|
||||||
// Activity indicators
|
// Activity indicators
|
||||||
_bleService.onRxActivity = () {
|
_bleService.onRxActivity = () {
|
||||||
_rxActivity = true;
|
_rxActivity = true;
|
||||||
@@ -299,6 +324,103 @@ class ConnectionProvider with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set advertised name
|
||||||
|
Future<void> setAdvertName(String name) async {
|
||||||
|
if (!_bleService.isConnected) {
|
||||||
|
_error = 'Not connected to device';
|
||||||
|
notifyListeners();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await _bleService.setAdvertName(name);
|
||||||
|
} catch (e) {
|
||||||
|
_error = 'Failed to set name: $e';
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set advertised position
|
||||||
|
Future<void> setAdvertLatLon({
|
||||||
|
required double latitude,
|
||||||
|
required double longitude,
|
||||||
|
}) async {
|
||||||
|
if (!_bleService.isConnected) {
|
||||||
|
_error = 'Not connected to device';
|
||||||
|
notifyListeners();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await _bleService.setAdvertLatLon(
|
||||||
|
latitude: latitude,
|
||||||
|
longitude: longitude,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
_error = 'Failed to set position: $e';
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set radio parameters
|
||||||
|
Future<void> setRadioParams({
|
||||||
|
required int frequency,
|
||||||
|
required int bandwidth,
|
||||||
|
required int spreadingFactor,
|
||||||
|
required int codingRate,
|
||||||
|
}) async {
|
||||||
|
if (!_bleService.isConnected) {
|
||||||
|
_error = 'Not connected to device';
|
||||||
|
notifyListeners();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await _bleService.setRadioParams(
|
||||||
|
frequency: frequency,
|
||||||
|
bandwidth: bandwidth,
|
||||||
|
spreadingFactor: spreadingFactor,
|
||||||
|
codingRate: codingRate,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
_error = 'Failed to set radio params: $e';
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set transmit power
|
||||||
|
Future<void> setTxPower(int powerDbm) async {
|
||||||
|
if (!_bleService.isConnected) {
|
||||||
|
_error = 'Not connected to device';
|
||||||
|
notifyListeners();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await _bleService.setTxPower(powerDbm);
|
||||||
|
} catch (e) {
|
||||||
|
_error = 'Failed to set TX power: $e';
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Request fresh device info (triggers SelfInfo response)
|
||||||
|
Future<void> refreshDeviceInfo() async {
|
||||||
|
if (!_bleService.isConnected) {
|
||||||
|
_error = 'Not connected to device';
|
||||||
|
notifyListeners();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// The device query command triggers a SelfInfo response
|
||||||
|
await _bleService.refreshDeviceInfo();
|
||||||
|
} catch (e) {
|
||||||
|
_error = 'Failed to refresh device info: $e';
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Clear error message
|
/// Clear error message
|
||||||
void clearError() {
|
void clearError() {
|
||||||
_error = null;
|
_error = null;
|
||||||
|
|||||||
1018
lib/screens/device_config_screen.dart
Normal file
1018
lib/screens/device_config_screen.dart
Normal file
File diff suppressed because it is too large
Load Diff
@@ -2,14 +2,13 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import '../providers/connection_provider.dart';
|
import '../providers/connection_provider.dart';
|
||||||
import '../providers/app_provider.dart';
|
import '../providers/app_provider.dart';
|
||||||
import '../models/device_info.dart' as models;
|
|
||||||
import '../services/tile_cache_service.dart';
|
|
||||||
import '../theme/app_theme.dart';
|
import '../theme/app_theme.dart';
|
||||||
import 'messages_tab.dart';
|
import 'messages_tab.dart';
|
||||||
import 'contacts_tab.dart';
|
import 'contacts_tab.dart';
|
||||||
import 'map_tab.dart';
|
import 'map_tab.dart';
|
||||||
import 'map_management_screen.dart';
|
import 'map_management_screen.dart';
|
||||||
import 'settings_screen.dart';
|
import 'settings_screen.dart';
|
||||||
|
import 'device_config_screen.dart';
|
||||||
|
|
||||||
class HomeScreen extends StatefulWidget {
|
class HomeScreen extends StatefulWidget {
|
||||||
final Function(AppThemeMode) onThemeChanged;
|
final Function(AppThemeMode) onThemeChanged;
|
||||||
@@ -352,7 +351,7 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
|
|||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
isConnected
|
isConnected
|
||||||
? deviceInfo.deviceName ?? 'Connected'
|
? deviceInfo.displayName ?? 'Connected'
|
||||||
: 'Disconnected',
|
: 'Disconnected',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
@@ -420,22 +419,42 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
OutlinedButton(
|
// Settings button
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => const DeviceConfigScreen(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.settings),
|
||||||
|
iconSize: 20,
|
||||||
|
tooltip: 'Device Settings',
|
||||||
|
constraints: const BoxConstraints(
|
||||||
|
minWidth: 36,
|
||||||
|
minHeight: 36,
|
||||||
|
),
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
// Disconnect button (prominent, icon only)
|
||||||
|
FilledButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await provider.disconnect();
|
await provider.disconnect();
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
context.read<AppProvider>().clearAllData();
|
context.read<AppProvider>().clearAllData();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
style: OutlinedButton.styleFrom(
|
style: FilledButton.styleFrom(
|
||||||
|
backgroundColor: Colors.red.shade700,
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
side: const BorderSide(color: Colors.white),
|
padding: const EdgeInsets.all(10),
|
||||||
shape: RoundedRectangleBorder(
|
minimumSize: const Size(40, 40),
|
||||||
borderRadius: BorderRadius.circular(20),
|
shape: const CircleBorder(),
|
||||||
),
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
|
||||||
),
|
),
|
||||||
child: const Text('Disconnect', style: TextStyle(fontSize: 13)),
|
child: const Icon(Icons.power_settings_new, size: 20),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -467,7 +486,7 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
isConnected
|
isConnected
|
||||||
? deviceInfo.deviceName ?? 'Connected'
|
? deviceInfo.displayName ?? 'Connected'
|
||||||
: 'Not Connected',
|
: 'Not Connected',
|
||||||
style: Theme.of(context).textTheme.bodyMedium,
|
style: Theme.of(context).textTheme.bodyMedium,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -687,7 +687,7 @@ class _MapTabState extends State<MapTab> with AutomaticKeepAliveClientMixin {
|
|||||||
super.build(context); // Required for AutomaticKeepAliveClientMixin
|
super.build(context); // Required for AutomaticKeepAliveClientMixin
|
||||||
return Consumer2<ContactsProvider, MessagesProvider>(
|
return Consumer2<ContactsProvider, MessagesProvider>(
|
||||||
builder: (context, contactsProvider, messagesProvider, child) {
|
builder: (context, contactsProvider, messagesProvider, child) {
|
||||||
final contactsWithLocation = contactsProvider.chatContactsWithLocation;
|
final contactsWithLocation = contactsProvider.contactsWithLocation;
|
||||||
final sarMarkers = messagesProvider.sarMarkers;
|
final sarMarkers = messagesProvider.sarMarkers;
|
||||||
final center = _calculateCenter(contactsWithLocation, sarMarkers);
|
final center = _calculateCenter(contactsWithLocation, sarMarkers);
|
||||||
|
|
||||||
@@ -729,7 +729,7 @@ class _MapTabState extends State<MapTab> with AutomaticKeepAliveClientMixin {
|
|||||||
onContactTap: (contact) {
|
onContactTap: (contact) {
|
||||||
_showDetailedCompassWithContact(
|
_showDetailedCompassWithContact(
|
||||||
context,
|
context,
|
||||||
contactsProvider.chatContactsWithLocation,
|
contactsProvider.contactsWithLocation,
|
||||||
messagesProvider.sarMarkers,
|
messagesProvider.sarMarkers,
|
||||||
contact,
|
contact,
|
||||||
);
|
);
|
||||||
@@ -742,7 +742,7 @@ class _MapTabState extends State<MapTab> with AutomaticKeepAliveClientMixin {
|
|||||||
onSarMarkerTap: (marker) {
|
onSarMarkerTap: (marker) {
|
||||||
_showDetailedCompassWithSarMarker(
|
_showDetailedCompassWithSarMarker(
|
||||||
context,
|
context,
|
||||||
contactsProvider.chatContactsWithLocation,
|
contactsProvider.contactsWithLocation,
|
||||||
messagesProvider.sarMarkers,
|
messagesProvider.sarMarkers,
|
||||||
marker,
|
marker,
|
||||||
);
|
);
|
||||||
@@ -807,7 +807,7 @@ class _MapTabState extends State<MapTab> with AutomaticKeepAliveClientMixin {
|
|||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () => _showDetailedCompass(
|
onTap: () => _showDetailedCompass(
|
||||||
context,
|
context,
|
||||||
contactsProvider.chatContactsWithLocation,
|
contactsProvider.contactsWithLocation,
|
||||||
messagesProvider.sarMarkers,
|
messagesProvider.sarMarkers,
|
||||||
),
|
),
|
||||||
child: _CompassWidget(
|
child: _CompassWidget(
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ typedef OnContactCallback = void Function(Contact contact);
|
|||||||
typedef OnContactsCompleteCallback = void Function(List<Contact> contacts);
|
typedef OnContactsCompleteCallback = void Function(List<Contact> contacts);
|
||||||
typedef OnMessageCallback = void Function(Message message);
|
typedef OnMessageCallback = void Function(Message message);
|
||||||
typedef OnTelemetryCallback = void Function(Uint8List publicKey, Uint8List lppData);
|
typedef OnTelemetryCallback = void Function(Uint8List publicKey, Uint8List lppData);
|
||||||
|
typedef OnSelfInfoCallback = void Function(Map<String, dynamic> selfInfo);
|
||||||
typedef OnErrorCallback = void Function(String error);
|
typedef OnErrorCallback = void Function(String error);
|
||||||
typedef OnConnectionStateCallback = void Function(bool isConnected);
|
typedef OnConnectionStateCallback = void Function(bool isConnected);
|
||||||
|
|
||||||
@@ -30,6 +31,7 @@ class MeshCoreBleService {
|
|||||||
OnContactsCompleteCallback? onContactsComplete;
|
OnContactsCompleteCallback? onContactsComplete;
|
||||||
OnMessageCallback? onMessageReceived;
|
OnMessageCallback? onMessageReceived;
|
||||||
OnTelemetryCallback? onTelemetryReceived;
|
OnTelemetryCallback? onTelemetryReceived;
|
||||||
|
OnSelfInfoCallback? onSelfInfoReceived;
|
||||||
OnErrorCallback? onError;
|
OnErrorCallback? onError;
|
||||||
|
|
||||||
// Internal state
|
// Internal state
|
||||||
@@ -636,11 +638,29 @@ class MeshCoreBleService {
|
|||||||
print(' Position: ${advLat / 1000000.0}, ${advLon / 1000000.0}');
|
print(' Position: ${advLat / 1000000.0}, ${advLon / 1000000.0}');
|
||||||
print(' Radio: freq=$radioFreq, bw=$radioBw, sf=$radioSf, cr=$radioCr');
|
print(' Radio: freq=$radioFreq, bw=$radioBw, sf=$radioSf, cr=$radioCr');
|
||||||
|
|
||||||
|
String? selfName;
|
||||||
if (reader.hasRemaining) {
|
if (reader.hasRemaining) {
|
||||||
final selfName = String.fromCharCodes(reader.readRemainingBytes().takeWhile((b) => b != 0));
|
selfName = String.fromCharCodes(reader.readRemainingBytes().takeWhile((b) => b != 0));
|
||||||
print(' Self name: $selfName');
|
print(' Self name: $selfName');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Call callback with parsed data
|
||||||
|
onSelfInfoReceived?.call({
|
||||||
|
'protocolVersion': protocolVersion,
|
||||||
|
'deviceType': deviceType,
|
||||||
|
'txPower': txPower,
|
||||||
|
'maxTxPower': maxTxPower,
|
||||||
|
'publicKey': publicKey,
|
||||||
|
'advLat': advLat,
|
||||||
|
'advLon': advLon,
|
||||||
|
'manualAddContacts': manualAddContacts == 1,
|
||||||
|
'radioFreq': radioFreq,
|
||||||
|
'radioBw': radioBw,
|
||||||
|
'radioSf': radioSf,
|
||||||
|
'radioCr': radioCr,
|
||||||
|
'selfName': selfName,
|
||||||
|
});
|
||||||
|
|
||||||
print(' ✅ [SelfInfo] Parsed successfully');
|
print(' ✅ [SelfInfo] Parsed successfully');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(' ❌ [SelfInfo] Parsing error: $e');
|
print(' ❌ [SelfInfo] Parsing error: $e');
|
||||||
@@ -718,6 +738,11 @@ class MeshCoreBleService {
|
|||||||
await _sendAppStart();
|
await _sendAppStart();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Refresh device info (public method)
|
||||||
|
Future<void> refreshDeviceInfo() async {
|
||||||
|
await _sendDeviceQuery();
|
||||||
|
}
|
||||||
|
|
||||||
/// Get contacts from device
|
/// Get contacts from device
|
||||||
Future<void> getContacts() async {
|
Future<void> getContacts() async {
|
||||||
final writer = BufferWriter();
|
final writer = BufferWriter();
|
||||||
@@ -793,6 +818,50 @@ class MeshCoreBleService {
|
|||||||
await _writeData(writer.toBytes());
|
await _writeData(writer.toBytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set advertised name
|
||||||
|
Future<void> setAdvertName(String name) async {
|
||||||
|
final writer = BufferWriter();
|
||||||
|
writer.writeByte(MeshCoreConstants.cmdSetAdvertName);
|
||||||
|
writer.writeString(name);
|
||||||
|
await _writeData(writer.toBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set advertised latitude and longitude
|
||||||
|
Future<void> setAdvertLatLon({
|
||||||
|
required double latitude,
|
||||||
|
required double longitude,
|
||||||
|
}) async {
|
||||||
|
final writer = BufferWriter();
|
||||||
|
writer.writeByte(MeshCoreConstants.cmdSetAdvertLatLon);
|
||||||
|
writer.writeInt32LE((latitude * 1000000).round());
|
||||||
|
writer.writeInt32LE((longitude * 1000000).round());
|
||||||
|
await _writeData(writer.toBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set radio parameters
|
||||||
|
Future<void> setRadioParams({
|
||||||
|
required int frequency, // Hz
|
||||||
|
required int bandwidth, // 0-9 (see bandwidth options)
|
||||||
|
required int spreadingFactor, // 7-12
|
||||||
|
required int codingRate, // 5-8
|
||||||
|
}) async {
|
||||||
|
final writer = BufferWriter();
|
||||||
|
writer.writeByte(MeshCoreConstants.cmdSetRadioParams);
|
||||||
|
writer.writeUInt32LE(frequency);
|
||||||
|
writer.writeUInt16LE(bandwidth);
|
||||||
|
writer.writeByte(spreadingFactor);
|
||||||
|
writer.writeByte(codingRate);
|
||||||
|
await _writeData(writer.toBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set transmit power
|
||||||
|
Future<void> setTxPower(int powerDbm) async {
|
||||||
|
final writer = BufferWriter();
|
||||||
|
writer.writeByte(MeshCoreConstants.cmdSetTxPower);
|
||||||
|
writer.writeByte(powerDbm);
|
||||||
|
await _writeData(writer.toBytes());
|
||||||
|
}
|
||||||
|
|
||||||
/// Reset packet counters
|
/// Reset packet counters
|
||||||
void resetCounters() {
|
void resetCounters() {
|
||||||
_rxPacketCount = 0;
|
_rxPacketCount = 0;
|
||||||
|
|||||||
@@ -72,8 +72,8 @@ class SampleDataGenerator {
|
|||||||
outPath: Uint8List(32),
|
outPath: Uint8List(32),
|
||||||
advName: teamNames[i],
|
advName: teamNames[i],
|
||||||
lastAdvert: now.millisecondsSinceEpoch ~/ 1000,
|
lastAdvert: now.millisecondsSinceEpoch ~/ 1000,
|
||||||
advLat: (lat * 1e7).toInt(),
|
advLat: (lat * 1e6).toInt(),
|
||||||
advLon: (lon * 1e7).toInt(),
|
advLon: (lon * 1e6).toInt(),
|
||||||
lastMod: now.millisecondsSinceEpoch ~/ 1000,
|
lastMod: now.millisecondsSinceEpoch ~/ 1000,
|
||||||
telemetry: telemetry,
|
telemetry: telemetry,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class MapMarkers {
|
|||||||
// Marker icon
|
// Marker icon
|
||||||
Container(
|
Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Theme.of(context).colorScheme.primary,
|
color: _getContactTypeColor(contact, context),
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
border: Border.all(color: Colors.white, width: 2),
|
border: Border.all(color: Colors.white, width: 2),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
@@ -64,8 +64,8 @@ class MapMarkers {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
padding: const EdgeInsets.all(6),
|
padding: const EdgeInsets.all(6),
|
||||||
child: const Icon(
|
child: Icon(
|
||||||
Icons.person,
|
_getContactTypeIcon(contact),
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
size: 18,
|
size: 18,
|
||||||
),
|
),
|
||||||
@@ -294,6 +294,32 @@ class MapMarkers {
|
|||||||
return Colors.grey;
|
return Colors.grey;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Color _getContactTypeColor(Contact contact, BuildContext context) {
|
||||||
|
switch (contact.type) {
|
||||||
|
case ContactType.chat:
|
||||||
|
return Theme.of(context).colorScheme.primary; // Blue for team members
|
||||||
|
case ContactType.repeater:
|
||||||
|
return Colors.deepPurple; // Purple for repeaters
|
||||||
|
case ContactType.room:
|
||||||
|
return Colors.teal; // Teal for rooms/channels
|
||||||
|
case ContactType.none:
|
||||||
|
return Colors.grey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static IconData _getContactTypeIcon(Contact contact) {
|
||||||
|
switch (contact.type) {
|
||||||
|
case ContactType.chat:
|
||||||
|
return Icons.person; // Person for team members
|
||||||
|
case ContactType.repeater:
|
||||||
|
return Icons.router; // Router icon for repeaters
|
||||||
|
case ContactType.room:
|
||||||
|
return Icons.forum; // Forum/chat icon for rooms
|
||||||
|
case ContactType.none:
|
||||||
|
return Icons.help_outline;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _InfoRow extends StatelessWidget {
|
class _InfoRow extends StatelessWidget {
|
||||||
|
|||||||
Reference in New Issue
Block a user