Remove old app icons and update Info.plist version; add radio settings localization and improve device configuration UI

This commit is contained in:
Janez T
2025-10-16 21:01:29 +02:00
parent abca014720
commit b6283e1c8f
58 changed files with 347 additions and 101 deletions

View File

@@ -13,6 +13,9 @@ android {
compileOptions { compileOptions {
sourceCompatibility = JavaVersion.VERSION_11 sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11
// Required by some dependencies (e.g., flutter_local_notifications)
// to support Java 8+ APIs on older Android devices.
isCoreLibraryDesugaringEnabled = true
} }
kotlinOptions { kotlinOptions {
@@ -42,3 +45,8 @@ android {
flutter { flutter {
source = "../.." source = "../.."
} }
dependencies {
// Core library desugaring support library
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.4")
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 24 KiB

BIN
icon.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Binary file not shown.

View File

@@ -489,7 +489,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 14; CURRENT_PROJECT_VERSION = 17;
DEVELOPMENT_TEAM = JND55328G8; DEVELOPMENT_TEAM = JND55328G8;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
@@ -511,7 +511,7 @@
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 14; CURRENT_PROJECT_VERSION = 17;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.meshcore.sar.meshcoreSarApp.RunnerTests; PRODUCT_BUNDLE_IDENTIFIER = com.meshcore.sar.meshcoreSarApp.RunnerTests;
@@ -529,7 +529,7 @@
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 14; CURRENT_PROJECT_VERSION = 17;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.meshcore.sar.meshcoreSarApp.RunnerTests; PRODUCT_BUNDLE_IDENTIFIER = com.meshcore.sar.meshcoreSarApp.RunnerTests;
@@ -545,7 +545,7 @@
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 14; CURRENT_PROJECT_VERSION = 17;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.meshcore.sar.meshcoreSarApp.RunnerTests; PRODUCT_BUNDLE_IDENTIFIER = com.meshcore.sar.meshcoreSarApp.RunnerTests;
@@ -676,7 +676,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 14; CURRENT_PROJECT_VERSION = 17;
DEVELOPMENT_TEAM = JND55328G8; DEVELOPMENT_TEAM = JND55328G8;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
@@ -699,7 +699,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 14; CURRENT_PROJECT_VERSION = 17;
DEVELOPMENT_TEAM = JND55328G8; DEVELOPMENT_TEAM = JND55328G8;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 821 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 861 B

After

Width:  |  Height:  |  Size: 881 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -21,7 +21,7 @@
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>14</string> <string>17</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>UILaunchStoryboardName</key> <key>UILaunchStoryboardName</key>

View File

@@ -5,22 +5,17 @@
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.000206"> <testcase classname="fastlane.lanes" name="0: default_platform" time="0.000203">
</testcase> </testcase>
<testcase classname="fastlane.lanes" name="1: increment_build_number" time="0.235136"> <testcase classname="fastlane.lanes" name="1: increment_build_number" time="0.287931">
</testcase> </testcase>
<testcase classname="fastlane.lanes" name="2: build_app" time="103.133272"> <testcase classname="fastlane.lanes" name="2: build_app" time="31.633362">
</testcase>
<testcase classname="fastlane.lanes" name="3: upload_to_testflight" time="333.986172">
</testcase> </testcase>

View File

@@ -1733,5 +1733,48 @@
"tapToViewOnMap": "Tap to view on map", "tapToViewOnMap": "Tap to view on map",
"@tapToViewOnMap": { "@tapToViewOnMap": {
"description": "Notification action text" "description": "Notification action text"
},
"radioSettings": "Radio Settings",
"@radioSettings": {
"description": "Section title for radio settings"
},
"frequencyMHz": "Frequency (MHz)",
"@frequencyMHz": {
"description": "Label for radio frequency field"
},
"frequencyExample": "e.g., 869.618",
"@frequencyExample": {
"description": "Helper text example for frequency"
},
"bandwidth": "Bandwidth",
"@bandwidth": {
"description": "Label for bandwidth dropdown"
},
"spreadingFactor": "Spreading Factor",
"@spreadingFactor": {
"description": "Label for spreading factor dropdown"
},
"codingRate": "Coding Rate",
"@codingRate": {
"description": "Label for coding rate dropdown"
},
"txPowerDbm": "TX Power (dBm)",
"@txPowerDbm": {
"description": "Label for TX power field"
},
"maxPowerDbm": "Max: {power} dBm",
"@maxPowerDbm": {
"description": "Helper text showing maximum TX power",
"placeholders": {
"power": { "type": "int" }
}
} }
} }

View File

@@ -581,5 +581,14 @@
"coordinates": "Koordinate", "coordinates": "Koordinate",
"tapToViewOnMap": "Dodirnite za prikaz na karti" "tapToViewOnMap": "Dodirnite za prikaz na karti",
"radioSettings": "Postavke radija",
"frequencyMHz": "Frekvencija (MHz)",
"frequencyExample": "npr. 869.618",
"bandwidth": "Širina pojasa",
"spreadingFactor": "Faktor širenja",
"codingRate": "Omjer kodiranja",
"txPowerDbm": "TX snaga (dBm)",
"maxPowerDbm": "Maks: {power} dBm"
} }

View File

@@ -1850,6 +1850,54 @@ abstract class AppLocalizations {
/// In en, this message translates to: /// In en, this message translates to:
/// **'Tap to view on map'** /// **'Tap to view on map'**
String get tapToViewOnMap; String get tapToViewOnMap;
/// Section title for radio settings
///
/// In en, this message translates to:
/// **'Radio Settings'**
String get radioSettings;
/// Label for radio frequency field
///
/// In en, this message translates to:
/// **'Frequency (MHz)'**
String get frequencyMHz;
/// Helper text example for frequency
///
/// In en, this message translates to:
/// **'e.g., 869.618'**
String get frequencyExample;
/// Label for bandwidth dropdown
///
/// In en, this message translates to:
/// **'Bandwidth'**
String get bandwidth;
/// Label for spreading factor dropdown
///
/// In en, this message translates to:
/// **'Spreading Factor'**
String get spreadingFactor;
/// Label for coding rate dropdown
///
/// In en, this message translates to:
/// **'Coding Rate'**
String get codingRate;
/// Label for TX power field
///
/// In en, this message translates to:
/// **'TX Power (dBm)'**
String get txPowerDbm;
/// Helper text showing maximum TX power
///
/// In en, this message translates to:
/// **'Max: {power} dBm'**
String maxPowerDbm(int power);
} }
class _AppLocalizationsDelegate class _AppLocalizationsDelegate

