This commit is contained in:
HDDen
2026-04-28 16:26:42 +03:00
67 changed files with 5771 additions and 611 deletions
+33 -3
View File
@@ -1,8 +1,19 @@
import '../utils/platform_info.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_foreground_task/flutter_foreground_task.dart';
import '../l10n/app_localizations.dart';
import '../utils/platform_info.dart';
class BackgroundService {
bool _initialized = false;
String? Function()? _languageOverrideProvider;
/// Allows the app to expose its current language override (e.g. from
/// AppSettingsService) so the foreground notification matches the app UI
/// language instead of only the system locale.
void setLanguageOverrideProvider(String? Function()? provider) {
_languageOverrideProvider = provider;
}
Future<void> initialize() async {
if (!PlatformInfo.isAndroid || _initialized) return;
@@ -34,13 +45,32 @@ class BackgroundService {
}
final running = await FlutterForegroundTask.isRunningService;
if (running) return;
final l10n = await _loadLocalizations();
await FlutterForegroundTask.startService(
notificationTitle: 'MeshCore running',
notificationText: 'Keeping BLE connected',
notificationTitle: l10n.background_serviceTitle,
notificationText: l10n.background_serviceText,
callback: startCallback,
);
}
Future<AppLocalizations> _loadLocalizations() async {
final supported = AppLocalizations.supportedLocales;
final override = _languageOverrideProvider?.call();
if (override != null && override.isNotEmpty) {
final overrideLocale = Locale(override);
final isSupported = supported.any(
(l) => l.languageCode == overrideLocale.languageCode,
);
if (isSupported) {
return AppLocalizations.delegate.load(overrideLocale);
}
}
final preferred =
WidgetsBinding.instance.platformDispatcher.locales;
final match = basicLocaleListResolution(preferred, supported);
return AppLocalizations.delegate.load(match);
}
Future<void> stop() async {
if (!PlatformInfo.isAndroid) return;
final running = await FlutterForegroundTask.isRunningService;
+48 -1
View File
@@ -24,6 +24,26 @@ class LineOfSightSample {
});
}
class LineOfSightObstruction {
final int sampleIndex;
final LatLng point;
final double distanceMeters;
final double clearanceMeters;
final double obstructionMeters;
final double terrainMeters;
final double lineHeightMeters;
const LineOfSightObstruction({
required this.sampleIndex,
required this.point,
required this.distanceMeters,
required this.clearanceMeters,
required this.obstructionMeters,
required this.terrainMeters,
required this.lineHeightMeters,
});
}
class LineOfSightResult {
final bool hasData;
final bool isClear;
@@ -31,6 +51,7 @@ class LineOfSightResult {
final double maxObstructionMeters;
final double? firstObstructionDistanceMeters;
final List<LineOfSightSample> samples;
final List<LineOfSightObstruction> obstructions;
final String? errorMessage;
final double usedKFactor;
final double? frequencyMHz;
@@ -42,6 +63,7 @@ class LineOfSightResult {
required this.maxObstructionMeters,
required this.firstObstructionDistanceMeters,
required this.samples,
required this.obstructions,
required this.usedKFactor,
this.frequencyMHz,
this.errorMessage,
@@ -56,7 +78,8 @@ class LineOfSightResult {
isClear = false,
maxObstructionMeters = 0,
firstObstructionDistanceMeters = null,
samples = const [];
samples = const [],
obstructions = const [];
}
class LineOfSightPathSegment {
@@ -191,6 +214,7 @@ class LineOfSightService {
maxObstructionMeters: 0,
firstObstructionDistanceMeters: null,
samples: const [],
obstructions: const [],
usedKFactor: kFactor,
frequencyMHz: frequencyMHz,
);
@@ -249,7 +273,9 @@ class LineOfSightService {
var maxObstructionMeters = 0.0;
double? firstObstructionDistanceMeters;
final samples = <LineOfSightSample>[];
final obstructions = <LineOfSightObstruction>[];
var isClear = true;
LineOfSightObstruction? clusterWorstObstruction;
for (int i = 0; i < points.length; i++) {
final fraction = points.length == 1 ? 0.0 : i / (points.length - 1);
@@ -274,6 +300,23 @@ class LineOfSightService {
maxObstructionMeters = obstruction;
}
firstObstructionDistanceMeters ??= distanceFromStart;
final candidate = LineOfSightObstruction(
sampleIndex: i,
point: points[i],
distanceMeters: distanceFromStart,
clearanceMeters: clearance,
obstructionMeters: obstruction,
terrainMeters: terrainHeight,
lineHeightMeters: lineHeight,
);
if (clusterWorstObstruction == null ||
candidate.obstructionMeters >
clusterWorstObstruction.obstructionMeters) {
clusterWorstObstruction = candidate;
}
} else if (clusterWorstObstruction != null) {
obstructions.add(clusterWorstObstruction);
clusterWorstObstruction = null;
}
samples.add(
@@ -286,6 +329,9 @@ class LineOfSightService {
),
);
}
if (clusterWorstObstruction != null) {
obstructions.add(clusterWorstObstruction);
}
return LineOfSightResult(
hasData: true,
@@ -294,6 +340,7 @@ class LineOfSightService {
maxObstructionMeters: maxObstructionMeters,
firstObstructionDistanceMeters: firstObstructionDistanceMeters,
samples: samples,
obstructions: obstructions,
usedKFactor: kFactor,
frequencyMHz: frequencyMHz,
);