首次提交:关于页、夜间模式选项

This commit is contained in:
2026-07-30 22:24:59 +08:00
parent dac9529f96
commit b6d623a1eb
10 changed files with 430 additions and 94 deletions
+123 -49
View File
@@ -7,7 +7,7 @@ import 'package:device_info_plus/device_info_plus.dart';
// --- 本地导入 ---
import '../models/device_matching.dart';
import '../models/saf_permission_state.dart';
import '../models/data_abnormal_type.dart'; // 🆕 1.7.0
import '../models/data_abnormal_type.dart';
import '../services/database_service.dart';
import '../services/battery_data_service.dart';
import '../services/saf_storage_service.dart';
@@ -21,6 +21,7 @@ import '../widgets/charge_entry.dart';
import '../widgets/charge_detail_page.dart';
import '../widgets/trend_chart.dart';
import '../pages/changelog_page.dart';
import '../pages/about_page.dart';
// --- 潘通色定义 ---
const Color kHealthGreen = Color(0xFF00B140);
@@ -48,6 +49,9 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
double _minVolt = 0;
bool _hasData = false;
// ---------- 🆕 1.7.0 异常状态标记 ----------
bool _hasAbnormalDataToday = false;
// ---------- 趋势图数据 ----------
List<Map<String, dynamic>> _trendData = [];
@@ -293,6 +297,9 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
Future<void> readBatteryCapacity() async {
dev.log('🔴🔴🔴 readBatteryCapacity 被调用了 🔴🔴🔴', name: 'BatteryHealth');
// 🟢 每次读取时重置异常标记
_hasAbnormalDataToday = false;
_safState = await _safService.getPermissionState();
dev.log('🔵 SAF状态: $_safState', name: 'BatteryHealth');
@@ -364,29 +371,54 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
name: 'BatteryHealth',
);
// 🆕 1.7.0 计算异常比例
final abnormalRatio = _calculateAbnormalRatio(lines, _designCap);
dev.log(
'🔵 异常比例: ${(abnormalRatio * 100).toStringAsFixed(1)}%',
name: 'BatteryHealth',
);
final cap = CapacityExtractor.extractCapacity(lines);
int? finalCap;
if (cap != null) {
// 🆕 1.7.0 异常处理逻辑
if (cap != null && abnormalRatio < 0.5) {
// 短时尖刺:走校验
final validatedCap = _validateCapacity(cap, _designCap);
if (validatedCap != null) {
finalCap = validatedCap;
_hasAbnormalDataToday = false;
dev.log('🔵 短时尖刺校验通过: $cap', name: 'BatteryHealth');
} else {
finalCap = null;
_hasAbnormalDataToday = true;
dev.log('⚠️ 短时尖刺,已丢弃: $cap', name: 'BatteryHealth');
}
} else if (cap != null && abnormalRatio >= 0.5) {
// 长期偏离:放行(疑似扩容)
finalCap = cap;
dev.log('🔵 从日志提取容量: $cap', name: 'BatteryHealth');
_hasAbnormalDataToday = false;
dev.log(
'🔵 长期偏离(${(abnormalRatio * 100).toStringAsFixed(0)}% 记录 > 115%),疑似扩容电池,已放行: $cap',
name: 'BatteryHealth',
);
} else {
// cap == null,尝试复制前一天
dev.log('🔵 今日无充满记录,尝试复制前一天容量', name: 'BatteryHealth');
final recent = await _dbService.queryRecentData(1);
if (recent.isNotEmpty) {
finalCap = recent.first['capacity'] as int;
_hasAbnormalDataToday = false;
dev.log('🔵 复制前一天容量: $finalCap', name: 'BatteryHealth');
} else {
finalCap = null;
_hasAbnormalDataToday = false;
dev.log('🔵 无历史数据可复制', name: 'BatteryHealth');
}
}
// 🟢 1.7.0 容量有效性校验
final validatedCap = _validateCapacity(finalCap, _designCap);
setState(() {
_currentCap = (validatedCap ?? _currentCap).toDouble();
_currentCap = (finalCap ?? _currentCap).toDouble();
_cycleCount = cycle;
_batteryTemp = temp;
_batteryVolt = volt;
@@ -413,17 +445,40 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
}
}
// ---------- 🆕 1.7.0 统计当天超过 115% 的容量记录占比 ----------
double _calculateAbnormalRatio(List<String> lines, int designCap) {
final capReg = RegExp(r'(?:learned_cap|remaining_cap)=(\d+)');
int totalCount = 0;
int abnormalCount = 0;
for (String line in lines) {
if (!line.contains('tbat=')) continue;
final match = capReg.firstMatch(line);
if (match == null) continue;
final value = int.parse(match.group(1)!);
final capInMah = value > 100000 ? value ~/ 1000 : value;
totalCount++;
if (capInMah > designCap * 1.15) {
abnormalCount++;
}
}
if (totalCount == 0) return 0.0;
return abnormalCount / totalCount;
}
// ---------- 🆕 1.7.0 容量有效性校验 ----------
int? _validateCapacity(int? capacity, int designCap) {
if (capacity == null) return null;
// 容量必须大于 0
if (capacity <= 0) {
dev.log('⚠️ 容量异常:<= 0,已忽略', name: 'BatteryHealth');
return null;
}
// 容量不能低于设计容量的 60%
if (capacity < designCap * 0.6) {
dev.log(
'⚠️ 容量异常:低于设计容量 60%$capacity < ${designCap * 0.6}),已忽略',
@@ -432,7 +487,6 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
return null;
}
// 容量不能超过设计容量的 115%
if (capacity > designCap * 1.15) {
dev.log(
'⚠️ 容量异常:高于设计容量 115%($capacity > ${designCap * 1.15}),已忽略',
@@ -449,12 +503,14 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
double currentCap,
int designCap,
double voltage,
bool hasAbnormalDataToday,
) {
// 容量异常:高于 115% 或低于 60%
// 🟢 优先:今天有异常数据被丢弃 → 强制显示红色
if (hasAbnormalDataToday) return DataAbnormalType.capacityTooHigh;
// 其次:当前值异常
if (currentCap > designCap * 1.15) return DataAbnormalType.capacityTooHigh;
if (currentCap < designCap * 0.6) return DataAbnormalType.capacityTooLow;
// 电压异常:低于 3.0V 或高于 5.0V
if (voltage < 3.0 || voltage > 5.0) return DataAbnormalType.voltageAbnormal;
return DataAbnormalType.none;
@@ -611,6 +667,8 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
// ---------- Build ----------
@override
Widget build(BuildContext context) {
final isDark = Theme.of(context).brightness == Brightness.dark;
final double healthPercent = (_designCap > 0)
? (_currentCap / _designCap) * 100
: 0.0;
@@ -619,45 +677,52 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
? '充电中 · ${_getChargeTypeLabel()} · ${_getDisplayPower()}'
: '';
// 🆕 1.7.0 异常检测
final abnormalType = _checkAbnormalType(
_currentCap,
_designCap,
_batteryVolt,
_hasAbnormalDataToday,
);
return AnnotatedRegion<SystemUiOverlayStyle>(
value: const SystemUiOverlayStyle(
statusBarColor: Color.fromRGBO(157, 212, 237, 0.1),
statusBarIconBrightness: Brightness.dark,
statusBarBrightness: Brightness.dark,
value: SystemUiOverlayStyle(
statusBarColor: isDark
? const Color(0xFF1E1E1E)
: const Color.fromRGBO(157, 212, 237, 0.1),
statusBarIconBrightness: isDark ? Brightness.light : Brightness.dark,
statusBarBrightness: isDark ? Brightness.light : Brightness.dark,
),
child: Scaffold(
backgroundColor: Colors.white,
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
appBar: AppBar(
backgroundColor: Colors.white,
backgroundColor: Theme.of(context).appBarTheme.backgroundColor,
elevation: 0,
scrolledUnderElevation: 0,
shadowColor: Colors.transparent,
surfaceTintColor: Colors.transparent,
title: const Text(
title: Text(
'电池健康度',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.w600,
color: Colors.black,
color: isDark ? Colors.white : Colors.black,
),
),
actions: [
IconButton(
icon: const Icon(Icons.refresh),
icon: Icon(
Icons.refresh,
color: isDark ? Colors.white : Colors.black,
),
onPressed: readBatteryCapacity,
tooltip: '刷新数据',
color: Colors.black,
),
PopupMenuButton<String>(
icon: const Icon(Icons.more_vert),
color: Colors.white,
icon: Icon(
Icons.more_vert,
color: isDark ? Colors.white : Colors.black,
),
color: isDark ? const Color(0xFF2C2C2C) : Colors.white,
onSelected: (value) {
switch (value) {
case 'changelog':
@@ -668,24 +733,23 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
),
);
break;
case 'dark_mode':
break;
case 'about':
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const AboutPage(),
),
);
break;
case 'dark_mode':
// 夜间模式切换逻辑(需要全局状态管理)
break;
}
},
itemBuilder: (context) => [
const PopupMenuItem(value: 'changelog', child: Text('更新日志')),
const PopupMenuItem(
value: 'dark_mode',
enabled: false,
child: Text('深色模式(开发中)'),
),
const PopupMenuItem(
value: 'about',
enabled: false,
child: Text('关于(开发中)'),
),
const PopupMenuItem(value: 'dark_mode', child: Text('夜间模式')),
const PopupMenuItem(value: 'about', child: Text('关于')),
],
),
],
@@ -701,22 +765,23 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
children: [
Text(
_deviceModel,
style: const TextStyle(
style: TextStyle(
fontSize: 14,
color: Colors.grey,
color: isDark
? Colors.grey.shade400
: Colors.grey,
fontWeight: FontWeight.w400,
),
),
const SizedBox(height: 4),
// 🟢 BatterySummary(带异常检测)
RepaintBoundary(
child: BatterySummary(
healthPercent: healthPercent,
cycleCount: _cycleCount,
currentCap: _currentCap,
designCap: _designCap,
abnormalType: abnormalType, // 🆕 1.7.0
abnormalType: abnormalType,
),
),
@@ -761,7 +826,6 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
const SizedBox(height: 4),
// 🟢 BatteryInfoPanel(带异常检测)
RepaintBoundary(
child: BatteryInfoPanel(
batteryVolt: _batteryVolt,
@@ -770,7 +834,7 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
minVolt: _minVolt,
maxTemp: _maxTemp,
minTemp: _minTemp,
abnormalType: abnormalType, // 🆕 1.7.0
abnormalType: abnormalType,
),
),
],
@@ -779,19 +843,29 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
),
],
)
: const Center(
: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.battery_unknown, size: 64, color: Colors.grey),
SizedBox(height: 16),
Icon(
Icons.battery_unknown,
size: 64,
color: isDark ? Colors.grey.shade600 : Colors.grey,
),
const SizedBox(height: 16),
Text(
'暂无数据',
style: TextStyle(fontSize: 18, color: Colors.grey),
style: TextStyle(
fontSize: 18,
color: isDark ? Colors.grey.shade400 : Colors.grey,
),
),
Text(
'点击右上角刷新尝试',
style: TextStyle(fontSize: 14, color: Colors.grey),
style: TextStyle(
fontSize: 14,
color: isDark ? Colors.grey.shade500 : Colors.grey,
),
),
],
),