View File

@@ -1009,4 +1009,30 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get tapToViewOnMap => 'Tap to view on map'; String get tapToViewOnMap => 'Tap to view on map';
@override
String get radioSettings => 'Radio Settings';
@override
String get frequencyMHz => 'Frequency (MHz)';
@override
String get frequencyExample => 'e.g., 869.618';
@override
String get bandwidth => 'Bandwidth';
@override
String get spreadingFactor => 'Spreading Factor';
@override
String get codingRate => 'Coding Rate';
@override
String get txPowerDbm => 'TX Power (dBm)';
@override
String maxPowerDbm(int power) {
return 'Max: $power dBm';
}
} }

View File

@@ -1011,4 +1011,30 @@ class AppLocalizationsHr extends AppLocalizations {
@override @override
String get tapToViewOnMap => 'Dodirnite za prikaz na karti'; String get tapToViewOnMap => 'Dodirnite za prikaz na karti';
@override
String get radioSettings => 'Postavke radija';
@override
String get frequencyMHz => 'Frekvencija (MHz)';
@override
String get frequencyExample => 'npr. 869.618';
@override
String get bandwidth => 'Širina pojasa';
@override
String get spreadingFactor => 'Faktor širenja';
@override
String get codingRate => 'Omjer kodiranja';
@override
String get txPowerDbm => 'TX snaga (dBm)';
@override
String maxPowerDbm(int power) {
return 'Maks: $power dBm';
}
} }

View File

