1.9.0-BatteryManager电池充电状态信息引入完成

This commit is contained in:
2026-10-11 17:19:54 +08:00
parent 4829cd1da2
commit 8be4ff7e40
6 changed files with 259 additions and 45 deletions
@@ -0,0 +1,51 @@
package com.lxh.battery_health
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager
class BatteryManagerHelper(private val context: Context) {
fun getBatteryData(): Map<String, Any?> {
val bm = context.getSystemService(Context.BATTERY_SERVICE) as BatteryManager
// 电流(µA)。部分机型充电为负、放电为正,部分相反
val rawCurrentUa = bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_CURRENT_NOW)
// 电压 / 状态从 ACTION_BATTERY_CHANGED 广播里拿(比 BATTERY_PROPERTY_VOLTAGE 兼容性好)
val intent = context.registerReceiver(
null,
IntentFilter(Intent.ACTION_BATTERY_CHANGED)
)
val voltageMv = intent?.getIntExtra(BatteryManager.EXTRA_VOLTAGE, -1) ?: -1
val status = intent?.getIntExtra(BatteryManager.EXTRA_STATUS, -1) ?: -1
val plugged = intent?.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) ?: -1
val temperatureTenth = intent?.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, -1) ?: -1
val isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
status == BatteryManager.BATTERY_STATUS_FULL
// 符号规约:充电为正
val normalizedUa = when {
rawCurrentUa == Int.MIN_VALUE -> 0
isCharging && rawCurrentUa < 0 -> -rawCurrentUa
!isCharging && rawCurrentUa > 0 -> -rawCurrentUa
else -> rawCurrentUa
}
// 功率 W = V × A = (mV / 1000) × (µA / 1_000_000)
val powerW = if (voltageMv > 0 && normalizedUa != 0) {
(voltageMv / 1000.0) * (normalizedUa / 1_000_000.0)
} else 0.0
return mapOf(
"voltageMv" to voltageMv,
"currentUa" to normalizedUa,
"powerW" to powerW,
"temperatureC" to if (temperatureTenth > 0) temperatureTenth / 10.0 else 0.0,
"plugged" to plugged, // 0=未插, 1=AC, 2=USB, 4=WIRELESS
"isCharging" to isCharging,
)
}
}
@@ -255,26 +255,35 @@ private fun clearRefreshRate() {
// 充电状态通道 // 充电状态通道
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHARGE_CHANNEL) MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHARGE_CHANNEL)
.setMethodCallHandler { call, result -> .setMethodCallHandler { call, result ->
if (call.method == "getChargeState") { when (call.method) {
val isCharging = getBatteryStatus() "getChargeState" -> {
val chargeType = if (isCharging) { val isCharging = getBatteryStatus()
val batteryIntent = registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED)) val chargeType = if (isCharging) {
val plug = batteryIntent?.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) ?: -1 val batteryIntent = registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
when (plug) { val plug = batteryIntent?.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) ?: -1
BatteryManager.BATTERY_PLUGGED_AC -> "AC" when (plug) {
BatteryManager.BATTERY_PLUGGED_USB -> "USB" BatteryManager.BATTERY_PLUGGED_AC -> "AC"
BatteryManager.BATTERY_PLUGGED_WIRELESS -> "WIRELESS" BatteryManager.BATTERY_PLUGGED_USB -> "USB"
else -> "UNKNOWN" BatteryManager.BATTERY_PLUGGED_WIRELESS -> "WIRELESS"
} else -> "UNKNOWN"
} else {
"NONE"
} }
result.success(mapOf("isCharging" to isCharging, "chargeType" to chargeType))
} else { } else {
result.notImplemented() "NONE"
}
result.success(mapOf("isCharging" to isCharging, "chargeType" to chargeType))
}
// 🆕 新增分支
"getBatteryManagerData" -> {
try {
result.success(BatteryManagerHelper(applicationContext).getBatteryData())
} catch (e: Exception) {
result.error("BM_ERROR", e.message, null)
} }
} }
else -> result.notImplemented()
}
}
// SAF 通道 // SAF 通道
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, SAF_CHANNEL) MethodChannel(flutterEngine.dartExecutor.binaryMessenger, SAF_CHANNEL)
+38 -10
View File
@@ -5,6 +5,7 @@ import 'package:flutter/services.dart';
import 'package:device_info_plus/device_info_plus.dart'; import 'package:device_info_plus/device_info_plus.dart';
import '../theme/theme_service.dart'; import '../theme/theme_service.dart';
import '../theme/app_colors.dart'; import '../theme/app_colors.dart';
import '../services/battery_manager_service.dart';
// --- 本地导入 --- // --- 本地导入 ---
import '../models/device_matching.dart'; import '../models/device_matching.dart';
@@ -64,6 +65,9 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
int? _wiredLevel; int? _wiredLevel;
bool _isWireless = false; bool _isWireless = false;
DateTime? _chargingStartTime; DateTime? _chargingStartTime;
double _bmPower = 0.0;
double _bmVolt = 0.0;
double _bmCurr = 0.0;
// ---------- SAF 状态 ---------- // ---------- SAF 状态 ----------
final SafStorageService _safService = SafStorageService(); final SafStorageService _safService = SafStorageService();
@@ -80,6 +84,7 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
// 轮询定时器(15秒) // 轮询定时器(15秒)
Timer? _pollTimer; Timer? _pollTimer;
Timer? _bmTimer;
// ---------- 生命周期 ---------- // ---------- 生命周期 ----------
@override @override
@@ -124,7 +129,7 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
readBatteryCapacity(); readBatteryCapacity();
} }
}); });
_startBmPolling(); // ← 调用,不是定义
dev.log('🔵 ========== initState 结束 ==========', name: 'BatteryHealth'); dev.log('🔵 ========== initState 结束 ==========', name: 'BatteryHealth');
} }
@@ -132,6 +137,7 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
void dispose() { void dispose() {
_chargeEventSubscription?.cancel(); _chargeEventSubscription?.cancel();
_pollTimer?.cancel(); _pollTimer?.cancel();
_bmTimer?.cancel();
_dbService.database.then((db) => db.close()).ignore(); _dbService.database.then((db) => db.close()).ignore();
super.dispose(); super.dispose();
} }
@@ -252,6 +258,9 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
_chargeCurr = 0.0; _chargeCurr = 0.0;
_wiredLevel = null; _wiredLevel = null;
_chargingStartTime = null; _chargingStartTime = null;
_bmPower = 0.0; // 🆕
_bmVolt = 0.0; // 🆕
_bmCurr = 0.0; // 🆕
return; return;
} }
@@ -610,18 +619,34 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
} }
} }
// ---------- BM 轮询(1秒) ----------
void _startBmPolling() {
_bmTimer?.cancel();
_bmTimer = Timer.periodic(const Duration(seconds: 1), (_) async {
if (!_isCharging) return;
try {
final d = await BatteryManagerService.fetch();
if (!mounted) return;
setState(() {
_bmPower = d.powerW;
_bmVolt = d.voltageV;
_bmCurr = d.currentA;
});
} catch (e) {
// 静默降级,保留上一次的值
}
});
}
// ---------- 显示辅助 ---------- // ---------- 显示辅助 ----------
String _getDisplayPower() { String _getDisplayPower() {
if (!_isCharging) return ''; if (!_isCharging) return '';
if (_isWireless) { return ChargeUtils.getMainDisplayPowerBm(
return ChargeUtils.getDisplayPowerWireless(_chargePower); _bmPower,
} else { _wiredLevel,
return ChargeUtils.getMainDisplayPowerWired( _chargeType,
_chargePower, _isWireless,
_wiredLevel, );
_chargeType,
);
}
} }
String _getChargeTypeLabel() { String _getChargeTypeLabel() {
@@ -820,6 +845,9 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
volt: _chargeVolt, volt: _chargeVolt,
curr: _chargeCurr, curr: _chargeCurr,
wiredLevel: _wiredLevel, wiredLevel: _wiredLevel,
bmPower: _bmPower, // 🆕
bmVolt: _bmVolt, // 🆕
bmCurr: _bmCurr, // 🆕
), ),
), ),
); );
+57
View File
@@ -0,0 +1,57 @@
import 'package:flutter/services.dart';
/// BatteryManager 实时数据(电池端:电压 / 电流 / 功率)
class BatteryManagerData {
final int voltageMv;
final int currentUa;
final double powerW;
final double temperatureC;
final int plugged; // 0=未插, 1=AC, 2=USB, 4=WIRELESS
final bool isCharging;
const BatteryManagerData({
required this.voltageMv,
required this.currentUa,
required this.powerW,
required this.temperatureC,
required this.plugged,
required this.isCharging,
});
factory BatteryManagerData.empty() => const BatteryManagerData(
voltageMv: 0,
currentUa: 0,
powerW: 0,
temperatureC: 0,
plugged: 0,
isCharging: false,
);
factory BatteryManagerData.fromMap(dynamic raw) {
if (raw is! Map) return BatteryManagerData.empty();
return BatteryManagerData(
voltageMv: (raw['voltageMv'] as num?)?.toInt() ?? 0,
currentUa: (raw['currentUa'] as num?)?.toInt() ?? 0,
powerW: (raw['powerW'] as num?)?.toDouble() ?? 0.0,
temperatureC: (raw['temperatureC'] as num?)?.toDouble() ?? 0.0,
plugged: (raw['plugged'] as num?)?.toInt() ?? 0,
isCharging: raw['isCharging'] as bool? ?? false,
);
}
/// 电压 V
double get voltageV => voltageMv / 1000.0;
/// 电流 A
double get currentA => currentUa / 1_000_000.0;
}
class BatteryManagerService {
static const _channel = MethodChannel('com.lxh.battery_health/charge');
/// 主动拉取一次
static Future<BatteryManagerData> fetch() async {
final result = await _channel.invokeMethod('getBatteryManagerData');
return BatteryManagerData.fromMap(result);
}
}
+32 -2
View File
@@ -70,8 +70,11 @@ class ChargeUtils {
if (power == 0) return "读取中..."; if (power == 0) return "读取中...";
if (wiredLevel != null && ChargeMaps.wiredPower.containsKey(wiredLevel)) { if (wiredLevel != null && ChargeMaps.wiredPower.containsKey(wiredLevel)) {
final requested = ChargeMaps.wiredPower[wiredLevel]!.round(); final requested = ChargeMaps.wiredPower[wiredLevel]!;
return "≈${requested}w/L-$wiredLevel"; final str = requested == requested.roundToDouble()
? requested.toInt().toString()
: requested.toStringAsFixed(1);
return "≈${str}w/L-$wiredLevel";
} }
return "≈${power.round()}w"; return "≈${power.round()}w";
@@ -96,4 +99,31 @@ class ChargeUtils {
if (chargeType == 'SDP') return 'USB'; if (chargeType == 'SDP') return 'USB';
return chargeType; return chargeType;
} }
/// 主界面:BM 电池端功率 + 档位
/// 示例:5.98w / L-7、12.3w、读取中...
static String getMainDisplayPowerBm(
double bmPower,
int? wiredLevel,
String chargeType,
bool isWireless,
) {
if (bmPower <= 0 || bmPower > 100) return '读取中...';
final powerStr = '${bmPower.toStringAsFixed(1)}w';
if (isWireless || chargeType == 'SDP') return powerStr;
// 有线:拼接标定瓦数
if (wiredLevel != null && ChargeMaps.wiredPower.containsKey(wiredLevel)) {
final requested = ChargeMaps.wiredPower[wiredLevel]!;
// 7.5 → "7.5",27 → "27"
final requestedStr = requested == requested.roundToDouble()
? requested.toInt().toString()
: requested.toStringAsFixed(1);
return '$powerStr / ${requestedStr}w';
}
return powerStr;
}
} }
+56 -17
View File
@@ -10,6 +10,9 @@ class ChargeDetailPage extends StatelessWidget {
final double volt; final double volt;
final double curr; final double curr;
final int? wiredLevel; final int? wiredLevel;
final double bmPower;
final double bmVolt;
final double bmCurr;
const ChargeDetailPage({ const ChargeDetailPage({
super.key, super.key,
@@ -19,6 +22,9 @@ class ChargeDetailPage extends StatelessWidget {
required this.volt, required this.volt,
required this.curr, required this.curr,
this.wiredLevel, this.wiredLevel,
this.bmPower = 0,
this.bmVolt = 0,
this.bmCurr = 0,
}); });
String _getProtocolDisplay() { String _getProtocolDisplay() {
@@ -62,18 +68,31 @@ class ChargeDetailPage extends StatelessWidget {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
// ========== BMS 来源 ==========
_buildSectionTitle(context, 'BMS 日志'),
_buildTextRow(context, '协议', _getProtocolDisplay()), _buildTextRow(context, '协议', _getProtocolDisplay()),
const Divider(), const Divider(),
if (!isWireless) ...[ if (!isWireless) ...[
_buildTextRow(context, '标定档位', _getRequestedDisplay()), _buildTextRow(
context,
'标定档位',
_getRequestedDisplay(),
valueFont: 'Flyme',
),
const Divider(), const Divider(),
_buildTextRow(context, '实际功率', _getActualDisplay()), _buildTextRow(
context,
'实际功率',
_getActualDisplay(),
valueFont: 'Flyme',
),
const Divider(), const Divider(),
] else ...[ ] else ...[
_buildTextRow( _buildTextRow(
context, context,
'功率', '功率',
ChargeUtils.getDisplayPowerWireless(power), ChargeUtils.getDisplayPowerWireless(power),
valueFont: 'Flyme',
), ),
const Divider(), const Divider(),
], ],
@@ -81,20 +100,19 @@ class ChargeDetailPage extends StatelessWidget {
const Divider(), const Divider(),
_buildDetailRow(context, '电流', curr, 'A'), _buildDetailRow(context, '电流', curr, 'A'),
const Divider(), const Divider(),
if (isWireless) _buildTextRow(context, '类型', '无线'),
if (!isWireless && wiredLevel == null) const SizedBox(height: 24),
_buildTextRow(context, '档位', '自动'),
// ========== BM 来源 ==========
_buildSectionTitle(context, 'BatteryManager(电池端)'),
_buildDetailRow(context, '电压', bmVolt, 'V'),
const Divider(),
_buildDetailRow(context, '电流', bmCurr, 'A'),
const Divider(),
bmPower > 0
? _buildDetailRow(context, '功率', bmPower, 'W')
: _buildTextRow(context, '功率', '读取中...'),
const Spacer(), const Spacer(),
Center(
child: Text(
'数据来自 BMS 实时日志',
style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 12,
color: c.textTertiary,
),
),
),
], ],
), ),
), ),
@@ -102,7 +120,28 @@ class ChargeDetailPage extends StatelessWidget {
); );
} }
Widget _buildTextRow(BuildContext context, String label, String value) { Widget _buildSectionTitle(BuildContext context, String title) {
final c = context.colors;
return Padding(
padding: const EdgeInsets.only(bottom: 8),
child: Text(
title,
style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 13,
fontWeight: FontWeight.w600,
color: c.textTertiary,
),
),
);
}
Widget _buildTextRow(
BuildContext context,
String label,
String value, {
String valueFont = 'HarmonyOS_Sans_SC',
}) {
final c = context.colors; final c = context.colors;
return Padding( return Padding(
padding: const EdgeInsets.symmetric(vertical: 6.0), padding: const EdgeInsets.symmetric(vertical: 6.0),
@@ -121,7 +160,7 @@ class ChargeDetailPage extends StatelessWidget {
Text( Text(
value, value,
style: TextStyle( style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC', fontFamily: valueFont,
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: c.textPrimary, color: c.textPrimary,