1.9.0-BatteryManager电池充电状态信息引入完成
This commit is contained in:
@@ -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)
|
||||
.setMethodCallHandler { call, result ->
|
||||
if (call.method == "getChargeState") {
|
||||
val isCharging = getBatteryStatus()
|
||||
val chargeType = if (isCharging) {
|
||||
val batteryIntent = registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
|
||||
val plug = batteryIntent?.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) ?: -1
|
||||
when (plug) {
|
||||
BatteryManager.BATTERY_PLUGGED_AC -> "AC"
|
||||
BatteryManager.BATTERY_PLUGGED_USB -> "USB"
|
||||
BatteryManager.BATTERY_PLUGGED_WIRELESS -> "WIRELESS"
|
||||
else -> "UNKNOWN"
|
||||
}
|
||||
} else {
|
||||
"NONE"
|
||||
.setMethodCallHandler { call, result ->
|
||||
when (call.method) {
|
||||
"getChargeState" -> {
|
||||
val isCharging = getBatteryStatus()
|
||||
val chargeType = if (isCharging) {
|
||||
val batteryIntent = registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
|
||||
val plug = batteryIntent?.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) ?: -1
|
||||
when (plug) {
|
||||
BatteryManager.BATTERY_PLUGGED_AC -> "AC"
|
||||
BatteryManager.BATTERY_PLUGGED_USB -> "USB"
|
||||
BatteryManager.BATTERY_PLUGGED_WIRELESS -> "WIRELESS"
|
||||
else -> "UNKNOWN"
|
||||
}
|
||||
result.success(mapOf("isCharging" to isCharging, "chargeType" to chargeType))
|
||||
} 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 通道
|
||||
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, SAF_CHANNEL)
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:flutter/services.dart';
|
||||
import 'package:device_info_plus/device_info_plus.dart';
|
||||
import '../theme/theme_service.dart';
|
||||
import '../theme/app_colors.dart';
|
||||
import '../services/battery_manager_service.dart';
|
||||
|
||||
// --- 本地导入 ---
|
||||
import '../models/device_matching.dart';
|
||||
@@ -64,6 +65,9 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
|
||||
int? _wiredLevel;
|
||||
bool _isWireless = false;
|
||||
DateTime? _chargingStartTime;
|
||||
double _bmPower = 0.0;
|
||||
double _bmVolt = 0.0;
|
||||
double _bmCurr = 0.0;
|
||||
|
||||
// ---------- SAF 状态 ----------
|
||||
final SafStorageService _safService = SafStorageService();
|
||||
@@ -80,6 +84,7 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
|
||||
|
||||
// 轮询定时器(15秒)
|
||||
Timer? _pollTimer;
|
||||
Timer? _bmTimer;
|
||||
|
||||
// ---------- 生命周期 ----------
|
||||
@override
|
||||
@@ -124,7 +129,7 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
|
||||
readBatteryCapacity();
|
||||
}
|
||||
});
|
||||
|
||||
_startBmPolling(); // ← 调用,不是定义
|
||||
dev.log('🔵 ========== initState 结束 ==========', name: 'BatteryHealth');
|
||||
}
|
||||
|
||||
@@ -132,6 +137,7 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
|
||||
void dispose() {
|
||||
_chargeEventSubscription?.cancel();
|
||||
_pollTimer?.cancel();
|
||||
_bmTimer?.cancel();
|
||||
_dbService.database.then((db) => db.close()).ignore();
|
||||
super.dispose();
|
||||
}
|
||||
@@ -252,6 +258,9 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
|
||||
_chargeCurr = 0.0;
|
||||
_wiredLevel = null;
|
||||
_chargingStartTime = null;
|
||||
_bmPower = 0.0; // 🆕
|
||||
_bmVolt = 0.0; // 🆕
|
||||
_bmCurr = 0.0; // 🆕
|
||||
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() {
|
||||
if (!_isCharging) return '';
|
||||
if (_isWireless) {
|
||||
return ChargeUtils.getDisplayPowerWireless(_chargePower);
|
||||
} else {
|
||||
return ChargeUtils.getMainDisplayPowerWired(
|
||||
_chargePower,
|
||||
_wiredLevel,
|
||||
_chargeType,
|
||||
);
|
||||
}
|
||||
return ChargeUtils.getMainDisplayPowerBm(
|
||||
_bmPower,
|
||||
_wiredLevel,
|
||||
_chargeType,
|
||||
_isWireless,
|
||||
);
|
||||
}
|
||||
|
||||
String _getChargeTypeLabel() {
|
||||
@@ -820,6 +845,9 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
|
||||
volt: _chargeVolt,
|
||||
curr: _chargeCurr,
|
||||
wiredLevel: _wiredLevel,
|
||||
bmPower: _bmPower, // 🆕
|
||||
bmVolt: _bmVolt, // 🆕
|
||||
bmCurr: _bmCurr, // 🆕
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -70,8 +70,11 @@ class ChargeUtils {
|
||||
if (power == 0) return "读取中...";
|
||||
|
||||
if (wiredLevel != null && ChargeMaps.wiredPower.containsKey(wiredLevel)) {
|
||||
final requested = ChargeMaps.wiredPower[wiredLevel]!.round();
|
||||
return "≈${requested}w/L-$wiredLevel";
|
||||
final requested = ChargeMaps.wiredPower[wiredLevel]!;
|
||||
final str = requested == requested.roundToDouble()
|
||||
? requested.toInt().toString()
|
||||
: requested.toStringAsFixed(1);
|
||||
return "≈${str}w/L-$wiredLevel";
|
||||
}
|
||||
|
||||
return "≈${power.round()}w";
|
||||
@@ -96,4 +99,31 @@ class ChargeUtils {
|
||||
if (chargeType == 'SDP') return 'USB';
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,9 @@ class ChargeDetailPage extends StatelessWidget {
|
||||
final double volt;
|
||||
final double curr;
|
||||
final int? wiredLevel;
|
||||
final double bmPower;
|
||||
final double bmVolt;
|
||||
final double bmCurr;
|
||||
|
||||
const ChargeDetailPage({
|
||||
super.key,
|
||||
@@ -19,6 +22,9 @@ class ChargeDetailPage extends StatelessWidget {
|
||||
required this.volt,
|
||||
required this.curr,
|
||||
this.wiredLevel,
|
||||
this.bmPower = 0,
|
||||
this.bmVolt = 0,
|
||||
this.bmCurr = 0,
|
||||
});
|
||||
|
||||
String _getProtocolDisplay() {
|
||||
@@ -62,18 +68,31 @@ class ChargeDetailPage extends StatelessWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// ========== BMS 来源 ==========
|
||||
_buildSectionTitle(context, 'BMS 日志'),
|
||||
_buildTextRow(context, '协议', _getProtocolDisplay()),
|
||||
const Divider(),
|
||||
if (!isWireless) ...[
|
||||
_buildTextRow(context, '标定档位', _getRequestedDisplay()),
|
||||
_buildTextRow(
|
||||
context,
|
||||
'标定档位',
|
||||
_getRequestedDisplay(),
|
||||
valueFont: 'Flyme',
|
||||
),
|
||||
const Divider(),
|
||||
_buildTextRow(context, '实际功率', _getActualDisplay()),
|
||||
_buildTextRow(
|
||||
context,
|
||||
'实际功率',
|
||||
_getActualDisplay(),
|
||||
valueFont: 'Flyme',
|
||||
),
|
||||
const Divider(),
|
||||
] else ...[
|
||||
_buildTextRow(
|
||||
context,
|
||||
'功率',
|
||||
ChargeUtils.getDisplayPowerWireless(power),
|
||||
valueFont: 'Flyme',
|
||||
),
|
||||
const Divider(),
|
||||
],
|
||||
@@ -81,20 +100,19 @@ class ChargeDetailPage extends StatelessWidget {
|
||||
const Divider(),
|
||||
_buildDetailRow(context, '电流', curr, 'A'),
|
||||
const Divider(),
|
||||
if (isWireless) _buildTextRow(context, '类型', '无线'),
|
||||
if (!isWireless && wiredLevel == null)
|
||||
_buildTextRow(context, '档位', '自动'),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// ========== 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(),
|
||||
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;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 6.0),
|
||||
@@ -121,7 +160,7 @@ class ChargeDetailPage extends StatelessWidget {
|
||||
Text(
|
||||
value,
|
||||
style: TextStyle(
|
||||
fontFamily: 'HarmonyOS_Sans_SC',
|
||||
fontFamily: valueFont,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: c.textPrimary,
|
||||
|
||||
Reference in New Issue
Block a user