@@ -1011,4 +1011,30 @@ class AppLocalizationsSl extends AppLocalizations {
@override @override
String get tapToViewOnMap => 'Tapnite za prikaz na zemljevidu'; String get tapToViewOnMap => 'Tapnite za prikaz na zemljevidu';
@override
String get radioSettings => 'Nastavitve radia';
@override
String get frequencyMHz => 'Frekvenca (MHz)';
@override
String get frequencyExample => 'npr. 869.618';
@override
String get bandwidth => 'Pasovna širina';
@override
String get spreadingFactor => 'Faktor razširitve';
@override
String get codingRate => 'Razmerje kodiranja';
@override
String get txPowerDbm => 'Izhodna moč (dBm)';
@override
String maxPowerDbm(int power) {
return 'Največ: $power dBm';
}
} }

View File

@@ -581,5 +581,14 @@
"coordinates": "Koordinate", "coordinates": "Koordinate",
"tapToViewOnMap": "Tapnite za prikaz na zemljevidu" "tapToViewOnMap": "Tapnite za prikaz na zemljevidu",
"radioSettings": "Nastavitve radia",
"frequencyMHz": "Frekvenca (MHz)",
"frequencyExample": "npr. 869.618",
"bandwidth": "Pasovna širina",
"spreadingFactor": "Faktor razširitve",
"codingRate": "Razmerje kodiranja",
"txPowerDbm": "Izhodna moč (dBm)",
"maxPowerDbm": "Največ: {power} dBm"
} }

View File

