更新日志铺路部分,dev中

This commit is contained in:
2026-07-20 23:16:39 +08:00
parent c8866a740a
commit 0d8c6b081d
6 changed files with 437 additions and 121 deletions
+77
View File
@@ -0,0 +1,77 @@
## [1.5.3] - 2026-07-19
- 新增:机型匹配优化
### ✅ 已确认完美支持
- 魅族 20 / 20 Pro / 20 INFINITY(基于remaining_cap + status=full联合判断)
- 魅族 21 / 21 Pro(直接读取learned_cap字段信息)
### ⚠️ 部分支持(测试中)
- 魅族 18 / 18s / 18 Pro / 18s Pro(日志存在,字段解析适配中)
- 魅族 16s(需回退原厂固件验证)
### ❌ 确认不支持
- 魅族 21 Note(星纪团队固件,日志格式乱码无法识别,导致异常)
- 魅族 22(系统未生成 `mbattery_charger` 目录)
### 📌 说明
- 适配状态仅针对 BMS 日志可读性,与手机硬件本身无关
- 部分旧机型(如 16 系)若无日志生成,则无法使用本工具
- 后续若发现其他机型可用,将持续补充
----
## [1.5.2] - 2026-07-14
### 新增
- 浪漫雅圆字体,工程内文件为FlymeFont.ttf
### 🎨 UI 优化
- **字体体系重构**:数字全局替换为 FlymeFont(浪漫雅圆),中文保留 HarmonyOS Sans SC
- 健康度百分比、容量数值、循环次数、电压/温度读数均使用 FlymeFont
- 中文/单位/符号保持 HarmonyOS Sans SC,确保阅读舒适度
- **温标符号修正**:温度显示由 `°C`(度符号 + C)改为专用字符 `℃`U+2103),与 FlymeFont 原生适配
- **容量行视觉权重优化**
- 当前容量与设计容量数字使用 FlymeFont(字号 20,字重 300
- `mAh` 单位改用 HarmonyOS Sans SC(字号 16,字重 500),与 `cycles` 保持一致
- 整行颜色统一为 `rgb(95, 95, 95)`,弱化视觉重量,突出健康度大数字
- **电压/温度极值显示优化**:数值与单位统一使用 FlymeFont,箭头符号保留 HarmonyOS
### 🐛 修复
- 修复 FlymeFont 数字 1 在部分场景下显示为红色的问题(字体内部颜色预设)
- 修复容量行 `designCap`(设计容量)未正确应用 FlymeFont 的问题
- 修复部分 `TextStyle``color``foreground` 混用导致的渲染断言错误
- 统一主界面与二级页面状态栏样式
### 📊 影响范围
- `battery_summary.dart`:健康度/循环/容量显示样式重构
- `battery_info_panel.dart`:电压/温度显示字体统一
- `charge_detail_page.dart`:数值字体统一
- `trend_chart.dart`:坐标轴数字字体统一
### 📌 备注
- 本次更新纯 UI 优化,不影响数据读取与计算逻辑
----
## [1.5.0] - 2026-07-09
### 🐛 修复
- 修复有线充电功率计算单位错误(µV × µA 计算时少除 1000,导致功率放大 1000 倍,属于潜藏bug,外面看不出问题)
- 修复魅族 21 系列 `learned_cap` 因受 `status=Full` 限制导致容量未及时更新的问题
- `learned_cap` 优先级提升:在所有状态行中优先匹配,不再局限于 `status=Full`
### ⚡ 优化
- 有线充电功率显示重构:
- 主界面:`≈{实际功率}w/{请求功率}w`(例:`≈30w/65w`
- 二级页面:拆分为“请求档位”(`≈80w/L-10`)和“实际功率”(`≈30w`)两行
- USB 慢充保持 `≈7.5w` 不变
- UI 统一:
- 统一主界面与二级页面状态栏背景色
- 统一主界面与二级页面 AppBar 背景色为 `Colors.white`
- 状态栏图标保持深色(`Brightness.dark`
## 更早期日志请移步至项目Git Release页面查看
+93 -121
View File
@@ -4,21 +4,22 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:device_info_plus/device_info_plus.dart';
// 检查这些导入是否正确
import '../models/device_matching.dart'; // ✅ 存在
import '../models/saf_permission_state.dart'; // ✅ 存在
// --- 本地导入 ---
import '../models/device_matching.dart';
import '../models/saf_permission_state.dart';
import '../services/database_service.dart';
import '../services/battery_data_service.dart';
import '../services/saf_storage_service.dart'; // ✅ 存在
import '../logic/charge_parser.dart'; // ✅ 存在
import '../logic/capacity_extractor.dart'; // ✅ 存在(注意:不是 utils/)
import '../utils/time_utils.dart'; // ✅ 存在
import '../utils/charge_utils.dart'; // ✅ 存在
import '../widgets/trend_chart.dart'; // ⚠️ 需要确认文件内容完整
import '../widgets/battery_summary.dart'; // ⚠️ 需要确认文件内容完整
import '../widgets/battery_info_panel.dart'; // ⚠️ 需要确认文件内容完整
import '../widgets/charge_entry.dart'; // ⚠️ 需要确认文件内容完整
import '../widgets/charge_detail_page.dart'; // ✅ 存在
import '../services/saf_storage_service.dart';
import '../logic/charge_parser.dart';
import '../logic/capacity_extractor.dart';
import '../utils/time_utils.dart';
import '../utils/charge_utils.dart';
import '../widgets/battery_summary.dart';
import '../widgets/battery_info_panel.dart';
import '../widgets/charge_entry.dart';
import '../widgets/charge_detail_page.dart';
import '../widgets/battery_data_layout.dart'; // 🆕 1.6.0 UI 拆分
import '../pages/changelog_page.dart'; // 🆕 1.6.0 更新日志
// --- 潘通色定义 ---
const Color kHealthGreen = Color(0xFF00B140);
@@ -613,7 +614,6 @@ class _BatteryHealthPageState extends State<BatteryHealthPage>
if (_isWireless) {
return ChargeUtils.getDisplayPowerWireless(_chargePower);
} else {
// 🟢 1.5.0 改用新格式:实际/标定
return ChargeUtils.getMainDisplayPowerWired(
_chargePower,
_wiredLevel,
@@ -629,9 +629,17 @@ class _BatteryHealthPageState extends State<BatteryHealthPage>
// ---------- Build ----------
@override
Widget build(BuildContext context) {
// 计算健康度百分比(显式转为 double)
final double healthPercent = (_designCap > 0)
? (_currentCap / _designCap) * 100
: 0.0;
final String chargeSummary = _isCharging
? '充电中 · ${_getChargeTypeLabel()} · ${_getDisplayPower()}'
: '';
return AnnotatedRegion<SystemUiOverlayStyle>(
value: const SystemUiOverlayStyle(
// 🟢 1.5.0 统一状态栏颜色
statusBarColor: Color.fromRGBO(157, 212, 237, 0.1),
statusBarIconBrightness: Brightness.dark,
statusBarBrightness: Brightness.dark,
@@ -653,120 +661,84 @@ class _BatteryHealthPageState extends State<BatteryHealthPage>
),
),
actions: [
// 刷新按钮(左移)
IconButton(
icon: const Icon(Icons.refresh),
onPressed: readBatteryCapacity,
tooltip: '刷新数据',
color: Colors.black,
),
// 三点菜单
PopupMenuButton<String>(
icon: const Icon(Icons.more_vert),
color: Colors.white,
onSelected: (value) {
switch (value) {
case 'changelog':
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const ChangelogPage(),
),
);
break;
case 'dark_mode':
// 1.7 再实现
break;
case 'about':
// 1.7 再实现
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('关于(开发中)'),
),
],
),
],
),
body: _hasData ? _buildDataLayout() : _buildEmptyState(),
),
);
}
Widget _buildEmptyState() {
return const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.battery_unknown, size: 64, color: Colors.grey),
SizedBox(height: 16),
Text('暂无数据', style: TextStyle(fontSize: 18, color: Colors.grey)),
Text('点击右上角刷新尝试', style: TextStyle(fontSize: 14, color: Colors.grey)),
],
),
);
}
Widget _buildDataLayout() {
final healthPercent = (_designCap > 0)
? (_currentCap / _designCap) * 100
: 0;
String chargeSummary = '';
if (_isCharging) {
chargeSummary = '充电中 · ${_getChargeTypeLabel()} · ${_getDisplayPower()}';
}
return Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_deviceModel,
style: const TextStyle(
fontSize: 14,
color: Colors.grey,
fontWeight: FontWeight.w400,
),
),
const SizedBox(height: 4),
BatterySummary(
healthPercent: healthPercent.toDouble(), // 显式转换
cycleCount: _cycleCount,
currentCap: _currentCap,
designCap: _designCap,
),
const SizedBox(height: 24),
TrendChart(data: _trendData, designCap: _designCap),
const SizedBox(height: 8),
AnimatedBuilder(
animation: _chargeAnimationController,
builder: (context, child) {
final entryHeight = 28 * _entryFadeIn.value;
final chargeEntry = Opacity(
opacity: _entryFadeIn.value,
child: ChargeEntry(
summary: chargeSummary,
isCharging: _isCharging,
onTap: () {
if (_isCharging) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ChargeDetailPage(
chargeType: _chargeType,
isWireless: _isWireless,
power: _chargePower,
volt: _chargeVolt,
curr: _chargeCurr,
wiredLevel: _wiredLevel,
),
),
);
}
},
),
);
return Column(
children: [
SizedBox(height: entryHeight, child: chargeEntry),
Transform.translate(
offset: Offset(0, _bottomSlide.value),
child: BatteryInfoPanel(
batteryVolt: _batteryVolt,
batteryTemp: _batteryTemp,
maxVolt: _maxVolt,
minVolt: _minVolt,
maxTemp: _maxTemp,
minTemp: _minTemp,
),
),
],
);
},
),
const Spacer(),
],
body: _hasData
? AnimatedBuilder(
animation: _chargeAnimationController,
builder: (context, child) {
// 🟢 修复:传入 healthPercent 为 double,移除未定义的动画参数
// 动画在 BatteryDataLayout 内部不处理,由父级 AnimatedBuilder 控制
return BatteryDataLayout(
deviceModel: _deviceModel,
healthPercent: healthPercent,
cycleCount: _cycleCount,
currentCap: _currentCap,
designCap: _designCap,
trendData: _trendData,
isCharging: _isCharging,
chargeSummary: chargeSummary,
batteryVolt: _batteryVolt,
batteryTemp: _batteryTemp,
maxVolt: _maxVolt,
minVolt: _minVolt,
maxTemp: _maxTemp,
minTemp: _minTemp,
chargeType: _chargeType,
isWireless: _isWireless,
chargePower: _chargePower,
chargeVolt: _chargeVolt,
chargeCurr: _chargeCurr,
wiredLevel: _wiredLevel,
// 🟢 动画参数由父级 AnimatedBuilder 控制,
// BatteryDataLayout 不再需要透传 animation 参数
);
},
)
: const EmptyStateLayout(),
),
);
}
+99
View File
@@ -0,0 +1,99 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart' show rootBundle;
import 'package:flutter_markdown/flutter_markdown.dart';
class ChangelogPage extends StatefulWidget {
const ChangelogPage({super.key});
@override
State<ChangelogPage> createState() => _ChangelogPageState();
}
class _ChangelogPageState extends State<ChangelogPage> {
String _markdownContent = '';
bool _isLoading = true;
String? _error;
@override
void initState() {
super.initState();
_loadChangelog();
}
Future<void> _loadChangelog() async {
try {
final content = await rootBundle.loadString('assets/CHANGELOG.md');
setState(() {
_markdownContent = content;
_isLoading = false;
});
} catch (e) {
setState(() {
_error = '无法加载更新日志: $e';
_isLoading = false;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: const Text(
'更新日志',
style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
color: Colors.black,
),
),
backgroundColor: Colors.white,
elevation: 0,
foregroundColor: Colors.black,
iconTheme: const IconThemeData(color: Colors.black),
),
body: _isLoading
? const Center(child: CircularProgressIndicator())
: _error != null
? Center(
child: Text(
_error!,
style: const TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
color: Colors.grey,
),
),
)
: SingleChildScrollView(
padding: const EdgeInsets.all(20),
child: Markdown(
data: _markdownContent,
styleSheet: MarkdownStyleSheet(
h1: const TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 24,
fontWeight: FontWeight.w700,
color: Colors.black,
),
h2: const TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 20,
fontWeight: FontWeight.w600,
color: Colors.black87,
),
p: const TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 16,
color: Colors.black87,
),
listBullet: const TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 16,
color: Colors.black87,
),
),
),
),
);
}
}
+151
View File
@@ -0,0 +1,151 @@
import 'package:flutter/material.dart';
import '../widgets/trend_chart.dart';
import '../widgets/battery_summary.dart';
import '../widgets/battery_info_panel.dart';
import '../widgets/charge_entry.dart';
import '../widgets/charge_detail_page.dart';
/// 电池数据布局(含数据展示、趋势图、充电状态、电压温度面板)
class BatteryDataLayout extends StatelessWidget {
final String deviceModel;
final double healthPercent;
final int cycleCount;
final double currentCap;
final int designCap;
final List<Map<String, dynamic>> trendData;
final bool isCharging;
final String chargeSummary;
final double batteryVolt;
final double batteryTemp;
final double maxVolt;
final double minVolt;
final double maxTemp;
final double minTemp;
final String chargeType;
final bool isWireless;
final double chargePower;
final double chargeVolt;
final double chargeCurr;
final int? wiredLevel;
const BatteryDataLayout({
super.key,
required this.deviceModel,
required this.healthPercent,
required this.cycleCount,
required this.currentCap,
required this.designCap,
required this.trendData,
required this.isCharging,
required this.chargeSummary,
required this.batteryVolt,
required this.batteryTemp,
required this.maxVolt,
required this.minVolt,
required this.maxTemp,
required this.minTemp,
required this.chargeType,
required this.isWireless,
required this.chargePower,
required this.chargeVolt,
required this.chargeCurr,
this.wiredLevel,
});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
deviceModel,
style: const TextStyle(
fontSize: 14,
color: Colors.grey,
fontWeight: FontWeight.w400,
),
),
const SizedBox(height: 4),
BatterySummary(
healthPercent: healthPercent,
cycleCount: cycleCount,
currentCap: currentCap,
designCap: designCap,
),
const SizedBox(height: 24),
TrendChart(data: trendData, designCap: designCap),
const SizedBox(height: 8),
// 充电状态 + 信息面板(带进场动画)
_buildChargeAndInfoPanel(context),
const Spacer(),
],
),
);
}
Widget _buildChargeAndInfoPanel(BuildContext context) {
return Column(
children: [
// 充电状态入口(保持原有的透明度/位移动画由父级控制)
ChargeEntry(
summary: chargeSummary,
isCharging: isCharging,
onTap: () {
if (isCharging) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ChargeDetailPage(
chargeType: chargeType,
isWireless: isWireless,
power: chargePower,
volt: chargeVolt,
curr: chargeCurr,
wiredLevel: wiredLevel,
),
),
);
}
},
),
const SizedBox(height: 4),
BatteryInfoPanel(
batteryVolt: batteryVolt,
batteryTemp: batteryTemp,
maxVolt: maxVolt,
minVolt: minVolt,
maxTemp: maxTemp,
minTemp: minTemp,
),
],
);
}
}
/// 空状态布局(无数据时显示)
class EmptyStateLayout extends StatelessWidget {
const EmptyStateLayout({super.key});
@override
Widget build(BuildContext context) {
return const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.battery_unknown, size: 64, color: Colors.grey),
SizedBox(height: 16),
Text('暂无数据', style: TextStyle(fontSize: 18, color: Colors.grey)),
Text('点击右上角刷新尝试', style: TextStyle(fontSize: 14, color: Colors.grey)),
],
),
);
}
}
+16
View File
@@ -126,6 +126,14 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.0.0"
flutter_markdown:
dependency: "direct main"
description:
name: flutter_markdown
sha256: "08fb8315236099ff8e90cb87bb2b935e0a724a3af1623000a9cec930468e0f27"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.7.7+1"
flutter_svg:
dependency: "direct main"
description:
@@ -224,6 +232,14 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.3.0"
markdown:
dependency: transitive
description:
name: markdown
sha256: ee85086ad7698b42522c6ad42fe195f1b9898e4d974a1af4576c1a3a176cada9
url: "https://pub.flutter-io.cn"
source: hosted
version: "7.3.1"
matcher:
dependency: transitive
description:
+1
View File
@@ -38,6 +38,7 @@ dependencies:
path: ^1.9.0
flutter_svg: ^2.0.10+1
shared_preferences: ^2.2.0
flutter_markdown: ^0.7.0
# The following adds the Cupertino Icons font to your application.