refactor
This commit is contained in:
@@ -3,9 +3,7 @@ import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
import 'package:lbjconsole/screens/main_screen.dart';
|
||||
import 'package:lbjconsole/util/train_type_util.dart';
|
||||
import 'package:lbjconsole/util/loco_info_util.dart';
|
||||
import 'package:lbjconsole/util/loco_type_util.dart';
|
||||
import 'package:lbjconsole/services/loco_type_service.dart';
|
||||
import 'package:lbjconsole/services/database_service.dart';
|
||||
import 'package:lbjconsole/services/background_service.dart';
|
||||
|
||||
void main() async {
|
||||
|
||||
@@ -45,7 +45,7 @@ class MapState {
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'MapState(zoom: ' + zoom.toString() + ', centerLat: ' + centerLat.toString() + ', centerLng: ' + centerLng.toString() + ', bearing: ' + bearing.toString() + ')';
|
||||
return 'MapState(zoom: $zoom, centerLat: $centerLat, centerLng: $centerLng, bearing: $bearing)';
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -340,22 +340,6 @@ class HistoryScreenState extends State<HistoryScreen> {
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
String _getGroupKeyForRecord(TrainRecord record, MergeSettings settings) {
|
||||
switch (settings.groupBy) {
|
||||
case GroupBy.trainOnly:
|
||||
return record.train.trim();
|
||||
case GroupBy.locoOnly:
|
||||
return record.loco.trim();
|
||||
case GroupBy.trainAndLoco:
|
||||
return '${record.train.trim()}-${record.loco.trim()}';
|
||||
case GroupBy.trainOrLoco:
|
||||
final train = record.train.trim();
|
||||
if (train.isNotEmpty) return train;
|
||||
final loco = record.loco.trim();
|
||||
if (loco.isNotEmpty) return loco;
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
bool _hasDataChanged(List<Object> newItems) {
|
||||
if (_displayItems.length != newItems.length) return true;
|
||||
|
||||
@@ -3,8 +3,6 @@ import 'package:flutter/services.dart';
|
||||
import 'dart:async';
|
||||
import 'dart:developer' as developer;
|
||||
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
|
||||
import 'package:lbjconsole/models/merged_record.dart';
|
||||
import 'package:lbjconsole/models/train_record.dart';
|
||||
import 'package:lbjconsole/screens/history_screen.dart';
|
||||
import 'package:lbjconsole/screens/map_screen.dart';
|
||||
import 'package:lbjconsole/screens/map_webview_screen.dart';
|
||||
@@ -307,17 +305,17 @@ class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver {
|
||||
developer.log('rtl_tcp: setup_listener');
|
||||
_settingsSubscription =
|
||||
DatabaseService.instance.onSettingsChanged((settings) {
|
||||
developer.log('rtl_tcp: settings_changed: enabled=${(settings?['rtlTcpEnabled'] ?? 0) == 1}, host=${settings?['rtlTcpHost']?.toString() ?? '127.0.0.1'}, port=${settings?['rtlTcpPort']?.toString() ?? '14423'}');
|
||||
developer.log('rtl_tcp: settings_changed: enabled=${(settings['rtlTcpEnabled'] ?? 0) == 1}, host=${settings['rtlTcpHost']?.toString() ?? '127.0.0.1'}, port=${settings['rtlTcpPort']?.toString() ?? '14423'}');
|
||||
if (mounted) {
|
||||
final rtlTcpEnabled = (settings?['rtlTcpEnabled'] ?? 0) == 1;
|
||||
final rtlTcpEnabled = (settings['rtlTcpEnabled'] ?? 0) == 1;
|
||||
if (rtlTcpEnabled != _rtlTcpEnabled) {
|
||||
setState(() {
|
||||
_rtlTcpEnabled = rtlTcpEnabled;
|
||||
});
|
||||
|
||||
if (rtlTcpEnabled) {
|
||||
final host = settings?['rtlTcpHost']?.toString() ?? '127.0.0.1';
|
||||
final port = settings?['rtlTcpPort']?.toString() ?? '14423';
|
||||
final host = settings['rtlTcpHost']?.toString() ?? '127.0.0.1';
|
||||
final port = settings['rtlTcpPort']?.toString() ?? '14423';
|
||||
_connectToRtlTcp(host, port);
|
||||
} else {
|
||||
_rtlTcpConnectionSubscription?.cancel();
|
||||
@@ -661,11 +659,12 @@ class _PixelPerfectBluetoothDialogState
|
||||
|
||||
Future<void> _startScan() async {
|
||||
if (_scanState == _ScanState.scanning) return;
|
||||
if (mounted)
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_devices.clear();
|
||||
_scanState = _ScanState.scanning;
|
||||
});
|
||||
}
|
||||
await widget.bleService.startScan(
|
||||
timeout: const Duration(seconds: 8),
|
||||
onScanResults: (devices) {
|
||||
@@ -784,7 +783,7 @@ class _PixelPerfectBluetoothDialogState
|
||||
.titleMedium
|
||||
?.copyWith(fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 8),
|
||||
Text('$currentAddress',
|
||||
Text(currentAddress,
|
||||
style: TextStyle(color: isConnected ? Colors.green : Colors.grey)),
|
||||
const SizedBox(height: 16),
|
||||
if (_lastReceivedTime != null && isConnected) ...[
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'dart:async';
|
||||
import 'dart:math' show sin, cos, sqrt, atan2, pi;
|
||||
import 'dart:math' show sin, cos, sqrt, atan2;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_map/flutter_map.dart';
|
||||
import 'package:latlong2/latlong.dart';
|
||||
@@ -26,19 +26,19 @@ class _MapScreenState extends State<MapScreen> {
|
||||
double _currentRotation = 0.0;
|
||||
|
||||
bool _isMapInitialized = false;
|
||||
bool _isFollowingLocation = false;
|
||||
final bool _isFollowingLocation = false;
|
||||
bool _isLocationPermissionGranted = false;
|
||||
Timer? _locationTimer;
|
||||
|
||||
String _selectedTimeFilter = 'unlimited';
|
||||
final Map<String, Duration> _timeFilterOptions = {
|
||||
'unlimited': Duration.zero,
|
||||
'1hour': Duration(hours: 1),
|
||||
'6hours': Duration(hours: 6),
|
||||
'12hours': Duration(hours: 12),
|
||||
'24hours': Duration(hours: 24),
|
||||
'7days': Duration(days: 7),
|
||||
'30days': Duration(days: 30),
|
||||
'1hour': const Duration(hours: 1),
|
||||
'6hours': const Duration(hours: 6),
|
||||
'12hours': const Duration(hours: 12),
|
||||
'24hours': const Duration(hours: 24),
|
||||
'7days': const Duration(days: 7),
|
||||
'30days': const Duration(days: 30),
|
||||
};
|
||||
|
||||
@override
|
||||
@@ -81,8 +81,8 @@ class _MapScreenState extends State<MapScreen> {
|
||||
if (lat == 39.9042 && lon == 116.4074) {
|
||||
} else if (lat == 0.0 && lon == 0.0) {
|
||||
} else {
|
||||
final beijingLat = 39.9042;
|
||||
final beijingLon = 116.4074;
|
||||
const beijingLat = 39.9042;
|
||||
const beijingLon = 116.4074;
|
||||
final distance =
|
||||
_calculateDistance(lat, lon, beijingLat, beijingLon);
|
||||
|
||||
@@ -411,7 +411,7 @@ class _MapScreenState extends State<MapScreen> {
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black.withOpacity(0.8),
|
||||
color: Colors.black.withValues(alpha: 0.8),
|
||||
borderRadius: BorderRadius.circular(3),
|
||||
),
|
||||
child: Text(
|
||||
@@ -572,8 +572,8 @@ class _MapScreenState extends State<MapScreen> {
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.surfaceVariant
|
||||
.withOpacity(0.3),
|
||||
.surfaceContainerHighest
|
||||
.withValues(alpha: 0.3),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Padding(
|
||||
|
||||
@@ -32,7 +32,7 @@ class MapWebViewScreenState extends State<MapWebViewScreen>
|
||||
double _currentRotation = 0.0;
|
||||
LatLng? _currentLocation;
|
||||
LatLng? _lastTrainLocation;
|
||||
bool _isDataLoaded = false;
|
||||
final bool _isDataLoaded = false;
|
||||
final Completer<void> _webViewReadyCompleter = Completer<void>();
|
||||
|
||||
@override
|
||||
@@ -299,7 +299,7 @@ class MapWebViewScreenState extends State<MapWebViewScreen>
|
||||
_updateTrainMarkers();
|
||||
}
|
||||
});
|
||||
} catch (e, stackTrace) {
|
||||
} catch (e) {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
|
||||
@@ -30,7 +30,7 @@ class RealtimeScreenState extends State<RealtimeScreen> {
|
||||
List<LatLng> _selectedGroupRoute = [];
|
||||
List<Marker> _mapMarkers = [];
|
||||
bool _showMap = true;
|
||||
Set<String> _selectedGroupKeys = {};
|
||||
final Set<String> _selectedGroupKeys = {};
|
||||
LatLng? _userLocation;
|
||||
bool _isLocationPermissionGranted = false;
|
||||
Timer? _locationTimer;
|
||||
@@ -376,8 +376,7 @@ class RealtimeScreenState extends State<RealtimeScreen> {
|
||||
}
|
||||
|
||||
LatLng? _parsePositionFromRecord(TrainRecord record) {
|
||||
if (record.positionInfo == null ||
|
||||
record.positionInfo.isEmpty ||
|
||||
if (record.positionInfo.isEmpty ||
|
||||
record.positionInfo == '<NUL>') {
|
||||
return null;
|
||||
}
|
||||
@@ -916,8 +915,8 @@ class RealtimeScreenState extends State<RealtimeScreen> {
|
||||
flex: 1,
|
||||
child: FlutterMap(
|
||||
mapController: _mapController,
|
||||
options: MapOptions(
|
||||
initialCenter: const LatLng(35.8617, 104.1954),
|
||||
options: const MapOptions(
|
||||
initialCenter: LatLng(35.8617, 104.1954),
|
||||
initialZoom: 2.0,
|
||||
),
|
||||
children: [
|
||||
|
||||
@@ -4,17 +4,13 @@ import 'dart:io';
|
||||
|
||||
import 'package:lbjconsole/models/merged_record.dart';
|
||||
import 'package:lbjconsole/services/database_service.dart';
|
||||
import 'package:lbjconsole/services/ble_service.dart';
|
||||
import 'package:lbjconsole/services/background_service.dart';
|
||||
import 'package:lbjconsole/themes/app_theme.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
import 'package:cross_file/cross_file.dart';
|
||||
|
||||
class SettingsScreen extends StatefulWidget {
|
||||
final VoidCallback? onSettingsChanged;
|
||||
@@ -73,14 +69,14 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
Icon(Icons.wifi,
|
||||
color: Theme.of(context).colorScheme.primary),
|
||||
const SizedBox(width: 12),
|
||||
Text('RTL-TCP 源', style: AppTheme.titleMedium),
|
||||
const Text('RTL-TCP 源', style: AppTheme.titleMedium),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Column(
|
||||
const Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('启用 RTL-TCP 源', style: AppTheme.bodyLarge),
|
||||
@@ -94,7 +90,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
});
|
||||
_saveSettings();
|
||||
},
|
||||
activeColor: Theme.of(context).colorScheme.primary,
|
||||
activeThumbColor: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -273,7 +269,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
Icon(Icons.bluetooth,
|
||||
color: Theme.of(context).colorScheme.primary),
|
||||
const SizedBox(width: 12),
|
||||
Text('蓝牙设备', style: AppTheme.titleMedium),
|
||||
const Text('蓝牙设备', style: AppTheme.titleMedium),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
@@ -329,14 +325,14 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
Icon(Icons.settings,
|
||||
color: Theme.of(context).colorScheme.primary),
|
||||
const SizedBox(width: 12),
|
||||
Text('应用设置', style: AppTheme.titleMedium),
|
||||
const Text('应用设置', style: AppTheme.titleMedium),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Column(
|
||||
const Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('后台保活服务', style: AppTheme.bodyLarge),
|
||||
@@ -356,7 +352,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
await BackgroundService.stopService();
|
||||
}
|
||||
},
|
||||
activeColor: Theme.of(context).colorScheme.primary,
|
||||
activeThumbColor: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -364,7 +360,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Column(
|
||||
const Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('通知服务', style: AppTheme.bodyLarge),
|
||||
@@ -378,7 +374,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
});
|
||||
_saveSettings();
|
||||
},
|
||||
activeColor: Theme.of(context).colorScheme.primary,
|
||||
activeThumbColor: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -386,7 +382,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Column(
|
||||
const Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('地图组件类型', style: AppTheme.bodyLarge),
|
||||
@@ -394,7 +390,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
),
|
||||
DropdownButton<String>(
|
||||
value: _mapType,
|
||||
items: [
|
||||
items: const [
|
||||
DropdownMenuItem(
|
||||
value: 'webview',
|
||||
child: Text('矢量铁路地图', style: AppTheme.bodyMedium),
|
||||
@@ -422,7 +418,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Column(
|
||||
const Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('隐藏只有时间有效的记录', style: AppTheme.bodyLarge),
|
||||
@@ -436,7 +432,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
});
|
||||
_saveSettings();
|
||||
},
|
||||
activeColor: Theme.of(context).colorScheme.primary,
|
||||
activeThumbColor: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -463,14 +459,14 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
Icon(Icons.merge_type,
|
||||
color: Theme.of(context).colorScheme.primary),
|
||||
const SizedBox(width: 12),
|
||||
Text('记录合并', style: AppTheme.titleMedium),
|
||||
const Text('记录合并', style: AppTheme.titleMedium),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Column(
|
||||
const Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('启用记录合并', style: AppTheme.bodyLarge),
|
||||
@@ -484,7 +480,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
});
|
||||
_saveSettings();
|
||||
},
|
||||
activeColor: Theme.of(context).colorScheme.primary,
|
||||
activeThumbColor: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -494,11 +490,11 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 16),
|
||||
Text('分组方式', style: AppTheme.bodyLarge),
|
||||
const Text('分组方式', style: AppTheme.bodyLarge),
|
||||
const SizedBox(height: 8),
|
||||
DropdownButtonFormField<GroupBy>(
|
||||
value: _groupBy,
|
||||
items: [
|
||||
initialValue: _groupBy,
|
||||
items: const [
|
||||
DropdownMenuItem(
|
||||
value: GroupBy.trainOnly,
|
||||
child: Text('仅车次号', style: AppTheme.bodyMedium)),
|
||||
@@ -532,11 +528,11 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
style: AppTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text('时间窗口', style: AppTheme.bodyLarge),
|
||||
const Text('时间窗口', style: AppTheme.bodyLarge),
|
||||
const SizedBox(height: 8),
|
||||
DropdownButtonFormField<TimeWindow>(
|
||||
value: _timeWindow,
|
||||
items: [
|
||||
initialValue: _timeWindow,
|
||||
items: const [
|
||||
DropdownMenuItem(
|
||||
value: TimeWindow.oneHour,
|
||||
child: Text('1小时内', style: AppTheme.bodyMedium)),
|
||||
@@ -579,7 +575,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Column(
|
||||
const Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('隐藏不可分组记录', style: AppTheme.bodyLarge),
|
||||
@@ -593,7 +589,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
});
|
||||
_saveSettings();
|
||||
},
|
||||
activeColor: Theme.of(context).colorScheme.primary,
|
||||
activeThumbColor: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -623,7 +619,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
Icon(Icons.storage,
|
||||
color: Theme.of(context).colorScheme.primary),
|
||||
const SizedBox(width: 12),
|
||||
Text('数据管理', style: AppTheme.titleMedium),
|
||||
const Text('数据管理', style: AppTheme.titleMedium),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
@@ -705,7 +701,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
],
|
||||
),
|
||||
),
|
||||
Icon(
|
||||
const Icon(
|
||||
Icons.chevron_right,
|
||||
color: Colors.white54,
|
||||
size: 20,
|
||||
@@ -760,7 +756,6 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
|
||||
if (exportedPath != null) {
|
||||
final file = File(exportedPath);
|
||||
final fileName = file.path.split(Platform.pathSeparator).last;
|
||||
|
||||
await Share.shareXFiles(
|
||||
[XFile(file.path)],
|
||||
@@ -954,11 +949,11 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
children: [
|
||||
Icon(Icons.info, color: Theme.of(context).colorScheme.primary),
|
||||
const SizedBox(width: 12),
|
||||
Text('关于', style: AppTheme.titleMedium),
|
||||
const Text('关于', style: AppTheme.titleMedium),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text('LBJ Console', style: AppTheme.titleMedium),
|
||||
const Text('LBJ Console', style: AppTheme.titleMedium),
|
||||
const SizedBox(height: 8),
|
||||
FutureBuilder<String>(
|
||||
future: _getAppVersion(),
|
||||
@@ -979,7 +974,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
await launchUrl(url);
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
child: const Text(
|
||||
'https://github.com/undef-i/LBJConsole',
|
||||
style: AppTheme.caption,
|
||||
),
|
||||
|
||||
@@ -3,7 +3,6 @@ import 'dart:io';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter_background_service/flutter_background_service.dart';
|
||||
import 'package:flutter_background_service_android/flutter_background_service_android.dart';
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
import 'package:lbjconsole/services/ble_service.dart';
|
||||
|
||||
@@ -107,7 +106,7 @@ class BackgroundService {
|
||||
_notificationId,
|
||||
'LBJ Console',
|
||||
'蓝牙连接监控中',
|
||||
NotificationDetails(
|
||||
const NotificationDetails(
|
||||
android: AndroidNotificationDetails(
|
||||
_notificationChannelId,
|
||||
_notificationChannelName,
|
||||
@@ -146,7 +145,7 @@ class BackgroundService {
|
||||
_notificationId,
|
||||
'LBJ Console',
|
||||
isConnected ? '蓝牙已连接 - $deviceStatus' : '蓝牙未连接 - 自动重连中',
|
||||
NotificationDetails(
|
||||
const NotificationDetails(
|
||||
android: AndroidNotificationDetails(
|
||||
_notificationChannelId,
|
||||
_notificationChannelName,
|
||||
|
||||
@@ -145,7 +145,9 @@ class BLEService {
|
||||
if (isConnected ||
|
||||
_isConnecting ||
|
||||
_isManualDisconnect ||
|
||||
_isAutoConnectBlocked) return;
|
||||
_isAutoConnectBlocked) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (var device in allFoundDevices) {
|
||||
if (_shouldAutoConnectTo(device)) {
|
||||
@@ -168,10 +170,13 @@ class BLEService {
|
||||
final deviceAddress = device.remoteId.str;
|
||||
|
||||
if (_targetDeviceName.isNotEmpty &&
|
||||
deviceName.toLowerCase() == _targetDeviceName.toLowerCase())
|
||||
deviceName.toLowerCase() == _targetDeviceName.toLowerCase()) {
|
||||
return true;
|
||||
}
|
||||
if (_lastKnownDeviceAddress != null &&
|
||||
_lastKnownDeviceAddress == deviceAddress) return true;
|
||||
_lastKnownDeviceAddress == deviceAddress) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ class DatabaseService {
|
||||
}
|
||||
_database = await _initDatabase();
|
||||
return _database!;
|
||||
} catch (e, stackTrace) {
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
@@ -38,8 +38,6 @@ class DatabaseService {
|
||||
return false;
|
||||
}
|
||||
|
||||
final db = await database;
|
||||
final result = await db.rawQuery('SELECT 1');
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
@@ -59,7 +57,7 @@ class DatabaseService {
|
||||
);
|
||||
|
||||
return db;
|
||||
} catch (e, stackTrace) {
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
@@ -206,7 +204,7 @@ class DatabaseService {
|
||||
final records =
|
||||
result.map((json) => TrainRecord.fromDatabaseJson(json)).toList();
|
||||
return records;
|
||||
} catch (e, stackTrace) {
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
@@ -239,7 +237,7 @@ class DatabaseService {
|
||||
final records =
|
||||
result.map((json) => TrainRecord.fromDatabaseJson(json)).toList();
|
||||
return records;
|
||||
} catch (e, stackTrace) {
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'package:lbjconsole/util/loco_type_util.dart';
|
||||
|
||||
class LocoTypeService {
|
||||
static final LocoTypeService _instance = LocoTypeService._internal();
|
||||
|
||||
@@ -111,7 +111,7 @@ class MergeService {
|
||||
}
|
||||
});
|
||||
|
||||
final reusedRecords = _reuseDiscardedRecords(
|
||||
_reuseDiscardedRecords(
|
||||
discardedRecords, mergedRecordIds, settings.groupBy);
|
||||
|
||||
final singleRecords = filteredRecords
|
||||
@@ -283,100 +283,4 @@ class MergeService {
|
||||
return result;
|
||||
}
|
||||
|
||||
static List<Object> _groupByTrainOrLoco(List<TrainRecord> records) {
|
||||
final List<MergedTrainRecord> mergedRecords = [];
|
||||
final List<TrainRecord> singleRecords = [];
|
||||
final Set<String> usedRecordIds = {};
|
||||
|
||||
for (int i = 0; i < records.length; i++) {
|
||||
final record = records[i];
|
||||
if (usedRecordIds.contains(record.uniqueId)) continue;
|
||||
|
||||
final group = <TrainRecord>[record];
|
||||
|
||||
for (int j = i + 1; j < records.length; j++) {
|
||||
final otherRecord = records[j];
|
||||
if (usedRecordIds.contains(otherRecord.uniqueId)) continue;
|
||||
|
||||
final recordTrain = record.train.trim();
|
||||
final otherTrain = otherRecord.train.trim();
|
||||
final recordLoco = record.loco.trim();
|
||||
final otherLoco = otherRecord.loco.trim();
|
||||
|
||||
final trainMatch = recordTrain.isNotEmpty &&
|
||||
recordTrain != "<NUL>" &&
|
||||
!recordTrain.contains("-----") &&
|
||||
otherTrain.isNotEmpty &&
|
||||
otherTrain != "<NUL>" &&
|
||||
!otherTrain.contains("-----") &&
|
||||
recordTrain == otherTrain;
|
||||
|
||||
final locoMatch = recordLoco.isNotEmpty &&
|
||||
recordLoco != "<NUL>" &&
|
||||
otherLoco.isNotEmpty &&
|
||||
otherLoco != "<NUL>" &&
|
||||
recordLoco == otherLoco;
|
||||
|
||||
final bothTrainEmpty = (recordTrain.isEmpty ||
|
||||
recordTrain == "<NUL>" ||
|
||||
recordTrain.contains("----")) &&
|
||||
(otherTrain.isEmpty ||
|
||||
otherTrain == "<NUL>" ||
|
||||
otherTrain.contains("----"));
|
||||
|
||||
if (trainMatch || locoMatch || (bothTrainEmpty && locoMatch)) {
|
||||
group.add(otherRecord);
|
||||
}
|
||||
}
|
||||
|
||||
if (group.length >= 2) {
|
||||
for (final record in group) {
|
||||
usedRecordIds.add(record.uniqueId);
|
||||
}
|
||||
|
||||
final firstRecord = group.first;
|
||||
final train = firstRecord.train.trim();
|
||||
final loco = firstRecord.loco.trim();
|
||||
String uniqueGroupKey;
|
||||
|
||||
if (train.isNotEmpty &&
|
||||
train != "<NUL>" &&
|
||||
!train.contains("-----") &&
|
||||
loco.isNotEmpty &&
|
||||
loco != "<NUL>") {
|
||||
uniqueGroupKey = "train_or_loco:${train}_$loco";
|
||||
} else if (train.isNotEmpty &&
|
||||
train != "<NUL>" &&
|
||||
!train.contains("-----")) {
|
||||
uniqueGroupKey = "train_or_loco:train:$train";
|
||||
} else if (loco.isNotEmpty && loco != "<NUL>") {
|
||||
uniqueGroupKey = "train_or_loco:loco:$loco";
|
||||
} else {
|
||||
uniqueGroupKey = "train_or_loco:group_${mergedRecords.length}";
|
||||
}
|
||||
|
||||
mergedRecords.add(MergedTrainRecord(
|
||||
groupKey: uniqueGroupKey,
|
||||
records: group,
|
||||
latestRecord: group.first,
|
||||
));
|
||||
} else {
|
||||
singleRecords.add(record);
|
||||
usedRecordIds.add(record.uniqueId);
|
||||
}
|
||||
}
|
||||
|
||||
final List<Object> result = [...mergedRecords, ...singleRecords];
|
||||
result.sort((a, b) {
|
||||
final aTime = a is MergedTrainRecord
|
||||
? a.latestRecord.receivedTimestamp
|
||||
: (a as TrainRecord).receivedTimestamp;
|
||||
final bTime = b is MergedTrainRecord
|
||||
? b.latestRecord.receivedTimestamp
|
||||
: (b as TrainRecord).receivedTimestamp;
|
||||
return bTime.compareTo(aTime);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ class NotificationService {
|
||||
const AndroidInitializationSettings initializationSettingsAndroid =
|
||||
AndroidInitializationSettings('@mipmap/ic_launcher');
|
||||
|
||||
final InitializationSettings initializationSettings =
|
||||
const InitializationSettings initializationSettings =
|
||||
InitializationSettings(
|
||||
android: initializationSettingsAndroid,
|
||||
);
|
||||
@@ -61,7 +61,7 @@ class NotificationService {
|
||||
return;
|
||||
}
|
||||
|
||||
final String title = '列车信息';
|
||||
const String title = '列车信息';
|
||||
final String body = _buildNotificationContent(record);
|
||||
|
||||
final AndroidNotificationDetails androidPlatformChannelSpecifics =
|
||||
|
||||
@@ -149,8 +149,9 @@ class _LbJState {
|
||||
break;
|
||||
|
||||
case _lbjSyncAddr:
|
||||
if (numeric.length >= 5)
|
||||
if (numeric.length >= 5) {
|
||||
time = "${numeric.substring(1, 3)}:${numeric.substring(3, 5)}";
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -167,11 +168,11 @@ class _LbJState {
|
||||
|
||||
String gpsPosition = "";
|
||||
if (posLatDeg.isNotEmpty && posLatMin.isNotEmpty) {
|
||||
gpsPosition = "${posLatDeg}°${posLatMin}′";
|
||||
gpsPosition = "$posLatDeg°$posLatMin′";
|
||||
}
|
||||
if (posLonDeg.isNotEmpty && posLonMin.isNotEmpty) {
|
||||
gpsPosition +=
|
||||
(gpsPosition.isEmpty ? "" : " ") + "${posLonDeg}°${posLonMin}′";
|
||||
"${gpsPosition.isEmpty ? "" : " "}$posLonDeg°$posLonMin′";
|
||||
}
|
||||
|
||||
String kmPosition = positionKm.replaceAll(' <NUL>', '');
|
||||
|
||||
@@ -9,13 +9,11 @@ class AppTheme {
|
||||
canvasColor: Colors.black,
|
||||
cardColor: const Color(0xFF121212),
|
||||
primaryColor: Colors.blue,
|
||||
colorScheme: ColorScheme.dark(
|
||||
colorScheme: const ColorScheme.dark(
|
||||
primary: Colors.blue,
|
||||
secondary: Colors.blueAccent,
|
||||
surface: const Color(0xFF121212),
|
||||
background: Colors.black,
|
||||
surface: Color(0xFF121212),
|
||||
onSurface: Colors.white,
|
||||
onBackground: Colors.white,
|
||||
),
|
||||
appBarTheme: const AppBarTheme(
|
||||
backgroundColor: Colors.black,
|
||||
@@ -67,16 +65,16 @@ class AppTheme {
|
||||
thickness: 1,
|
||||
),
|
||||
switchTheme: SwitchThemeData(
|
||||
thumbColor: MaterialStateProperty.resolveWith<Color?>(
|
||||
(Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.selected)) {
|
||||
thumbColor: WidgetStateProperty.resolveWith<Color?>(
|
||||
(Set<WidgetState> states) {
|
||||
if (states.contains(WidgetState.selected)) {
|
||||
return Colors.blue;
|
||||
}
|
||||
return Colors.grey;
|
||||
}),
|
||||
trackColor: MaterialStateProperty.resolveWith<Color?>(
|
||||
(Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.selected)) {
|
||||
trackColor: WidgetStateProperty.resolveWith<Color?>(
|
||||
(Set<WidgetState> states) {
|
||||
if (states.contains(WidgetState.selected)) {
|
||||
return Colors.blue.withOpacity(0.5);
|
||||
}
|
||||
return Colors.grey.withOpacity(0.5);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class LocoInfoUtil {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class TrainTypeUtil {
|
||||
|
||||
Reference in New Issue
Block a user