@@ -48,31 +48,44 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
text: deviceInfo.selfName ?? deviceInfo.deviceName ?? '', text: deviceInfo.selfName ?? deviceInfo.deviceName ?? '',
); );
_latController = TextEditingController( _latController = TextEditingController(
text: deviceInfo.advLat != null ? (deviceInfo.advLat! / 1000000).toStringAsFixed(6) : '0.0', text: deviceInfo.advLat != null
? (deviceInfo.advLat! / 1000000).toStringAsFixed(6)
: '0.0',
); );
_lonController = TextEditingController( _lonController = TextEditingController(
text: deviceInfo.advLon != null ? (deviceInfo.advLon! / 1000000).toStringAsFixed(6) : '0.0', text: deviceInfo.advLon != null
? (deviceInfo.advLon! / 1000000).toStringAsFixed(6)
: '0.0',
); );
_freqController = TextEditingController( _freqController = TextEditingController(
text: deviceInfo.radioFreq != null ? (deviceInfo.radioFreq! / 1000).toStringAsFixed(3) : '869.618', text: deviceInfo.radioFreq != null
? (deviceInfo.radioFreq! / 1000).toStringAsFixed(3)
: '869.618',
); );
_txPowerController = TextEditingController( _txPowerController = TextEditingController(
text: deviceInfo.txPower?.toString() ?? '20', text: deviceInfo.txPower?.toString() ?? '20',
); );
if (deviceInfo.radioBw != null && deviceInfo.radioBw! >= 0 && deviceInfo.radioBw! <= 9) { if (deviceInfo.radioBw != null &&
deviceInfo.radioBw! >= 0 &&
deviceInfo.radioBw! <= 9) {
_selectedBandwidth = _bandwidthFromValue(deviceInfo.radioBw!); _selectedBandwidth = _bandwidthFromValue(deviceInfo.radioBw!);
} }
if (deviceInfo.radioSf != null && deviceInfo.radioSf! >= 7 && deviceInfo.radioSf! <= 12) { if (deviceInfo.radioSf != null &&
deviceInfo.radioSf! >= 7 &&
deviceInfo.radioSf! <= 12) {
_selectedSpreadingFactor = deviceInfo.radioSf!; _selectedSpreadingFactor = deviceInfo.radioSf!;
} }
if (deviceInfo.radioCr != null && deviceInfo.radioCr! >= 5 && deviceInfo.radioCr! <= 8) { if (deviceInfo.radioCr != null &&
deviceInfo.radioCr! >= 5 &&
deviceInfo.radioCr! <= 8) {
_selectedCodingRate = deviceInfo.radioCr!; _selectedCodingRate = deviceInfo.radioCr!;
} }
// Check if telemetry is enabled (check if lat/lon are set and not zero) // Check if telemetry is enabled (check if lat/lon are set and not zero)
_telemetryEnabled = (deviceInfo.advLat != null && deviceInfo.advLat! != 0) || _telemetryEnabled =
(deviceInfo.advLon != null && deviceInfo.advLon! != 0); (deviceInfo.advLat != null && deviceInfo.advLat! != 0) ||
(deviceInfo.advLon != null && deviceInfo.advLon! != 0);
} }
@override @override
@@ -87,17 +100,28 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
String _bandwidthFromValue(int bw) { String _bandwidthFromValue(int bw) {
switch (bw) { switch (bw) {
case 0: return '7.8 kHz'; case 0:
case 1: return '10.4 kHz'; return '7.8 kHz';
case 2: return '15.6 kHz'; case 1:
case 3: return '20.8 kHz'; return '10.4 kHz';
case 4: return '31.25 kHz'; case 2:
case 5: return '41.7 kHz'; return '15.6 kHz';
case 6: return '62.5 kHz'; case 3:
case 7: return '125 kHz'; return '20.8 kHz';
case 8: return '250 kHz'; case 4:
case 9: return '500 kHz'; return '31.25 kHz';
default: return '62.5 kHz'; case 5:
return '41.7 kHz';
case 6:
return '62.5 kHz';
case 7:
return '125 kHz';
case 8:
return '250 kHz';
case 9:
return '500 kHz';
default:
return '62.5 kHz';
} }
} }
@@ -159,10 +183,7 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
); );
} else { } else {
// Clear position // Clear position
await connectionProvider.setAdvertLatLon( await connectionProvider.setAdvertLatLon(latitude: 0.0, longitude: 0.0);
latitude: 0.0,
longitude: 0.0,
);
// Set telemetry modes to "Deny" (mode 0) // Set telemetry modes to "Deny" (mode 0)
final telemetryModes = 0x00; final telemetryModes = 0x00;
@@ -269,7 +290,11 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
if (!serviceEnabled) { if (!serviceEnabled) {
if (context.mounted) { if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(AppLocalizations.of(context)!.locationServicesDisabled)), SnackBar(
content: Text(
AppLocalizations.of(context)!.locationServicesDisabled,
),
),
); );
} }
return; return;
@@ -281,7 +306,11 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
if (permission == LocationPermission.denied) { if (permission == LocationPermission.denied) {
if (context.mounted) { if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(AppLocalizations.of(context)!.locationPermissionDenied)), SnackBar(
content: Text(
AppLocalizations.of(context)!.locationPermissionDenied,
),
),
); );
} }
return; return;
@@ -291,7 +320,13 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
if (permission == LocationPermission.deniedForever) { if (permission == LocationPermission.deniedForever) {
if (context.mounted) { if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(AppLocalizations.of(context)!.locationPermissionPermanentlyDenied)), SnackBar(
content: Text(
AppLocalizations.of(
context,
)!.locationPermissionPermanentlyDenied,
),
),
); );
} }
return; return;
@@ -312,19 +347,21 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
if (context.mounted) { if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: Text(AppLocalizations.of(context)!.locationBroadcast( content: Text(
position.latitude.toStringAsFixed(6), AppLocalizations.of(context)!.locationBroadcast(
position.longitude.toStringAsFixed(6), position.latitude.toStringAsFixed(6),
)), position.longitude.toStringAsFixed(6),
),
),
backgroundColor: Colors.green, backgroundColor: Colors.green,
), ),
); );
} }
} catch (e) { } catch (e) {
if (context.mounted) { if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(
SnackBar(content: Text('Failed to get location: $e')), context,
); ).showSnackBar(SnackBar(content: Text('Failed to get location: $e')));
} }
} }
} }
@@ -352,11 +389,17 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
if (context.mounted) { if (context.mounted) {
final deviceInfo = connectionProvider.deviceInfo; final deviceInfo = connectionProvider.deviceInfo;
final lat = deviceInfo.advLat != null ? (deviceInfo.advLat! / 1000000).toStringAsFixed(6) : '0.0'; final lat = deviceInfo.advLat != null
final lon = deviceInfo.advLon != null ? (deviceInfo.advLon! / 1000000).toStringAsFixed(6) : '0.0'; ? (deviceInfo.advLat! / 1000000).toStringAsFixed(6)
: '0.0';
final lon = deviceInfo.advLon != null
? (deviceInfo.advLon! / 1000000).toStringAsFixed(6)
: '0.0';
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: Text(AppLocalizations.of(context)!.advertisedAtLocation(lat, lon)), content: Text(
AppLocalizations.of(context)!.advertisedAtLocation(lat, lon),
),
backgroundColor: Colors.green, backgroundColor: Colors.green,
), ),
); );
@@ -365,7 +408,9 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
if (context.mounted) { if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: Text(AppLocalizations.of(context)!.failedToAdvertise(e.toString())), content: Text(
AppLocalizations.of(context)!.failedToAdvertise(e.toString()),
),
backgroundColor: Colors.red, backgroundColor: Colors.red,
), ),
); );
@@ -383,9 +428,7 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
final theme = Theme.of(context); final theme = Theme.of(context);
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(title: Text(AppLocalizations.of(context)!.settings)),
title: Text(AppLocalizations.of(context)!.settings),
),
body: ListView( body: ListView(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
children: [ children: [
@@ -408,11 +451,26 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
_InfoRow('Type', _getDeviceTypeString(deviceInfo.deviceType)), _InfoRow('Type', _getDeviceTypeString(deviceInfo.deviceType)),
_InfoRow('Model', deviceInfo.manufacturerModel ?? 'Unknown'), _InfoRow('Model', deviceInfo.manufacturerModel ?? 'Unknown'),
_InfoRow('Version', deviceInfo.semanticVersion ?? 'Unknown'), _InfoRow('Version', deviceInfo.semanticVersion ?? 'Unknown'),
_InfoRow('Build Date', deviceInfo.firmwareBuildDate ?? 'Unknown'), _InfoRow(
_InfoRow('Firmware', 'v${deviceInfo.firmwareVersion?.toString() ?? "?"}'), 'Build Date',
_InfoRow('Max Contacts', deviceInfo.maxContacts?.toString() ?? 'Unknown'), deviceInfo.firmwareBuildDate ?? 'Unknown',
_InfoRow('Max Channels', deviceInfo.maxChannels?.toString() ?? 'Unknown'), ),
_CopyableInfoRow('Public Key', _getPublicKeyHex(deviceInfo.publicKey)), _InfoRow(
'Firmware',
'v${deviceInfo.firmwareVersion?.toString() ?? "?"}',
),
_InfoRow(
'Max Contacts',
deviceInfo.maxContacts?.toString() ?? 'Unknown',
),
_InfoRow(
'Max Channels',
deviceInfo.maxChannels?.toString() ?? 'Unknown',
),
_CopyableInfoRow(
'Public Key',
_getPublicKeyHex(deviceInfo.publicKey),
),
], ],
), ),
), ),
@@ -439,17 +497,6 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
Row( Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
IconButton.outlined(
onPressed: _isBroadcasting ? null : _broadcastNow,
icon: _isBroadcasting
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Icon(Icons.sensors),
tooltip: AppLocalizations.of(context)!.settings,
),
const SizedBox(width: 8), const SizedBox(width: 8),
IconButton.filled( IconButton.filled(
onPressed: _savePublicInfo, onPressed: _savePublicInfo,
@@ -554,7 +601,7 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Text(
'Radio Settings', AppLocalizations.of(context)!.radioSettings,
style: theme.textTheme.titleLarge?.copyWith( style: theme.textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
@@ -571,21 +618,23 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
// LoRa Frequency // LoRa Frequency
TextField( TextField(
controller: _freqController, controller: _freqController,
decoration: const InputDecoration( decoration: InputDecoration(
labelText: 'Frequency (MHz)', labelText: AppLocalizations.of(context)!.frequencyMHz,
border: OutlineInputBorder(), border: const OutlineInputBorder(),
helperText: 'e.g., 869.618', helperText: AppLocalizations.of(context)!.frequencyExample,
),
keyboardType: const TextInputType.numberWithOptions(
decimal: true,
), ),
keyboardType: const TextInputType.numberWithOptions(decimal: true),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
// Bandwidth // Bandwidth
DropdownButtonFormField<String>( DropdownButtonFormField<String>(
value: _selectedBandwidth, value: _selectedBandwidth,
decoration: const InputDecoration( decoration: InputDecoration(
labelText: 'Bandwidth', labelText: AppLocalizations.of(context)!.bandwidth,
border: OutlineInputBorder(), border: const OutlineInputBorder(),
), ),
items: _bandwidthOptions.map((String value) { items: _bandwidthOptions.map((String value) {
return DropdownMenuItem<String>( return DropdownMenuItem<String>(
@@ -606,11 +655,13 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
// Spreading Factor // Spreading Factor
DropdownButtonFormField<int>( DropdownButtonFormField<int>(
value: _selectedSpreadingFactor, value: _selectedSpreadingFactor,
decoration: const InputDecoration( decoration: InputDecoration(
labelText: 'Spreading Factor', labelText: AppLocalizations.of(context)!.spreadingFactor,
border: OutlineInputBorder(), border: const OutlineInputBorder(),
), ),
items: List.generate(6, (index) => index + 7).map((int value) { items: List.generate(6, (index) => index + 7).map((
int value,
) {
return DropdownMenuItem<int>( return DropdownMenuItem<int>(
value: value, value: value,
child: Text(value.toString()), child: Text(value.toString()),
@@ -629,11 +680,13 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
// Coding Rate // Coding Rate
DropdownButtonFormField<int>( DropdownButtonFormField<int>(
value: _selectedCodingRate, value: _selectedCodingRate,
decoration: const InputDecoration( decoration: InputDecoration(
labelText: 'Coding Rate', labelText: AppLocalizations.of(context)!.codingRate,
border: OutlineInputBorder(), border: const OutlineInputBorder(),
), ),
items: List.generate(4, (index) => index + 5).map((int value) { items: List.generate(4, (index) => index + 5).map((
int value,
) {
return DropdownMenuItem<int>( return DropdownMenuItem<int>(
value: value, value: value,
child: Text(value.toString()), child: Text(value.toString()),
@@ -653,9 +706,9 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
TextField( TextField(
controller: _txPowerController, controller: _txPowerController,
decoration: InputDecoration( decoration: InputDecoration(
labelText: 'TX Power (dBm)', labelText: AppLocalizations.of(context)!.txPowerDbm,
border: const OutlineInputBorder(), border: const OutlineInputBorder(),
helperText: 'Max: ${deviceInfo.maxTxPower ?? 22} dBm', helperText: AppLocalizations.of(context)!.maxPowerDbm(deviceInfo.maxTxPower ?? 22),
), ),
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
), ),
@@ -673,11 +726,16 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
String _getDeviceTypeString(int? deviceType) { String _getDeviceTypeString(int? deviceType) {
if (deviceType == null) return 'Unknown'; if (deviceType == null) return 'Unknown';
switch (deviceType) { switch (deviceType) {
case 0: return 'None/Unknown'; case 0:
case 1: return 'Chat Node'; return 'None/Unknown';
case 2: return 'Repeater'; case 1:
case 3: return 'Room/Channel'; return 'Chat Node';
default: return 'Type $deviceType'; case 2:
return 'Repeater';
case 3:
return 'Room/Channel';
default:
return 'Type $deviceType';
} }
} }
@@ -713,9 +771,7 @@ class _InfoRow extends StatelessWidget {
Expanded( Expanded(
child: Text( child: Text(
value, value,
style: const TextStyle( style: const TextStyle(fontWeight: FontWeight.w600),
fontWeight: FontWeight.w600,
),
), ),
), ),
], ],

View File

@@ -254,7 +254,7 @@ class _HomeScreenState extends State<HomeScreen>
// Info banner // Info banner
Container( Container(
margin: const EdgeInsets.symmetric(horizontal: 16), margin: const EdgeInsets.only(left: 16, right: 16, top: 16),
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primaryContainer, color: Theme.of(context).colorScheme.primaryContainer,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 821 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 642 B

After

Width:  |  Height:  |  Size: 668 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 235 KiB

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 642 B

After

Width:  |  Height:  |  Size: 668 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 235 KiB

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 235 KiB

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB