27 Commits
Author SHA1 Message Date
lxh2875931338 125fb969e4 部分字体补充统一、日志更新 2026-10-11 17:42:12 +08:00
lxh2875931338 8be4ff7e40 1.9.0-BatteryManager电池充电状态信息引入完成 2026-10-11 17:19:54 +08:00
lxh2875931338 4829cd1da2 小调整,去掉部分冗余无用句 2026-10-11 16:08:45 +08:00
lxh2875931338 7989495521 深色模式代码第二轮补充 2026-10-11 16:03:06 +08:00
lxh2875931338 1d033670f7 新增夜间模式相关代码 2026-10-11 15:44:44 +08:00
lxh2875931338 2512f88f30 更新日志补充changelog 2026-09-16 22:54:42 +08:00
lxh2875931338 1f94114d26 增加电池temp_avg参数,UI端与数据库端同步实现更新 2026-09-16 22:32:46 +08:00
lxh2875931338 75e9e85848 准备更新temp_avg相关字段能力 2026-09-16 20:47:19 +08:00
lxh2875931338 68e7be98ed 1.7.0确认更新、路线进度更迭优化 2026-07-31 21:18:57 +08:00
lxh2875931338 7c5bcd22f0 关于页确定完成设置 2026-07-31 21:08:23 +08:00
lxh2875931338 a8a96c1d38 移除本项目的跨平台开发工具,并更新部分git排除信息 2026-07-31 20:47:54 +08:00
lxh2875931338 8f635e65a3 更新项目targetSDK至36 2026-07-31 20:42:26 +08:00
lxh2875931338 f83969ed9c 准备测试关于页 2026-07-31 20:39:07 +08:00
lxh2875931338 636873e449 确认回退到此版本进行重新整合开发 2026-07-31 00:32:44 +08:00
lxh2875931338 dac9529f96 数据入库增加校验再记录 2026-07-30 22:00:06 +08:00
lxh2875931338 c8717e5e1f 增加作弊数据检测 2026-07-30 21:57:08 +08:00
lxh2875931338 c85a523ec0 机型匹配情况 2026-07-21 19:21:05 +08:00
lxh2875931338 067f0d17e8 机型匹配情况说明 2026-07-21 19:19:50 +08:00
lxh2875931338 a63be9f675 20inf最低支持版本说明 2026-07-21 19:15:43 +08:00
lxh2875931338 86c38a97f5 增加机型匹配信息与适配说明 2026-07-21 19:13:27 +08:00
lxh2875931338 2026e55fde readme机型匹配列表更新 2026-07-21 18:57:35 +08:00
lxh2875931338 286162d41b readme机型匹配列表更新 2026-07-21 18:56:49 +08:00
lxh2875931338 4006684466 Merge branch 'main' of http://192.168.3.16:3800/lxh2875931338/MEIZU-Battery-Healthy 2026-07-21 18:54:50 +08:00
lxh2875931338 e5856296d4 readme机型匹配列表更新 2026-07-21 18:54:19 +08:00
lxh2875931338 e049cab9ad readme机型匹配列表更新 2026-07-21 18:49:27 +08:00
lxh2875931338 2b3d9471cd 补充版本号更新:1.6.0-Release 2026-07-21 18:39:26 +08:00
lxh2875931338 76fd0417e7 紧急补充:魅族21note字段更新 2026-07-21 00:09:47 +08:00
138 changed files with 2284 additions and 4714 deletions
+1
View File
@@ -33,6 +33,7 @@ migrate_working_dir/
/build/
/coverage/
# Symbolication related
app.*.symbols
-4
View File
@@ -1,4 +0,0 @@
{
"cmake.ignoreCMakeListsMissing": true,
"gitlens.codeLens.enabled": false
}
+33 -6
View File
@@ -26,8 +26,9 @@
| :--- | :--- | :--- |
| 1.5.0 | 功率计算优化 | ✅ 已发布 |
| 1.5.2 | 部分场景字体替换 | ✅ 已发布 |
| 1.6.0 | 更新日志独立页面 | 📋近期开发 |
| 1.7.0 | 适配夜间模式 | 📋近期开发 |
| 1.6.0 | 更新日志独立页面 | ✅ 已发布 |
| 1.7.0 | 关于页与紧急发布log文件数据反作弊能力 | ✅ 已发布 |
| 1.8.0 | 适配夜间模式 | 📋近期开发 |
| 2.0.0 | 数据看板月份显示扩展等系列更新 | 📋 远景 |
| 3.0.0 | 搭配PC上位机记录充电信息等 | 📋 远景 |
---
@@ -40,10 +41,36 @@
---
## 机型匹配情况
目前据群友反馈已知:
- 魅族22无mbattery_charger目录生成,无法实时计算相应百分比,因此已在最近版本去除相关匹配信息
- 魅族18在后续的版本中关闭了mbattery_log生成,疑似无效,老版本可能支持
- 最完美的支持版本至少可覆盖魅族20/21系机型(20inf已确认,机型匹配ID已更新)
| 机型 | 系统版本 | 容量来源 | 设计容量 | 状态 |
|---|---|---|---|---|
| 魅族 22 | Flyme 12.x | 系统未生成 `mbattery_charger` 目录 | 5510 mAh | ❌ 不支持 |
| 魅族 21 | Flyme 12.x | `learned_cap` | 4800 mAh | ✅ 与 21 Pro 同步获得支持 |
| 魅族 21 Pro | Flyme 12.x | `learned_cap` | 5050 mAh | ✅ 初始支持 |
| 魅族 21 Note | Flyme 12.x | `learned_cap` | 5500 mAh | ✅ 1.6.0 新增支持 |
| 魅族 21 Note | Flyme 10 / 11 | 日志格式异常 | — | ❌ 不支持 |
| 魅族 20 | Flyme 12.x | `remaining_cap` + `status=Full` | 4700 mAh | ✅ 1.4.0 首次支持 |
| 魅族 20 Pro | Flyme 12.x | `remaining_cap` + `status=Full` | 5000 mAh | ✅ 1.4.0 首次支持 |
| 魅族 20 INFINITY | Flyme 10.x | `remaining_cap` + `status=Full` | 4800 mAh | ✅ 1.5.3 字段匹配 |
| 魅族 18 / 18s / 18 Pro / 18s Pro / 18X | Flyme 9 | 日志停止生成 | 4000 / 4500 mAh | ❌ 无适配条件 |
| 魅族 18 / 18s / 18 Pro / 18s Pro / 18X | Flyme 10 | 不再主动适配 | 4000 / 4500 mAh | ⚠️ 实验性支持 |
| 魅族 17 / 17Pro | Flyme 8 / 9 / 10 | 等待群友提供字段信息 | 4500 / 4500mAh | ⚠️ 待验证 |
| 魅族 16th / 16th Plus / 16X / 16s / 16sPro / 16Xs / 16T | Flyme 7 / 8 / 9 | 需回退原厂固件验证 | - | ⚠️ 若新版本无日志,则变为实验性支持,不主动适配 |
### 状态说明
| 状态 | 含义 |
|---|---|
| ✅ 完美支持 | 已通过实机验证,所有字段完整解析 |
| ⚠️ 实验性支持 | 理论上可支持,但需要用户协助验证 |
| ⚠️ 待验证 | 尚未收到实机日志反馈,待确认 |
| ❌ 不支持 | 系统未生成 BMS 日志,或日志格式无法解析 |
### 特别说明
- **Flyme 版本差异**:同一机型在不同 Flyme 版本下日志格式可能不同。例如 21 Note 在 Flyme 12.x 下可正常读取 `learned_cap`,但在 Flyme 10/11 下日志格式异常。
- **日志停止生成**:部分机型在升级到新系统后(如 17/18 系升级到 Flyme 10),系统不再向 `mbattery_charger` 目录写入新日志,文件夹虽存在但停止更新。
- **适配策略**:优先支持日志格式稳定的 Flyme 12.x 机型,旧版本系统暂不主动适配,若有用户长期停留在旧版本并愿意协助测试,可酌情增加支持。
---
+5
View File
@@ -7,6 +7,11 @@
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
analyzer:
exclude:
- build/**
- android/**
- web/**
include: package:flutter_lints/flutter.yaml
linter:
+8 -7
View File
@@ -1,12 +1,11 @@
plugins {
id("com.android.application")
id("kotlin-android")
id("dev.flutter.flutter-gradle-plugin")
}
android {
namespace = "com.lxh.battery_health"
compileSdk = 34
compileSdk = 36
ndkVersion = flutter.ndkVersion
compileOptions {
@@ -14,14 +13,10 @@ android {
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
}
defaultConfig {
applicationId = "com.lxh.battery_health"
minSdk = flutter.minSdkVersion
targetSdk = 34
targetSdk = 36
versionCode = flutter.versionCode
versionName = flutter.versionName
multiDexEnabled = true
@@ -64,6 +59,12 @@ android {
}
}
kotlin { // ← 新增块
compilerOptions {
jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17
}
}
flutter {
source = "../.."
}
-44
View File
@@ -1,44 +0,0 @@
plugins {
id("com.android.application")
id("kotlin-android")
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id("dev.flutter.flutter-gradle-plugin")
}
android {
namespace = "com.lxh.battery_health"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_11.toString()
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "com.lxh.battery_health"
// You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = flutter.minSdkVersion
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode
versionName = flutter.versionName
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig = signingConfigs.getByName("debug")
}
}
}
flutter {
source = "../.."
}
-73
View File
@@ -1,73 +0,0 @@
plugins {
id("com.android.application")
id("kotlin-android")
id("dev.flutter.flutter-gradle-plugin")
}
android {
namespace = "com.lxh.battery_health"
compileSdk = 34
ndkVersion = flutter.ndkVersion
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
}
defaultConfig {
applicationId = "com.lxh.battery_health"
minSdk = flutter.minSdkVersion
targetSdk = 34
versionCode = flutter.versionCode
versionName = flutter.versionName
multiDexEnabled = true
}
signingConfigs {
// 如果没有正式签名,可以用 debug 签名代替(测试用)
// 正式发布时请替换为自己的密钥
getByName("debug") {
// debug 签名已经存在,无需额外配置
}
create("release") {
// 如果有正式签名,在这里配置
// keyAlias = "..."
// keyPassword = "..."
// storeFile = file("...")
// storePassword = "..."
}
}
buildTypes {
debug {
// 🟢 关键:debug 版本包名加 .dev 后缀
applicationIdSuffix = ".dev"
signingConfig = signingConfigs.getByName("debug")
isDebuggable = true
isMinifyEnabled = false
isShrinkResources = false
}
release {
// release 版本不加后缀,保持原包名
signingConfig = signingConfigs.getByName("debug") // 若没有正式签名,先用 debug 签名
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
}
flutter {
source = "../.."
}
dependencies {
// 无需额外添加 Activity KTX,因为已改用 startActivityForResult
}
+79
View File
@@ -0,0 +1,79 @@
# ============ Flutter 核心 ============
-keep class io.flutter.** { *; }
-keep class io.flutter.plugins.** { *; }
-keep class io.flutter.plugin.** { *; }
-keep class io.flutter.util.** { *; }
-keep class io.flutter.view.** { *; }
-keep class io.flutter.embedding.** { *; }
# 保留 Flutter 的 JNI 调用类
-keep class io.flutter.embedding.engine.FlutterJNI { *; }
-dontwarn io.flutter.embedding.**
# ============ Kotlin ============
-keep class kotlin.** { *; }
-keep class kotlin.Metadata { *; }
-dontwarn kotlin.**
-keepclassmembers class **$WhenMappings {
<fields>;
}
-keepclassmembers class kotlin.Metadata {
public <methods>;
}
# ============ sqflite / SQLite ============
-keep class com.tekartik.sqflite.** { *; }
-dontwarn com.tekartik.sqflite.**
# ============ shared_preferences ============
-keep class io.flutter.plugins.sharedpreferences.** { *; }
# ============ path_provider ============
-keep class io.flutter.plugins.pathprovider.** { *; }
# ============ permission_handler ============
-keep class com.baseflow.permissionhandler.** { *; }
# ============ url_launcher ============
-keep class io.flutter.plugins.urllauncher.** { *; }
# ============ device_info_plus ============
-keep class dev.fluttercommunity.plus.device_info.** { *; }
# ============ 你的原生通道(重要!) ============
# 你的 MethodChannel / EventChannel 相关类需要保留
-keep class com.lxh.battery_health.** { *; }
# ============ AndroidX 常见 ============
-keep class androidx.lifecycle.** { *; }
-dontwarn androidx.lifecycle.**
# ============ 保留 Parcelable / Serializable ============
-keepclassmembers class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
# ============ 异常堆栈保留(方便排查 release 崩溃) ============
-keepattributes SourceFile,LineNumberTable
-renamesourcefileattribute SourceFile
-keepattributes Signature
-keepattributes *Annotation*
-keepattributes Exceptions
-keepattributes InnerClasses
-keepattributes EnclosingMethod
# ============ 移除日志(可选) ============
# 如果你想在 release 里保留日志,注释掉下面这几行
-assumenosideeffects class android.util.Log {
public static *** v(...);
public static *** d(...);
public static *** i(...);
}
+8
View File
@@ -45,6 +45,14 @@
<intent>
<action android:name="android.intent.action.PROCESS_TEXT"/>
<data android:mimeType="text/plain"/>
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="https" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="http" />
</intent>
</queries>
</manifest>
@@ -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,
)
}
}
@@ -7,6 +7,7 @@ import android.content.IntentFilter
import android.content.SharedPreferences
import android.net.Uri
import android.os.BatteryManager
import android.app.Activity
import android.os.Build
import android.os.Bundle
import android.provider.DocumentsContract
@@ -135,7 +136,7 @@ class MainActivity : FlutterActivity() {
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == FOLDER_PICKER_REQUEST && resultCode == ComponentActivity.RESULT_OK) {
if (requestCode == FOLDER_PICKER_REQUEST && resultCode == RESULT_OK) {
val uri = data?.data
if (uri != null) {
try {
@@ -254,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)
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.12-all.zip
distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-9.3.1-bin.zip
+2 -2
View File
@@ -19,8 +19,8 @@ pluginManagement {
plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
id("com.android.application") version "8.9.1" apply false
id("org.jetbrains.kotlin.android") version "2.1.0" apply false
id("com.android.application") version "9.1.0" apply false
id("org.jetbrains.kotlin.android") version "2.2.20" apply false
}
include(":app")
+106
View File
@@ -1,7 +1,100 @@
## [1.9.0] - 2026-10-11
### ✨ 新增功能
- **BatteryManager 实时数据接入**
- 新增原生 `BatteryManagerHelper`,通过 Android 官方 API 读取电池端真实功率
- 主界面充电行改为显示电池端实时功率(1 秒刷新),反映电池实际吸收的能量
- 充电详情页拆分为两个数据源分区:
- **BMS 日志**:协议、标定档位、实际功率、电压、电流(原有)
- **BatteryManager(电池端)**:电池端电压、电流、功率(新增)
- 与专业功率计(PowerZ)实测数据一致,误差在 0.02W 以内
### 🎨 体验优化
- **充电行显示格式调整**
- 有线充电:`充电中 · PPS · 5.98W / 7.5W`(实际功率 / 标定档位)
- 无线充电:`充电中 · 无线 · 12.3W`
- USB 慢充:`充电中 · USB · 2.1W`
- 瓦特单位统一为大写 `W`
- 7.5W 等带小数的档位保留一位小数,其余整数值去掉 `.0`
- **字体细节优化**
- 充电行数字与拉丁字母改用 Flyme 字体,中文字符保持 HarmonyOS Sans SC
- 解决混合文本下的字体割裂感
- 充电详情页所有功率数值同步应用 Flyme 字体
### 🐛 问题修复
- 修复主界面充电行在充电器接入初期显示"读取中..."后数值迟迟不刷新的问题
- 修复详情页功率数值字体与其它行不统一的问题
- 修复 `wired_level` 解析后标定档位显示取整误差(如 `≈8W/L-1` 修正为 `≈7.5W/L-1`)
### 📝 技术说明
- `BATTERY_PROPERTY_CURRENT_NOW` 读数为**电池端净电流**(充电器输入减去系统功耗),而非充电器输出端数据
- 电池接近满电时数值会明显偏低(涓流阶段),属正常现象
- 与系统状态栏显示的充电功率为不同概念,前者反映电池实际吸收,后者反映充电器输出
---
## [1.8.0] - 2026-10-11
### 新增
- **夜间模式**:支持浅色 / 深色 / 跟随系统三种模式,可在右上角菜单切换
- **主题系统重构**:统一颜色抽象,所有界面颜色由主题管理
### 优化
- 深色主题下潘通色系重新调色,保证对比度
- AppBar 与内容区背景统一,消除浅色模式下的分界线
----
## [1.7.1] - 2026-09-16
### 新增
- **平均温度显示**:温度面板新增平均温度,与最高/最低温度同排展示(`↑ xx.x℃ ↓ xx.x℃ ⌀ xx.x℃`),更直观了解电池工作温度区间
### 优化
- **数据库升级至 v2**:新增 `temp_avg` 字段,历史数据自动迁移,每日平均温度随扫描自动入库
- **构建工具链同步**:适配最新 Flutter SDK 的 Android 构建要求(AGP 9.0 / Kotlin 2.2 / Gradle 9.3)
- **Release 打包加固**:补齐 ProGuard 混淆规则,release 版本构建更稳定
### 修复
- 修复 Release 版本因缺少混淆规则文件导致打包失败的问题
----
## [1.7.0] - 2026-07-31
### ✨ 新增功能
- **关于页面**
- 右上角三点菜单新增“关于”入口
- 展示应用 Logo、版本号、作者信息及版权声明
- 支持一键跳转源码仓库(Gitea),便于用户反馈问题
- **数据反作弊能力(紧急发布)**
- 容量数据入库前增加有效性校验,拒绝异常值污染趋势图
- 校验规则:容量低于设计容量 60% 或高于 115% 的日志数据将被自动过滤
- UI 层同步增加异常状态检测,容量或电压异常时相关数值自动标红警示
- 该机制同时作用于历史日志扫描与实时数据读取,确保统计结果真实可信
### 🔧 问题修复
- 修复部分机型上 `url_launcher` 无法正常打开链接的问题(Android 11+ 包可见性适配)
----
## [1.6.0] - 2026-07-20
### ✨ 新增
- **魅族 21 Note 机型适配**:新增 `21 Note` 特征字识别,设计容量 5500mAh
- 21 Note 运行 Flyme 12.6 版本,日志格式与 21 系列一致
- 支持 `learned_cap` 字段直接读取,与 21 / 21 Pro 逻辑相同
- 已加入 `DeviceMatching` 机型映射表
- **更新日志页面**:内置 `CHANGELOG.md` 渲染,版本历史一目了然
- 使用 `flutter_markdown` 解析,支持标准 Markdown 格式
- 滚动进度条:实时显示阅读进度,提升浏览体验
@@ -17,6 +110,14 @@
- 进入更新日志页面自动触发
- 退出页面自动释放,不影响系统省电策略
### ✅ 已确认完美支持(更新)
| 机型 | 容量来源 | 设计容量 |
|---|---|---|
| 魅族 20 / 20 Pro / 20 INFINITY | `remaining_cap` + `status=Full` | 4700 / 5000 / 4800 mAh |
| 魅族 21 / 21 Pro | `learned_cap` | 4800 / 5050 mAh |
| **魅族 21 Note(新增)** | `learned_cap` | **5500 mAh** |
### ⚡ 优化
- **UI 拆分**:将 `BatteryHealthPage` 的 UI 部分抽离至独立文件,降低耦合度
@@ -32,12 +133,17 @@
### 📊 影响范围
- `device_matching.dart`:新增 21 Note 机型映射
- `battery_health_page.dart`:AppBar 重构,动画简化
- `battery_data_layout.dart`(新增):UI 布局抽离
- `changelog_page.dart`(新增):更新日志页面
- `MainActivity.kt`:新增原生高刷控制通道
- `pubspec.yaml`:新增 `flutter_markdown` 依赖,添加 `assets/CHANGELOG.md`
### 📌 备注
- 本次更新为 1.6.0 封版版本,后续将聚焦 1.7.0(深色模式 + 关于页面)
- 21 Note 在 Flyme 12.6 上测试通过,日志解析正常 ✅
----
## [1.5.3] - 2026-07-19
+1
View File
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1789561763510" class="icon" viewBox="0 0 1282 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5901" xmlns:xlink="http://www.w3.org/1999/xlink" width="250.390625" height="200"><path d="M703.104 952h-2.272c-19.904 0-36.64-7.072-40.448-26.848L531.2 243.808l-139.392 328c-6.848 16.128-22.496 28.704-39.84 28.704H91.456A43.52 43.52 0 0 1 48 556.768a43.52 43.52 0 0 1 43.456-43.744h232.096l184.064-430.528a43.424 43.424 0 0 1 44.672-26.24 43.424 43.424 0 0 1 37.856 35.296l122.528 650.336 111.712-373.44c5.312-17.632 20.832-30.08 39.072-31.168 18.848-0.32 34.976 9.664 42.272 26.4l64.608 149.344h250.176a43.52 43.52 0 0 1 43.488 43.744 43.52 43.52 0 0 1-43.488 43.744h-278.624a43.52 43.52 0 0 1-39.808-26.4l-28.736-66.304-128.736 421.792c-5.472 18.56-22.496 22.4-41.504 22.4z m0 0" fill="#999999" p-id="5902"></path></svg>

After

Width:  |  Height:  |  Size: 972 B

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

-34
View File
@@ -1,34 +0,0 @@
**/dgph
*.mode1v3
*.mode2v3
*.moved-aside
*.pbxuser
*.perspectivev3
**/*sync/
.sconsign.dblite
.tags*
**/.vagrant/
**/DerivedData/
Icon?
**/Pods/
**/.symlinks/
profile
xcuserdata
**/.generated/
Flutter/App.framework
Flutter/Flutter.framework
Flutter/Flutter.podspec
Flutter/Generated.xcconfig
Flutter/ephemeral/
Flutter/app.flx
Flutter/app.zip
Flutter/flutter_assets/
Flutter/flutter_export_environment.sh
ServiceDefinitions.json
Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!default.mode1v3
!default.mode2v3
!default.pbxuser
!default.perspectivev3
-26
View File
@@ -1,26 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>App</string>
<key>CFBundleIdentifier</key>
<string>io.flutter.flutter.app</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>App</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>13.0</string>
</dict>
</plist>
-1
View File
@@ -1 +0,0 @@
#include "Generated.xcconfig"
-1
View File
@@ -1 +0,0 @@
#include "Generated.xcconfig"
-616
View File
@@ -1,616 +0,0 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 97C146E61CF9000F007C117D /* Project object */;
proxyType = 1;
remoteGlobalIDString = 97C146ED1CF9000F007C117D;
remoteInfo = Runner;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
331C8082294A63A400263BE5 /* RunnerTests */ = {
isa = PBXGroup;
children = (
331C807B294A618700263BE5 /* RunnerTests.swift */,
);
path = RunnerTests;
sourceTree = "<group>";
};
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
9740EEB31CF90195004384FC /* Generated.xcconfig */,
);
name = Flutter;
sourceTree = "<group>";
};
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
331C8082294A63A400263BE5 /* RunnerTests */,
);
sourceTree = "<group>";
};
97C146EF1CF9000F007C117D /* Products */ = {
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
331C8081294A63A400263BE5 /* RunnerTests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
);
path = Runner;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
331C8080294A63A400263BE5 /* RunnerTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
buildPhases = (
331C807D294A63A400263BE5 /* Sources */,
331C807F294A63A400263BE5 /* Resources */,
);
buildRules = (
);
dependencies = (
331C8086294A63A400263BE5 /* PBXTargetDependency */,
);
name = RunnerTests;
productName = RunnerTests;
productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
);
buildRules = (
);
dependencies = (
);
name = Runner;
productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = YES;
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
331C8080294A63A400263BE5 = {
CreatedOnToolsVersion = 14.0;
TestTargetID = 97C146ED1CF9000F007C117D;
};
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 1100;
};
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 97C146E51CF9000F007C117D;
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
97C146ED1CF9000F007C117D /* Runner */,
331C8080294A63A400263BE5 /* RunnerTests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
331C807F294A63A400263BE5 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
);
name = "Thin Binary";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
331C807D294A63A400263BE5 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
331C8086294A63A400263BE5 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 97C146ED1CF9000F007C117D /* Runner */;
targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C146FB1CF9000F007C117D /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C147001CF9000F007C117D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Profile;
};
249021D4217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.batteryHealth;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Profile;
};
331C8088294A63A400263BE5 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.example.batteryHealth.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
};
name = Debug;
};
331C8089294A63A400263BE5 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.example.batteryHealth.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
};
name = Release;
};
331C808A294A63A400263BE5 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.example.batteryHealth.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
};
name = Profile;
};
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
97C147061CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.batteryHealth;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
};
97C147071CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.batteryHealth;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
331C8088294A63A400263BE5 /* Debug */,
331C8089294A63A400263BE5 /* Release */,
331C808A294A63A400263BE5 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147031CF9000F007C117D /* Debug */,
97C147041CF9000F007C117D /* Release */,
249021D3217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147061CF9000F007C117D /* Debug */,
97C147071CF9000F007C117D /* Release */,
249021D4217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}
@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>
@@ -1,101 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1510"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "331C8080294A63A400263BE5"
BuildableName = "RunnerTests.xctest"
BlueprintName = "RunnerTests"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
enableGPUValidationMode = "1"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Profile"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
-7
View File
@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
</Workspace>
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>
-13
View File
@@ -1,13 +0,0 @@
import Flutter
import UIKit
@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
@@ -1,122 +0,0 @@
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@3x.png",
"scale" : "3x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@1x.png",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@1x.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@1x.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "Icon-App-83.5x83.5@2x.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "Icon-App-1024x1024@1x.png",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 295 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 450 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 282 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 462 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 704 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 586 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 862 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 862 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 762 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

@@ -1,23 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "LaunchImage.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 B

@@ -1,5 +0,0 @@
# Launch Screen Assets
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
@@ -1,37 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
</imageView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="LaunchImage" width="168" height="185"/>
</resources>
</document>
-26
View File
@@ -1,26 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies>
<scenes>
<!--Flutter View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>
-49
View File
@@ -1,49 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Battery Health</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>battery_health</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
</dict>
</plist>
-1
View File
@@ -1 +0,0 @@
#import "GeneratedPluginRegistrant.h"
-12
View File
@@ -1,12 +0,0 @@
import Flutter
import UIKit
import XCTest
class RunnerTests: XCTestCase {
func testExample() {
// If you add code to the Runner application, consider adding tests here.
// See https://developer.apple.com/documentation/xctest for more information about using XCTest.
}
}
+65 -45
View File
@@ -1,11 +1,14 @@
import 'package:flutter/material.dart';
import 'pages/battery_health_page.dart';
import 'services/saf_storage_service.dart';
import 'theme/app_colors.dart';
import 'theme/theme_service.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await SafStorageService().init();
await ThemeService.init(); // 🆕
runApp(const MyApp());
}
@@ -15,53 +18,70 @@ class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '电池健康度',
theme: ThemeData(
// 🟢 中文用 HarmonyOS Sans SC
fontFamily: 'HarmonyOS_Sans_SC',
// 🟢 数字回退到 FlymeFont
fontFamilyFallback: const ['Flyme'],
// 🟢 统一文字颜色为黑色
colorScheme: ColorScheme.fromSeed(
seedColor: const Color(0xFFFFFFFF),
).copyWith(onSurface: Colors.black),
textTheme: const TextTheme(
// 大标题/数字(健康度、容量等)
displayMedium: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontFamilyFallback: ['Flyme'],
color: Colors.black,
),
// 标题(如“容量百分比变动趋势”)
headlineMedium: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontFamilyFallback: ['Flyme'],
color: Colors.black,
),
// 正文(电压、温度、循环次数等)
bodyMedium: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontFamilyFallback: ['Flyme'],
color: Colors.black,
),
// 小字(日期、辅助信息)
bodySmall: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontFamilyFallback: ['Flyme'],
color: Colors.black87,
),
// 数字专用(大号数字,如健康度百分比)
displayLarge: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontFamilyFallback: ['Flyme'],
color: Colors.black,
),
return ValueListenableBuilder<ThemeMode>(
valueListenable: ThemeService.notifier,
builder: (context, mode, _) {
return MaterialApp(
title: '电池健康度',
themeMode: mode,
theme: _buildTheme(Brightness.light, kLightColors),
darkTheme: _buildTheme(Brightness.dark, kDarkColors),
home: const BatteryHealthPage(),
debugShowCheckedModeBanner: false,
);
},
);
}
ThemeData _buildTheme(Brightness brightness, AppColors colors) {
return ThemeData(
useMaterial3: true,
brightness: brightness,
scaffoldBackgroundColor: colors.background,
fontFamily: 'HarmonyOS_Sans_SC',
fontFamilyFallback: const ['Flyme'],
colorScheme: ColorScheme.fromSeed(
seedColor: colors.background,
brightness: brightness,
),
extensions: [colors], // 🆕 注册 ThemeExtension
appBarTheme: AppBarTheme(
backgroundColor: colors.background, // ← 关键:和 Scaffold 一致
foregroundColor: colors.textPrimary,
// ← 关掉 M3 的滑动变色
elevation: 0,
iconTheme: IconThemeData(color: colors.textPrimary),
),
textTheme: TextTheme(
displayMedium: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontFamilyFallback: const ['Flyme'],
color: colors.textPrimary,
),
useMaterial3: true,
headlineMedium: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontFamilyFallback: const ['Flyme'],
color: colors.textPrimary,
),
bodyMedium: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontFamilyFallback: const ['Flyme'],
color: colors.textPrimary,
),
bodySmall: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontFamilyFallback: const ['Flyme'],
color: colors.textSecondary,
),
displayLarge: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontFamilyFallback: const ['Flyme'],
color: colors.textPrimary,
),
),
home: const BatteryHealthPage(),
debugShowCheckedModeBanner: false,
);
}
}
+13
View File
@@ -0,0 +1,13 @@
enum DataAbnormalType {
/// 无异常
none,
/// 容量低于设计容量 60%
capacityTooLow,
/// 容量高于设计容量 115%
capacityTooHigh,
/// 电压异常(< 3.0V 或 > 5.0V)
voltageAbnormal,
}
+2
View File
@@ -3,6 +3,7 @@ class DeviceMatching {
/// 根据机型返回设计容量(mAh)
static int getDesignCapacity(String model) {
if (model.contains('21 Pro')) return 5050;
if (model.contains('21 Note')) return 5500;
if (model.contains('21')) return 4800;
if (model.contains('20 Pro')) return 5000;
if (model.contains('20 Inf')) return 4800;
@@ -29,6 +30,7 @@ class DeviceMatching {
/// 根据机型返回显示名称
static String getModelName(String model) {
if (model.contains('21 Pro')) return '魅族21 Pro';
if (model.contains('21 Note')) return '魅族21 Note';
if (model.contains('21')) return '魅族21';
if (model.contains('20 Pro')) return '魅族20 Pro';
if (model.contains('20 Inf')) return '魅族20 Infinity';
+158
View File
@@ -0,0 +1,158 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:url_launcher/url_launcher.dart';
import '../theme/app_colors.dart';
class AboutPage extends StatelessWidget {
const AboutPage({super.key});
@override
Widget build(BuildContext context) {
final c = context.colors;
return Scaffold(
appBar: AppBar(
title: const Text(
'关于',
style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontWeight: FontWeight.w500,
),
),
backgroundColor: c.background,
elevation: 0,
),
body: Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 40.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 120,
height: 120,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: c.surface,
),
child: ClipOval(
child: Image.asset(
'assets/logo.png',
width: 120,
height: 120,
fit: BoxFit.contain,
),
),
),
const SizedBox(height: 24),
Text(
'MEIZU Battery Healthy',
style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 24,
fontWeight: FontWeight.w600,
color: c.textPrimary,
),
),
const SizedBox(height: 8),
Text(
'版本 1.8.0',
style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 14,
color: c.textTertiary,
fontWeight: FontWeight.w400,
),
),
const SizedBox(height: 16),
Divider(
color: c.divider,
thickness: 0.5,
indent: 40,
endIndent: 40,
),
const SizedBox(height: 16),
Text(
'专机专配 · 仅针对魅族独家开发',
style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 14,
color: c.textSecondary,
),
),
const SizedBox(height: 8),
Text(
'通过读取 BMS 日志直接获取电池真实数据',
style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 13,
color: c.textTertiary,
),
),
const SizedBox(height: 24),
InkWell(
onTap: () => _launchUrl(context),
borderRadius: BorderRadius.circular(12),
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 12,
),
decoration: BoxDecoration(
color: c.surface,
borderRadius: BorderRadius.circular(12),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.code, size: 18, color: c.textPrimary),
const SizedBox(width: 8),
Text(
'查看源码 / 反馈',
style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 14,
color: c.textPrimary,
),
),
const SizedBox(width: 4),
Icon(Icons.open_in_new, size: 14, color: c.textTertiary),
],
),
),
),
const SizedBox(height: 40),
Text(
'Made with ❤️ in Flyme · 2026',
style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 12,
color: c.textTertiary,
),
),
],
),
),
),
);
}
Future<void> _launchUrl(BuildContext context) async {
const url = 'https://git.whitetop.xyz/lxh2875931338/MEIZU-Battery-Healthy';
final uri = Uri.parse(url);
try {
await launchUrl(uri, mode: LaunchMode.externalApplication);
} catch (e) {
debugPrint('打开链接失败: $e');
await Clipboard.setData(const ClipboardData(text: url));
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('无法自动打开浏览器,链接已复制到剪贴板。'),
duration: Duration(seconds: 3),
),
);
}
}
}
}
+211 -46
View File
@@ -3,10 +3,14 @@ import 'dart:developer' as dev;
import 'package:flutter/material.dart';
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';
import '../models/saf_permission_state.dart';
import '../models/data_abnormal_type.dart'; // 🆕 1.7.0
import '../services/database_service.dart';
import '../services/battery_data_service.dart';
import '../services/saf_storage_service.dart';
@@ -20,6 +24,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);
@@ -43,6 +48,7 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
String _deviceModel = "魅族21 Pro (默认)";
double _maxTemp = 0;
double _minTemp = 0;
double _batteryTempAvg = 0;
double _maxVolt = 0;
double _minVolt = 0;
bool _hasData = false;
@@ -59,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();
@@ -75,6 +84,7 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
// 轮询定时器(15秒)
Timer? _pollTimer;
Timer? _bmTimer;
// ---------- 生命周期 ----------
@override
@@ -119,7 +129,7 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
readBatteryCapacity();
}
});
_startBmPolling(); // ← 调用,不是定义
dev.log('🔵 ========== initState 结束 ==========', name: 'BatteryHealth');
}
@@ -127,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();
}
@@ -247,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;
}
@@ -302,7 +316,7 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
}
await _getDeviceInfo();
await _dataService.scanAndUpdateDB();
await _dataService.scanAndUpdateDB(designCap: _designCap);
final fileName = TimeUtils.getTodayFileName();
dev.log('🔵 今日文件名: $fileName', name: 'BatteryHealth');
@@ -357,6 +371,7 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
final volt = _extractVolt(lastLine);
final (maxTemp, minTemp) = _extractTempExtremes(validLines);
final (maxVolt, minVolt) = _extractVoltExtremes(validLines);
final tempAvg = _extractTempAvg(validLines);
dev.log(
'🔵 字段提取: cycle=$cycle, temp=$temp, volt=$volt',
@@ -381,10 +396,14 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
}
}
// 🟢 1.7.0 容量有效性校验
final validatedCap = _validateCapacity(finalCap, _designCap);
setState(() {
_currentCap = (finalCap ?? _currentCap).toDouble();
_currentCap = (validatedCap ?? _currentCap).toDouble();
_cycleCount = cycle;
_batteryTemp = temp;
_batteryTempAvg = tempAvg;
_batteryVolt = volt;
_maxTemp = maxTemp;
_minTemp = minTemp;
@@ -409,6 +428,53 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
}
}
// ---------- 🆕 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}),已忽略',
name: 'BatteryHealth',
);
return null;
}
// 容量不能超过设计容量的 115%
if (capacity > designCap * 1.15) {
dev.log(
'⚠️ 容量异常:高于设计容量 115%($capacity > ${designCap * 1.15}),已忽略',
name: 'BatteryHealth',
);
return null;
}
return capacity;
}
// ---------- 🆕 1.7.0 异常检测(供 UI 使用) ----------
DataAbnormalType _checkAbnormalType(
double currentCap,
int designCap,
double voltage,
) {
// 容量异常:高于 115% 或低于 60%
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;
}
// ---------- 辅助提取方法 ----------
int _extractCycle(String line) {
final reg = RegExp(r'cycle=(\d+)');
@@ -442,6 +508,20 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
return (maxTemp ?? 0, minTemp ?? 0);
}
double _extractTempAvg(List<String> lines) {
final reg = RegExp(r'tbat=(\d+)');
double sum = 0;
int count = 0;
for (String line in lines) {
final match = reg.firstMatch(line);
if (match != null) {
sum += double.parse(match.group(1)!);
count++;
}
}
return count > 0 ? sum / count : 0;
}
(double, double) _extractVoltExtremes(List<String> lines) {
final reg = RegExp(r'vbat=(\d+)');
double? maxVolt, minVolt;
@@ -539,24 +619,90 @@ 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() {
return ChargeUtils.getChargeTypeLabel(_isWireless, _chargeType);
}
// 🆕 夜间模式切换弹窗 ← 加在这里
void _showThemePicker() {
showModalBottomSheet(
context: context,
builder: (ctx) {
final current = ThemeService.notifier.value;
return SafeArea(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
leading: const Icon(Icons.brightness_auto),
title: const Text('跟随系统'),
trailing: current == ThemeMode.system
? const Icon(Icons.check)
: null,
onTap: () {
ThemeService.setMode(ThemeMode.system);
Navigator.pop(ctx);
},
),
ListTile(
leading: const Icon(Icons.light_mode),
title: const Text('浅色'),
trailing: current == ThemeMode.light
? const Icon(Icons.check)
: null,
onTap: () {
ThemeService.setMode(ThemeMode.light);
Navigator.pop(ctx);
},
),
ListTile(
leading: const Icon(Icons.dark_mode),
title: const Text('深色'),
trailing: current == ThemeMode.dark
? const Icon(Icons.check)
: null,
onTap: () {
ThemeService.setMode(ThemeMode.dark);
Navigator.pop(ctx);
},
),
],
),
);
},
);
}
// ---------- Build ----------
@override
Widget build(BuildContext context) {
@@ -568,26 +714,31 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
? '充电中 · ${_getChargeTypeLabel()} · ${_getDisplayPower()}'
: '';
// 🆕 1.7.0 异常检测
final abnormalType = _checkAbnormalType(
_currentCap,
_designCap,
_batteryVolt,
);
return AnnotatedRegion<SystemUiOverlayStyle>(
value: const SystemUiOverlayStyle(
statusBarColor: Color.fromRGBO(157, 212, 237, 0.1),
value: SystemUiOverlayStyle(
statusBarColor: context.colors.statusBarTint,
statusBarIconBrightness: Brightness.dark,
statusBarBrightness: Brightness.dark,
),
child: Scaffold(
backgroundColor: Colors.white,
// backgroundColor: Colors.white,
appBar: AppBar(
backgroundColor: Colors.white,
// backgroundColor: Colors.white,
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: context.colors.textPrimary,
),
),
actions: [
@@ -595,11 +746,11 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
icon: const Icon(Icons.refresh),
onPressed: readBatteryCapacity,
tooltip: '刷新数据',
color: Colors.black,
color: context.colors.icon,
),
PopupMenuButton<String>(
icon: const Icon(Icons.more_vert),
color: Colors.white,
color: context.colors.surface,
onSelected: (value) {
switch (value) {
case 'changelog':
@@ -610,24 +761,26 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
),
);
break;
case 'dark_mode':
case 'about': // 🟢 新增
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const AboutPage(),
),
);
break;
case 'about':
case 'dark_mode':
_showThemePicker();
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('关于(开发中)'),
value: 'about', // 🟢 新增
child: Text('关于'),
),
const PopupMenuItem(value: 'dark_mode', child: Text('夜间模式')),
],
),
],
@@ -635,37 +788,35 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
body: _hasData
? Column(
children: [
// 🟢 可滚动区域(带 RepaintBoundary)
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 设备型号
Text(
_deviceModel,
style: const TextStyle(
style: TextStyle(
fontSize: 14,
color: Colors.grey,
color: context.colors.textTertiary,
fontWeight: FontWeight.w400,
),
),
const SizedBox(height: 4),
// 🟢 BatterySummary 用 RepaintBoundary 隔离
// 🟢 BatterySummary(带异常检测)
RepaintBoundary(
child: BatterySummary(
healthPercent: healthPercent,
cycleCount: _cycleCount,
currentCap: _currentCap,
designCap: _designCap,
abnormalType: abnormalType, // 🆕 1.7.0
),
),
const SizedBox(height: 24),
// 🟢 TrendChart 用 RepaintBoundary 隔离
RepaintBoundary(
child: TrendChart(
data: _trendData,
@@ -675,7 +826,6 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
const SizedBox(height: 8),
// 🟢 充电条目 - 保留 AnimatedOpacity,但加 RepaintBoundary
RepaintBoundary(
child: AnimatedOpacity(
opacity: _isCharging ? 1.0 : 0.0,
@@ -695,6 +845,9 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
volt: _chargeVolt,
curr: _chargeCurr,
wiredLevel: _wiredLevel,
bmPower: _bmPower, // 🆕
bmVolt: _bmVolt, // 🆕
bmCurr: _bmCurr, // 🆕
),
),
);
@@ -706,15 +859,17 @@ class _BatteryHealthPageState extends State<BatteryHealthPage> {
const SizedBox(height: 4),
// 🟢 BatteryInfoPanel 也用 RepaintBoundary
// 🟢 BatteryInfoPanel(带异常检测)
RepaintBoundary(
child: BatteryInfoPanel(
batteryVolt: _batteryVolt,
batteryTemp: _batteryTemp,
tempAvg: _batteryTempAvg,
maxVolt: _maxVolt,
minVolt: _minVolt,
maxTemp: _maxTemp,
minTemp: _minTemp,
abnormalType: abnormalType, // 🆕 1.7.0
),
),
],
@@ -723,19 +878,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: context.colors.textTertiary,
),
const SizedBox(height: 16),
Text(
'暂无数据',
style: TextStyle(fontSize: 18, color: Colors.grey),
style: TextStyle(
fontSize: 18,
color: context.colors.textTertiary,
),
),
Text(
'点击右上角刷新尝试',
style: TextStyle(fontSize: 14, color: Colors.grey),
style: TextStyle(
fontSize: 14,
color: context.colors.textTertiary,
),
),
],
),
+27 -41
View File
@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; // 🟢 添加这一行
import 'package:flutter/services.dart' show rootBundle;
import 'package:flutter/services.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import '../theme/app_colors.dart';
class ChangelogPage extends StatefulWidget {
const ChangelogPage({super.key});
@@ -16,33 +16,27 @@ class _ChangelogPageState extends State<ChangelogPage>
String _markdownContent = '';
bool _isLoading = true;
String? _error;
Widget? _cachedMarkdown;
// ---------- 滚动 ----------
final ScrollController _scrollController = ScrollController();
double _scrollProgress = 0.0;
bool _isScrolling = false;
// ---------- 呼吸动画(维持高刷感知) ----------
// ---------- 呼吸动画 ----------
late AnimationController _breathController;
late Animation<double> _breathAnimation;
// ---------- 原生刷新率控制 ----------
// 🟢 改用 static final(不是 const)
static final _frameRateChannel = MethodChannel(
'com.lxh.battery_health/frame_rate',
);
// ---------- 生命周期 ----------
@override
void initState() {
super.initState();
_loadChangelog();
// 进入页面时申请高刷(原生层)
_frameRateChannel.invokeMethod('setHighRefreshRate');
// 呼吸动画:让进度条在滚动时轻微抖动,维持高刷感知
_breathController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 500),
@@ -71,16 +65,13 @@ class _ChangelogPageState extends State<ChangelogPage>
@override
void dispose() {
// 离开页面时恢复默认刷新率
_frameRateChannel.invokeMethod('clearRefreshRate');
_scrollController.removeListener(_updateProgress);
_scrollController.dispose();
_breathController.dispose();
super.dispose();
}
// ---------- 方法 ----------
void _updateProgress() {
if (!_scrollController.hasClients) return;
final maxScroll = _scrollController.position.maxScrollExtent;
@@ -97,7 +88,6 @@ class _ChangelogPageState extends State<ChangelogPage>
setState(() {
_markdownContent = content;
_isLoading = false;
_cachedMarkdown = _buildMarkdown(content);
});
} catch (e) {
setState(() {
@@ -107,78 +97,74 @@ class _ChangelogPageState extends State<ChangelogPage>
}
}
Widget _buildMarkdown(String content) {
Widget _buildMarkdown(BuildContext context, String content) {
final c = context.colors;
return Markdown(
data: content,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
styleSheet: MarkdownStyleSheet(
h1: const TextStyle(
h1: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 24,
fontWeight: FontWeight.w700,
color: Colors.black,
color: c.textPrimary,
),
h2: const TextStyle(
h2: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 20,
fontWeight: FontWeight.w600,
color: Colors.black87,
color: c.textPrimary,
),
h3: const TextStyle(
h3: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 18,
fontWeight: FontWeight.w600,
color: Colors.black87,
color: c.textSecondary,
),
p: const TextStyle(
p: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 16,
color: Colors.black87,
color: c.textSecondary,
),
listBullet: const TextStyle(
listBullet: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 16,
color: Colors.black87,
color: c.textSecondary,
),
code: const TextStyle(
code: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 14,
color: Colors.black87,
backgroundColor: Color(0xFFF5F5F5),
color: c.textSecondary,
backgroundColor: c.surface,
),
),
);
}
// ---------- Build ----------
@override
Widget build(BuildContext context) {
final c = context.colors;
final breathOffset = _isScrolling ? _breathAnimation.value : 0.0;
final displayProgress = (_scrollProgress + breathOffset).clamp(0.0, 1.0);
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: const Text(
title: Text(
'更新日志',
style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
color: Colors.black,
color: c.textPrimary,
),
),
backgroundColor: Colors.white,
elevation: 0,
foregroundColor: Colors.black,
iconTheme: const IconThemeData(color: Colors.black),
bottom: PreferredSize(
preferredSize: const Size.fromHeight(2.0),
child: LinearProgressIndicator(
value: displayProgress,
backgroundColor: Colors.grey.shade200,
valueColor: const AlwaysStoppedAnimation<Color>(
Color.fromRGBO(157, 212, 237, 1.0),
),
backgroundColor: c.divider,
valueColor: AlwaysStoppedAnimation<Color>(c.progressBar),
minHeight: 2.0,
),
),
@@ -189,16 +175,16 @@ class _ChangelogPageState extends State<ChangelogPage>
? Center(
child: Text(
_error!,
style: const TextStyle(
style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
color: Colors.grey,
color: c.textTertiary,
),
),
)
: SingleChildScrollView(
controller: _scrollController,
padding: const EdgeInsets.all(16.0),
child: _cachedMarkdown ?? const SizedBox.shrink(),
child: _buildMarkdown(context, _markdownContent),
),
);
}
+79 -38
View File
@@ -9,8 +9,40 @@ class BatteryDataService {
final SafStorageService _safService = SafStorageService();
final DatabaseService _dbService = DatabaseService();
/// 🟢 1.7.0 容量有效性校验(入库专用)
int? _validateCapacityForDB(int? capacity, int designCap) {
if (capacity == null) return null;
// 容量必须大于 0
if (capacity <= 0) {
dev.log('⚠️ [DB校验] 容量异常:<= 0,已拒绝入库', name: 'BatteryDataService');
return null;
}
// 容量不能低于设计容量的 60%
if (capacity < designCap * 0.6) {
dev.log(
'⚠️ [DB校验] 容量异常:低于设计容量 60%($capacity < ${designCap * 0.6}),已拒绝入库',
name: 'BatteryDataService',
);
return null;
}
// 容量不能超过设计容量的 115%
if (capacity > designCap * 1.15) {
dev.log(
'⚠️ [DB校验] 容量异常:高于设计容量 115%($capacity > ${designCap * 1.15}),已拒绝入库',
name: 'BatteryDataService',
);
return null;
}
return capacity;
}
/// 扫描所有日志文件并更新数据库
Future<void> scanAndUpdateDB() async {
/// 🟢 1.7.0 增加 designCap 参数,用于入库前校验
Future<void> scanAndUpdateDB({int? designCap}) async {
dev.log('🔵 scanAndUpdateDB 开始', name: 'BatteryHealth');
final state = await _safService.getPermissionState();
@@ -34,56 +66,65 @@ class BatteryDataService {
final lines = await _safService.readLogFile(fileName);
if (lines.isEmpty) continue;
// 🟢 1.4.5 改进:使用 CapacityExtractor 提取容量(仅从 Full 状态提取)
int? cap = CapacityExtractor.extractCapacity(lines);
// 使用 CapacityExtractor 提取容量
final cap = CapacityExtractor.extractCapacity(lines);
// 🟢 如果今天没有充满电(cap == null),尝试从昨天复制数据
if (cap == null) {
dev.log('🔵 日期 $dateStr 无充满记录,尝试复制前一天数据', name: 'BatteryHealth');
// 查询最近一条已有数据
final recentData = await _dbService.queryRecentData(1);
if (recentData.isNotEmpty) {
final yesterdayCap = recentData.first['capacity'] as int;
final yesterdayMaxVolt =
recentData.first['volt_max'] as double? ?? 0.0;
final yesterdayMinVolt =
recentData.first['volt_min'] as double? ?? 0.0;
dev.log(
'🔵 复制前一天数据: cap=$yesterdayCap, date=$dateStr',
name: 'BatteryHealth',
);
// 🟢 复制昨天的容量和电压极值到今天
await _dbService.insertOrUpdate(
dateStr,
yesterdayCap,
yesterdayMaxVolt,
yesterdayMinVolt,
);
} else {
dev.log('🔵 无历史数据可复制,跳过日期 $dateStr', name: 'BatteryHealth');
}
continue; // 已处理,跳过后续电压提取逻辑
// 🟢 1.7.0 如果 designCap 传入,则进行入库前校验
int? validatedCap = cap;
if (designCap != null && designCap > 0) {
validatedCap = _validateCapacityForDB(cap, designCap);
}
// 提取电压极值(从所有有效行提取,不限于 Full)
if (validatedCap == null) {
if (cap != null) {
dev.log(
'🔵 日期 $dateStr 容量校验未通过 ($cap),跳过入库',
name: 'BatteryHealth',
);
} else {
dev.log('🔵 日期 $dateStr 无有效容量数据,跳过入库', name: 'BatteryHealth');
}
continue;
}
// 提取电压极值与平均温度
final voltReg = RegExp(r'vbat=(\d+)');
final tempReg = RegExp(r'tbat=(\d+)');
double? maxVolt, minVolt;
double tempSum = 0;
int tempCount = 0;
for (String line in lines) {
if (!line.contains('tbat=')) continue;
final match = voltReg.firstMatch(line);
if (match != null) {
final v = int.parse(match.group(1)!) / 1000000;
final vMatch = voltReg.firstMatch(line);
if (vMatch != null) {
final v = int.parse(vMatch.group(1)!) / 1000000;
if (maxVolt == null || v > maxVolt) maxVolt = v;
if (minVolt == null || v < minVolt) minVolt = v;
}
final tMatch = tempReg.firstMatch(line);
if (tMatch != null) {
tempSum += double.parse(tMatch.group(1)!);
tempCount++;
}
}
if (maxVolt == null || minVolt == null) continue;
await _dbService.insertOrUpdate(dateStr, cap, maxVolt, minVolt);
dev.log('🔵 插入数据库: $dateStr, cap=$cap', name: 'BatteryHealth');
final tempAvg = tempCount > 0 ? tempSum / tempCount : 0.0;
await _dbService.insertOrUpdate(
dateStr,
validatedCap,
maxVolt,
minVolt,
tempAvg: tempAvg, // 🆕
);
dev.log(
'🔵 插入数据库: $dateStr, cap=$validatedCap',
name: 'BatteryHealth',
);
} catch (e) {
dev.log('🔵 处理文件 $fileName 异常: $e', name: 'BatteryHealth');
}
+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);
}
}
+20 -6
View File
@@ -6,22 +6,21 @@ import 'package:path/path.dart';
class DatabaseService {
static Database? _database;
static const String _tableName = 'battery_data';
static const int _dbVersion = 2; // 🆕 1.7.1 加入 temp_avg
/// 获取数据库实例(单例)
Future<Database> get database async {
if (_database != null) return _database!;
_database = await _initDatabase();
return _database!;
}
/// 初始化数据库
Future<Database> _initDatabase() async {
final dbPath = await getDatabasesPath();
final path = join(dbPath, 'battery_health.db');
return await openDatabase(
path,
version: 1,
version: _dbVersion,
onCreate: (db, version) async {
await db.execute('''
CREATE TABLE $_tableName (
@@ -29,6 +28,7 @@ class DatabaseService {
capacity INTEGER NOT NULL,
volt_max REAL NOT NULL,
volt_min REAL NOT NULL,
temp_avg REAL NOT NULL DEFAULT 0,
updated_at TEXT NOT NULL
)
''');
@@ -37,6 +37,15 @@ class DatabaseService {
''');
dev.log('✅ 数据库表创建成功', name: 'DatabaseService');
},
onUpgrade: (db, oldVersion, newVersion) async {
// 🆕 1.7.1 v1 -> v2:添加 temp_avg 字段
if (oldVersion < 2) {
await db.execute(
'ALTER TABLE $_tableName ADD COLUMN temp_avg REAL NOT NULL DEFAULT 0',
);
dev.log('✅ 数据库升级 v1->v2:已添加 temp_avg 字段', name: 'DatabaseService');
}
},
);
}
@@ -45,8 +54,9 @@ class DatabaseService {
String date,
int capacity,
double voltMax,
double voltMin,
) async {
double voltMin, {
double tempAvg = 0, // 🆕
}) async {
final db = await database;
final now = DateTime.now().toIso8601String();
@@ -55,9 +65,13 @@ class DatabaseService {
'capacity': capacity,
'volt_max': voltMax,
'volt_min': voltMin,
'temp_avg': tempAvg,
'updated_at': now,
}, conflictAlgorithm: ConflictAlgorithm.replace);
dev.log('📝 数据已保存: $date, cap=$capacity', name: 'DatabaseService');
dev.log(
'📝 数据已保存: $date, cap=$capacity, tempAvg=${tempAvg.toStringAsFixed(1)}',
name: 'DatabaseService',
);
}
/// 查询最近 N 条数据(按日期倒序)
+164
View File
@@ -0,0 +1,164 @@
import 'package:flutter/material.dart';
@immutable
class AppColors extends ThemeExtension<AppColors> {
final Color background; // 页面背景
final Color surface; // 卡片/面板背景
final Color textPrimary; // 主文字
final Color textSecondary; // 次要文字
final Color textTertiary; // 弱化文字
final Color divider; // 分割线
final Color icon; // 图标色
final Color healthGreen; // 健康度 ≥80%
final Color healthYellow; // 70~80%
final Color healthOrange;
final Color healthRed; // <70% 或异常
final Color barEmpty; // 无数据柱
final Color voltUp; // 电压↑
final Color voltDown; // 电压↓
final Color tempUp;
final Color tempDown;
final Color progressBar; // 更新日志进度条
final Color statusBarTint; // 状态栏背景
const AppColors({
required this.background,
required this.surface,
required this.textPrimary,
required this.textSecondary,
required this.textTertiary,
required this.divider,
required this.icon,
required this.healthGreen,
required this.healthYellow,
required this.healthOrange,
required this.healthRed,
required this.barEmpty,
required this.voltUp,
required this.voltDown,
required this.tempUp,
required this.tempDown,
required this.progressBar,
required this.statusBarTint,
});
@override
AppColors copyWith({
Color? background,
Color? surface,
Color? textPrimary,
Color? textSecondary,
Color? textTertiary,
Color? divider,
Color? icon,
Color? healthGreen,
Color? healthYellow,
Color? healthOrange,
Color? healthRed,
Color? barEmpty,
Color? voltUp,
Color? voltDown,
Color? tempUp,
Color? tempDown,
Color? progressBar,
Color? statusBarTint,
}) {
return AppColors(
background: background ?? this.background,
surface: surface ?? this.surface,
textPrimary: textPrimary ?? this.textPrimary,
textSecondary: textSecondary ?? this.textSecondary,
textTertiary: textTertiary ?? this.textTertiary,
divider: divider ?? this.divider,
icon: icon ?? this.icon,
healthGreen: healthGreen ?? this.healthGreen,
healthYellow: healthYellow ?? this.healthYellow,
healthOrange: healthOrange ?? this.healthOrange,
healthRed: healthRed ?? this.healthRed,
barEmpty: barEmpty ?? this.barEmpty,
voltUp: voltUp ?? this.voltUp,
voltDown: voltDown ?? this.voltDown,
tempUp: tempUp ?? this.tempUp,
tempDown: tempDown ?? this.tempDown,
progressBar: progressBar ?? this.progressBar,
statusBarTint: statusBarTint ?? this.statusBarTint,
);
}
@override
AppColors lerp(ThemeExtension<AppColors>? other, double t) {
if (other is! AppColors) return this;
return AppColors(
background: Color.lerp(background, other.background, t)!,
surface: Color.lerp(surface, other.surface, t)!,
textPrimary: Color.lerp(textPrimary, other.textPrimary, t)!,
textSecondary: Color.lerp(textSecondary, other.textSecondary, t)!,
textTertiary: Color.lerp(textTertiary, other.textTertiary, t)!,
divider: Color.lerp(divider, other.divider, t)!,
icon: Color.lerp(icon, other.icon, t)!,
healthGreen: Color.lerp(healthGreen, other.healthGreen, t)!,
healthYellow: Color.lerp(healthYellow, other.healthYellow, t)!,
healthOrange: Color.lerp(healthOrange, other.healthOrange, t)!,
healthRed: Color.lerp(healthRed, other.healthRed, t)!,
barEmpty: Color.lerp(barEmpty, other.barEmpty, t)!,
voltUp: Color.lerp(voltUp, other.voltUp, t)!,
voltDown: Color.lerp(voltDown, other.voltDown, t)!,
tempUp: Color.lerp(tempUp, other.tempUp, t)!,
tempDown: Color.lerp(tempDown, other.tempDown, t)!,
progressBar: Color.lerp(progressBar, other.progressBar, t)!,
statusBarTint: Color.lerp(statusBarTint, other.statusBarTint, t)!,
);
}
}
/// 浅色主题(保持现有视觉)
const AppColors kLightColors = AppColors(
background: Colors.white,
surface: Color(0xFFF0F4F8),
textPrimary: Colors.black,
textSecondary: Color(0xFF5F5F5F),
textTertiary: Colors.grey,
divider: Color(0xFFE0E0E0),
icon: Colors.black,
healthGreen: Color(0xFF00B140),
healthYellow: Color(0xFFEFDF00),
healthOrange: Color(0xFFD75A16),
healthRed: Colors.red,
barEmpty: Color(0xFFE0E0E0),
voltUp: Colors.red,
voltDown: Colors.blue,
tempUp: Colors.red,
tempDown: Colors.blue,
progressBar: Color.fromRGBO(157, 212, 237, 1.0),
statusBarTint: Color.fromRGBO(157, 212, 237, 0.1),
);
/// 深色主题(色相保留,明度调整)
const AppColors kDarkColors = AppColors(
background: Color(0xFF121212),
surface: Color(0xFF1E1E1E),
textPrimary: Color(0xFFE8E8E8),
textSecondary: Color(0xFFA0A0A0),
textTertiary: Color(0xFF707070),
divider: Color(0xFF2A2A2A),
icon: Color(0xFFE8E8E8),
healthGreen: Color(0xFF00D14D),
healthYellow: Color(0xFFD4C520),
healthOrange: Color(0xFFFF7838),
healthRed: Color(0xFFFF5252),
barEmpty: Color(0xFF2A2A2A),
voltUp: Color(0xFFFF5252),
voltDown: Color(0xFF5AA9FF),
tempUp: Color(0xFFFF5252),
tempDown: Color(0xFF5AA9FF),
progressBar: Color(0xFF5A9BBD),
statusBarTint: Color(0xFF121212),
);
/// 便捷扩展:context.colors.textPrimary
extension AppColorsX on BuildContext {
AppColors get colors => Theme.of(this).extension<AppColors>()!;
}
+45
View File
@@ -0,0 +1,45 @@
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class ThemeService {
static const _prefKey = 'theme_mode';
static final ValueNotifier<ThemeMode> notifier = ValueNotifier(
ThemeMode.system,
);
/// 启动时调用(main.dart)
static Future<void> init() async {
final prefs = await SharedPreferences.getInstance();
final saved = prefs.getString(_prefKey);
notifier.value = _parse(saved);
}
/// 菜单里调用
static Future<void> setMode(ThemeMode mode) async {
notifier.value = mode;
final prefs = await SharedPreferences.getInstance();
await prefs.setString(_prefKey, _name(mode));
}
static ThemeMode _parse(String? s) {
switch (s) {
case 'light':
return ThemeMode.light;
case 'dark':
return ThemeMode.dark;
default:
return ThemeMode.system;
}
}
static String _name(ThemeMode m) {
switch (m) {
case ThemeMode.light:
return 'light';
case ThemeMode.dark:
return 'dark';
case ThemeMode.system:
return 'system';
}
}
}
+44 -20
View File
@@ -1,7 +1,7 @@
import '../constants/charge_maps.dart';
class ChargeUtils {
// ============ 无线充电(保持不变) ============
// ============ 无线充电 ============
static String getDisplayPowerWireless(double power) {
if (power == 0) return '读取中...';
const List<int> levels = [5, 7, 15, 27, 40, 50, 66];
@@ -13,7 +13,7 @@ class ChargeUtils {
return '≈${matchedLevel}W';
}
// ============ 原有有线逻辑(保留兼容,但主界面不再直接调用) ============
// ============ 旧的有线逻辑(保留兼容) ============
static String getDisplayPowerWired(
double power,
int? wiredLevel,
@@ -34,57 +34,81 @@ class ChargeUtils {
return best > 0 ? '≈${best.toInt()}W' : '≈${power.round()}W';
}
// ============ 🟢 1.5.0 新增:主界面显示(实际/标定) ============
/// 示例:≈30w/65w
// ============ 主界面:实际 / 标定(旧版) ============
static String getMainDisplayPowerWired(
double power,
int? wiredLevel,
String chargeType,
) {
// USB 慢充锁定 7.5W
if (chargeType == "SDP") return "≈7.5w";
if (chargeType == "SDP") return "≈7.5W";
if (power == 0) return "读取中...";
final actual = power.round();
// 有档位且能查到标定值
if (wiredLevel != null && ChargeMaps.wiredPower.containsKey(wiredLevel)) {
final requested = ChargeMaps.wiredPower[wiredLevel]!.round();
return "≈${actual}w/${requested}w";
return "≈${actual}W/${requested}W";
}
// 无档位信息,仅显示实际功率
return "≈${actual}w";
return "≈${actual}W";
}
// ============ 🟢 1.5.0 新增:二级页面标定档位显示 ============
/// 示例:≈80w/L-10
// ============ 主界面:BM 电池端功率 + 标定档位 ============
/// 示例:5.98W / 7.5W、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]!;
final requestedStr = requested == requested.roundToDouble()
? requested.toInt().toString()
: requested.toStringAsFixed(1);
return '$powerStr / ${requestedStr}W';
}
return powerStr;
}
// ============ 详情页:标定档位 ============
/// 示例:≈80W/L-10、≈7.5W/L-1
static String getDetailRequestedPowerWired(
double power,
int? wiredLevel,
String chargeType,
) {
if (chargeType == "SDP") return "≈7.5w";
if (chargeType == "SDP") return "≈7.5W";
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";
return "≈${power.round()}W";
}
// ============ 🟢 1.5.0 新增:二级页面实际功率显示 ============
// ============ 详情页:实际功率 ============
static String getDetailActualPowerWired(double power, String chargeType) {
if (chargeType == "SDP") return "≈7.5w";
if (chargeType == "SDP") return "≈7.5W";
if (power == 0) return "读取中...";
return "≈${power.round()}w";
return "≈${power.round()}W";
}
// ============ 充电类型标签(保持不变) ============
// ============ 充电类型标签 ============
static String getChargeTypeLabel(bool isWireless, String chargeType) {
if (isWireless) {
return chargeType == "无线" ? "无线" : chargeType;
+76 -32
View File
@@ -1,5 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import '../models/data_abnormal_type.dart';
import '../theme/app_colors.dart';
class BatteryInfoPanel extends StatelessWidget {
final double batteryVolt;
@@ -8,6 +10,8 @@ class BatteryInfoPanel extends StatelessWidget {
final double minVolt;
final double maxTemp;
final double minTemp;
final double tempAvg;
final DataAbnormalType abnormalType;
const BatteryInfoPanel({
super.key,
@@ -17,21 +21,31 @@ class BatteryInfoPanel extends StatelessWidget {
required this.minVolt,
required this.maxTemp,
required this.minTemp,
this.tempAvg = 0,
this.abnormalType = DataAbnormalType.none,
});
@override
Widget build(BuildContext context) {
final c = context.colors;
final bool isVoltageAbnormal =
abnormalType == DataAbnormalType.voltageAbnormal;
final Color voltColor = isVoltageAbnormal ? c.healthRed : c.textPrimary;
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// ---------- 电压列 ----------
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildInfoItem(
_buildIcon('voltage'),
context,
_buildIcon(context, 'voltage'),
'电压',
'${batteryVolt.toStringAsFixed(3)}V',
textColor: voltColor,
),
const SizedBox(height: 8),
Row(
@@ -42,14 +56,14 @@ class BatteryInfoPanel extends StatelessWidget {
fontFamily: 'Flyme',
fontSize: 14,
fontWeight: FontWeight.w500,
color: Colors.red,
color: c.voltUp,
),
children: [
const TextSpan(
TextSpan(
text: '↑ ',
style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
color: Colors.red,
color: c.voltUp,
),
),
TextSpan(text: '${maxVolt.toStringAsFixed(3)}V'),
@@ -63,14 +77,14 @@ class BatteryInfoPanel extends StatelessWidget {
fontFamily: 'Flyme',
fontSize: 14,
fontWeight: FontWeight.w500,
color: Colors.blue,
color: c.voltDown,
),
children: [
const TextSpan(
TextSpan(
text: '↓ ',
style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
color: Colors.blue,
color: c.voltDown,
),
),
TextSpan(text: '${minVolt.toStringAsFixed(3)}V'),
@@ -82,12 +96,14 @@ class BatteryInfoPanel extends StatelessWidget {
],
),
),
// ---------- 温度列 ----------
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildInfoItem(
_buildIcon('temperature'),
context,
_buildIcon(context, 'temperature'),
'温度',
'${batteryTemp.toStringAsFixed(0)}℃',
),
@@ -98,43 +114,55 @@ class BatteryInfoPanel extends StatelessWidget {
text: TextSpan(
style: TextStyle(
fontFamily: 'Flyme',
fontSize: 14,
fontSize: 12,
fontWeight: FontWeight.w500,
color: Colors.red,
color: c.tempUp,
),
children: [
const TextSpan(
TextSpan(
text: '↑ ',
style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
color: Colors.red,
color: c.tempUp,
),
),
TextSpan(text: '${maxTemp.toStringAsFixed(0)}℃'),
TextSpan(text: '${maxTemp.toStringAsFixed(1)}℃'),
],
),
),
const SizedBox(width: 12),
const SizedBox(width: 6),
RichText(
text: TextSpan(
style: TextStyle(
fontFamily: 'Flyme',
fontSize: 14,
fontSize: 12,
fontWeight: FontWeight.w500,
color: Colors.blue,
color: c.tempDown,
),
children: [
const TextSpan(
TextSpan(
text: '↓ ',
style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
color: Colors.blue,
color: c.tempDown,
),
),
TextSpan(text: '${minTemp.toStringAsFixed(0)}℃'),
TextSpan(text: '${minTemp.toStringAsFixed(1)}℃'),
],
),
),
const SizedBox(width: 6),
_buildIcon(context, 'avg', size: 14),
const SizedBox(width: 2),
Text(
'${tempAvg.toStringAsFixed(1)}℃',
style: TextStyle(
fontFamily: 'Flyme',
fontSize: 12,
fontWeight: FontWeight.w500,
color: c.textTertiary,
),
),
],
),
],
@@ -144,48 +172,64 @@ class BatteryInfoPanel extends StatelessWidget {
);
}
Widget _buildIcon(String type) {
Widget _buildIcon(BuildContext context, String type, {double size = 18}) {
final c = context.colors;
final filter = ColorFilter.mode(c.textTertiary, BlendMode.srcIn);
switch (type) {
case 'voltage':
return SvgPicture.asset(
'assets/icons/si--lightning-fill.svg',
width: 18,
height: 18,
colorFilter: const ColorFilter.mode(Colors.grey, BlendMode.srcIn),
width: size,
height: size,
colorFilter: filter,
);
case 'temperature':
return SvgPicture.asset(
'assets/icons/mdi--temperature.svg',
width: 18,
height: 18,
colorFilter: const ColorFilter.mode(Colors.grey, BlendMode.srcIn),
width: size,
height: size,
colorFilter: filter,
);
case 'avg':
return SvgPicture.asset(
'assets/icons/avg.svg',
width: size,
height: size,
colorFilter: filter,
);
default:
return const Icon(Icons.help_outline, size: 18, color: Colors.grey);
return Icon(Icons.help_outline, size: size, color: c.textTertiary);
}
}
Widget _buildInfoItem(Widget icon, String label, String value) {
Widget _buildInfoItem(
BuildContext context,
Widget icon,
String label,
String value, {
Color? textColor,
}) {
final c = context.colors;
return Row(
children: [
icon,
const SizedBox(width: 6),
Text(
label,
style: const TextStyle(
style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 14,
color: Colors.grey,
color: c.textTertiary,
),
),
const SizedBox(width: 6),
Text(
value,
style: const TextStyle(
style: TextStyle(
fontFamily: 'Flyme',
fontSize: 16,
fontWeight: FontWeight.w500,
color: Colors.black,
color: textColor ?? c.textPrimary,
),
),
],
+28 -12
View File
@@ -1,10 +1,13 @@
import 'package:flutter/material.dart';
import '../models/data_abnormal_type.dart';
import '../theme/app_colors.dart';
class BatterySummary extends StatelessWidget {
final double healthPercent;
final int cycleCount;
final double currentCap;
final int designCap;
final DataAbnormalType abnormalType;
const BatterySummary({
super.key,
@@ -12,16 +15,28 @@ class BatterySummary extends StatelessWidget {
required this.cycleCount,
required this.currentCap,
required this.designCap,
this.abnormalType = DataAbnormalType.none,
});
@override
Widget build(BuildContext context) {
final bool isLowHealth = healthPercent < 70;
final c = context.colors;
final bool isAbnormal = abnormalType != DataAbnormalType.none;
final bool isCapacityAbnormal =
abnormalType == DataAbnormalType.capacityTooLow ||
abnormalType == DataAbnormalType.capacityTooHigh;
final Color healthColor = isAbnormal
? c.healthRed
: (healthPercent < 70 ? c.healthRed : c.textPrimary);
final Color capacityColor = isCapacityAbnormal
? c.healthRed
: c.textSecondary;
return Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
// 健康度大数字
Row(
crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.alphabetic,
@@ -33,7 +48,7 @@ class BatterySummary extends StatelessWidget {
fontSize: 40,
fontWeight: FontWeight.w500,
height: 1.0,
color: isLowHealth ? Colors.red : Colors.black,
color: healthColor,
),
),
const SizedBox(width: 2),
@@ -44,13 +59,14 @@ class BatterySummary extends StatelessWidget {
fontSize: 20,
fontWeight: FontWeight.w300,
height: 1.0,
color: isLowHealth ? Colors.red : Colors.grey.shade600,
color: healthColor == c.healthRed
? c.healthRed
: c.textSecondary,
),
),
],
),
const Spacer(),
// cycles + mAh
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
@@ -61,15 +77,15 @@ class BatterySummary extends StatelessWidget {
fontSize: 16,
fontWeight: FontWeight.w500,
height: 1.2,
color: Colors.black,
color: c.textPrimary,
),
children: [
TextSpan(text: '$cycleCount'),
TextSpan(
text: ' cycles',
style: const TextStyle(
style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
color: Colors.black,
color: c.textPrimary,
),
),
],
@@ -83,7 +99,7 @@ class BatterySummary extends StatelessWidget {
fontSize: 20,
fontWeight: FontWeight.w300,
height: 1.0,
color: const Color(0xFF5F5F5F),
color: capacityColor,
),
children: [
TextSpan(text: currentCap.toStringAsFixed(0)),
@@ -91,17 +107,17 @@ class BatterySummary extends StatelessWidget {
text: ' / ',
style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
color: const Color(0xFF5F5F5F).withValues(alpha: 0.5),
color: capacityColor.withOpacity(0.5),
),
),
TextSpan(text: designCap.toString()),
TextSpan(
text: ' mAh',
style: const TextStyle(
style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 16,
fontWeight: FontWeight.w500,
color: Color(0xFF5F5F5F),
color: capacityColor,
),
),
],
+88 -45
View File
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../utils/charge_utils.dart';
import '../theme/app_colors.dart';
class ChargeDetailPage extends StatelessWidget {
final String chargeType;
@@ -9,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,
@@ -18,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() {
@@ -41,61 +48,71 @@ class ChargeDetailPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final c = context.colors;
final isDark = Theme.of(context).brightness == Brightness.dark;
return AnnotatedRegion<SystemUiOverlayStyle>(
value: const SystemUiOverlayStyle(
statusBarColor: Color.fromRGBO(157, 212, 237, 0.1),
statusBarIconBrightness: Brightness.dark,
statusBarBrightness: Brightness.dark,
value: SystemUiOverlayStyle(
statusBarColor: c.statusBarTint,
statusBarIconBrightness: isDark ? Brightness.light : Brightness.dark,
statusBarBrightness: Theme.of(context).brightness,
),
child: Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: const Text('充电详情'),
backgroundColor: Colors.white,
backgroundColor: c.background,
elevation: 0,
scrolledUnderElevation: 0,
shadowColor: Colors.transparent,
surfaceTintColor: Colors.transparent,
foregroundColor: Colors.black,
iconTheme: const IconThemeData(color: Colors.black),
),
body: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildTextRow('协议', _getProtocolDisplay()),
// ========== BMS 来源 ==========
_buildSectionTitle(context, 'BMS 日志'),
_buildTextRow(context, '协议', _getProtocolDisplay()),
const Divider(),
if (!isWireless) ...[
_buildTextRow('标定档位', _getRequestedDisplay()),
_buildTextRow(
context,
'标定档位',
_getRequestedDisplay(),
valueFont: 'Flyme',
),
const Divider(),
_buildTextRow('实际功率', _getActualDisplay()),
_buildTextRow(
context,
'实际功率',
_getActualDisplay(),
valueFont: 'Flyme',
),
const Divider(),
] else ...[
_buildTextRow('功率', ChargeUtils.getDisplayPowerWireless(power)),
_buildTextRow(
context,
'功率',
ChargeUtils.getDisplayPowerWireless(power),
valueFont: 'Flyme',
),
const Divider(),
],
_buildDetailRow('电压', volt, 'V'),
_buildDetailRow(context, '电压', volt, 'V'),
const Divider(),
_buildDetailRow('电流', curr, 'A'),
_buildDetailRow(context, '电流', curr, 'A'),
const Divider(),
if (isWireless) _buildTextRow('类型', '无线'),
if (!isWireless && wiredLevel == null) _buildTextRow('档位', '自动'),
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: Colors.grey.shade500,
),
),
),
],
),
),
@@ -103,8 +120,29 @@ class ChargeDetailPage extends StatelessWidget {
);
}
/// 纯文本行(协议、功率、档位等)
Widget _buildTextRow(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),
child: Row(
@@ -112,20 +150,20 @@ class ChargeDetailPage extends StatelessWidget {
children: [
Text(
label,
style: const TextStyle(
style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 16,
fontWeight: FontWeight.w400,
color: Colors.grey,
color: c.textTertiary,
),
),
Text(
value,
style: const TextStyle(
fontFamily: 'HarmonyOS_Sans_SC', // 复杂字符串保持 Harmony
style: TextStyle(
fontFamily: valueFont,
fontSize: 16,
fontWeight: FontWeight.w500,
color: Colors.black87,
color: c.textPrimary,
),
),
],
@@ -133,8 +171,13 @@ class ChargeDetailPage extends StatelessWidget {
);
}
/// 数值行(电压/电流,数字用 Flyme,单位用 Harmony)
Widget _buildDetailRow(String label, double value, String unit) {
Widget _buildDetailRow(
BuildContext context,
String label,
double value,
String unit,
) {
final c = context.colors;
return Padding(
padding: const EdgeInsets.symmetric(vertical: 6.0),
child: Row(
@@ -142,24 +185,24 @@ class ChargeDetailPage extends StatelessWidget {
children: [
Text(
label,
style: const TextStyle(
style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 16,
fontWeight: FontWeight.w400,
color: Colors.grey,
color: c.textTertiary,
),
),
RichText(
text: TextSpan(
style: const TextStyle(
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Colors.black87,
color: c.textPrimary,
),
children: [
TextSpan(
text: value.toStringAsFixed(2),
style: const TextStyle(fontFamily: 'Flyme'), // 🟢 数字用 Flyme
style: const TextStyle(fontFamily: 'Flyme'),
),
TextSpan(
text: unit,
+65 -9
View File
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import '../theme/app_colors.dart';
class ChargeEntry extends StatelessWidget {
final String summary;
@@ -14,18 +15,23 @@ class ChargeEntry extends StatelessWidget {
@override
Widget build(BuildContext context) {
final c = context.colors;
return GestureDetector(
onTap: isCharging ? onTap : null,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
summary,
style: const TextStyle(
fontFamily: 'HarmonyOS_Sans_SC', // 🟢 混合文本用 Harmony
fontSize: 16,
fontWeight: FontWeight.w500,
color: Colors.black87,
Flexible(
child: RichText(
overflow: TextOverflow.ellipsis,
text: TextSpan(
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: c.textPrimary,
),
children: _buildSpans(summary),
),
),
),
Row(
@@ -36,7 +42,7 @@ class ChargeEntry extends StatelessWidget {
fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 16,
fontWeight: FontWeight.w500,
color: Colors.grey.shade700,
color: c.textTertiary,
),
),
const SizedBox(width: 4),
@@ -46,7 +52,7 @@ class ChargeEntry extends StatelessWidget {
fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 16,
fontWeight: FontWeight.w500,
color: Colors.grey.shade700,
color: c.textTertiary,
),
),
],
@@ -55,4 +61,54 @@ class ChargeEntry extends StatelessWidget {
),
);
}
/// 按 CJK / 非 CJK 拆分字符串,分别应用 HarmonyOS / Flyme
List<TextSpan> _buildSpans(String text) {
final spans = <TextSpan>[];
if (text.isEmpty) return spans;
final buffer = StringBuffer();
bool? isCJK;
void flush() {
if (buffer.isEmpty) return;
spans.add(
TextSpan(
text: buffer.toString(),
style: TextStyle(
fontFamily: (isCJK ?? false) ? 'HarmonyOS_Sans_SC' : 'Flyme',
),
),
);
buffer.clear();
}
for (final rune in text.runes) {
final cjk = _isCJKRune(rune);
if (isCJK == null || cjk == isCJK) {
buffer.writeCharCode(rune);
isCJK = cjk;
} else {
flush();
buffer.writeCharCode(rune);
isCJK = cjk;
}
}
flush();
return spans;
}
bool _isCJKRune(int rune) {
// CJK 统一表意文字
if (rune >= 0x4E00 && rune <= 0x9FFF) return true;
// CJK 扩展 A
if (rune >= 0x3400 && rune <= 0x4DBF) return true;
// 中文标点(。、,:;等)
if (rune >= 0x3000 && rune <= 0x303F) return true;
// CJK 兼容符号
if (rune >= 0xFE30 && rune <= 0xFE4F) return true;
// 全角字符
if (rune >= 0xFF00 && rune <= 0xFFEF) return true;
return false;
}
}
+19 -21
View File
@@ -1,11 +1,6 @@
import 'package:flutter/material.dart';
import '../theme/app_colors.dart';
// --- 潘通色定义 ---
const Color kHealthGreen = Color(0xFF00B140);
const Color kHealthYellow = Color(0xFFEFDF00);
const Color kHealthOrange = Color(0xFFD75A16);
/// 趋势图柱状图组件
class TrendChart extends StatelessWidget {
final List<Map<String, dynamic>> data;
final int designCap;
@@ -14,6 +9,8 @@ class TrendChart extends StatelessWidget {
@override
Widget build(BuildContext context) {
final c = context.colors;
List<Map<String, dynamic>> displayData = data.isNotEmpty
? data
: _getMockData();
@@ -48,12 +45,13 @@ class TrendChart extends StatelessWidget {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
Text(
'容量百分比变动趋势(近15天)',
style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 16,
fontWeight: FontWeight.w500,
color: c.textPrimary,
),
),
const SizedBox(height: 8),
@@ -64,13 +62,13 @@ class TrendChart extends StatelessWidget {
height: 180,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: const [
children: [
Text(
'100',
style: TextStyle(
fontFamily: 'Flyme',
fontSize: 10,
color: Colors.grey,
color: c.textTertiary,
height: 1.0,
),
),
@@ -79,7 +77,7 @@ class TrendChart extends StatelessWidget {
style: TextStyle(
fontFamily: 'Flyme',
fontSize: 10,
color: Colors.grey,
color: c.textTertiary,
height: 1.0,
),
),
@@ -106,14 +104,14 @@ class TrendChart extends StatelessWidget {
Color barColor;
if (value > 0) {
if (value >= 80) {
barColor = kHealthGreen;
barColor = c.healthGreen;
} else if (value >= 70) {
barColor = kHealthYellow;
barColor = c.healthYellow;
} else {
barColor = Colors.red;
barColor = c.healthRed;
}
} else {
barColor = Colors.grey.shade300;
barColor = c.barEmpty;
}
double clampedValue = value.clamp(70.0, 100.0);
double heightFactor =
@@ -144,10 +142,10 @@ class TrendChart extends StatelessWidget {
bottom: 2,
child: Text(
sevenDaysAgoDate,
style: const TextStyle(
style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 9,
color: Colors.grey,
color: c.textTertiary,
height: 1.0,
),
),
@@ -158,10 +156,10 @@ class TrendChart extends StatelessWidget {
bottom: 2,
child: Text(
todayDate,
style: const TextStyle(
style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 9,
color: Colors.black,
color: c.textPrimary,
height: 1.0,
fontWeight: FontWeight.w600,
),
@@ -176,14 +174,14 @@ class TrendChart extends StatelessWidget {
],
),
if (percents.every((v) => v == 0))
const Padding(
padding: EdgeInsets.only(top: 8.0),
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Text(
'暂无历史数据,请点击刷新按钮更新',
style: TextStyle(
fontFamily: 'HarmonyOS_Sans_SC',
fontSize: 12,
color: Colors.grey,
color: c.textTertiary,
),
),
),
-1
View File
@@ -1 +0,0 @@
flutter/ephemeral
-128
View File
@@ -1,128 +0,0 @@
# Project-level configuration.
cmake_minimum_required(VERSION 3.13)
project(runner LANGUAGES CXX)
# The name of the executable created for the application. Change this to change
# the on-disk name of your application.
set(BINARY_NAME "battery_health")
# The unique GTK application identifier for this application. See:
# https://wiki.gnome.org/HowDoI/ChooseApplicationID
set(APPLICATION_ID "com.lxh.battery_health")
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
# versions of CMake.
cmake_policy(SET CMP0063 NEW)
# Load bundled libraries from the lib/ directory relative to the binary.
set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
# Root filesystem for cross-building.
if(FLUTTER_TARGET_PLATFORM_SYSROOT)
set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})
set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
endif()
# Define build configuration options.
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE "Debug" CACHE
STRING "Flutter build mode" FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
"Debug" "Profile" "Release")
endif()
# Compilation settings that should be applied to most targets.
#
# Be cautious about adding new options here, as plugins use this function by
# default. In most cases, you should add new options to specific targets instead
# of modifying this function.
function(APPLY_STANDARD_SETTINGS TARGET)
target_compile_features(${TARGET} PUBLIC cxx_std_14)
target_compile_options(${TARGET} PRIVATE -Wall -Werror)
target_compile_options(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:-O3>")
target_compile_definitions(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:NDEBUG>")
endfunction()
# Flutter library and tool build rules.
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
add_subdirectory(${FLUTTER_MANAGED_DIR})
# System-level dependencies.
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
# Application build; see runner/CMakeLists.txt.
add_subdirectory("runner")
# Run the Flutter tool portions of the build. This must not be removed.
add_dependencies(${BINARY_NAME} flutter_assemble)
# Only the install-generated bundle's copy of the executable will launch
# correctly, since the resources must in the right relative locations. To avoid
# people trying to run the unbundled copy, put it in a subdirectory instead of
# the default top-level location.
set_target_properties(${BINARY_NAME}
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
)
# Generated plugin build rules, which manage building the plugins and adding
# them to the application.
include(flutter/generated_plugins.cmake)
# === Installation ===
# By default, "installing" just makes a relocatable bundle in the build
# directory.
set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
endif()
# Start with a clean build bundle directory every time.
install(CODE "
file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
" COMPONENT Runtime)
set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
COMPONENT Runtime)
install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
COMPONENT Runtime)
install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES})
install(FILES "${bundled_library}"
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
endforeach(bundled_library)
# Copy the native assets provided by the build.dart from all packages.
set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/")
install(DIRECTORY "${NATIVE_ASSETS_DIR}"
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
# Fully re-copy the assets directory on each build to avoid having stale files
# from a previous install.
set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
install(CODE "
file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
" COMPONENT Runtime)
install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
# Install the AOT library on non-Debug builds only.
if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
endif()
-88
View File
@@ -1,88 +0,0 @@
# This file controls Flutter-level build steps. It should not be edited.
cmake_minimum_required(VERSION 3.10)
set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
# Configuration provided via flutter tool.
include(${EPHEMERAL_DIR}/generated_config.cmake)
# TODO: Move the rest of this into files in ephemeral. See
# https://github.com/flutter/flutter/issues/57146.
# Serves the same purpose as list(TRANSFORM ... PREPEND ...),
# which isn't available in 3.10.
function(list_prepend LIST_NAME PREFIX)
set(NEW_LIST "")
foreach(element ${${LIST_NAME}})
list(APPEND NEW_LIST "${PREFIX}${element}")
endforeach(element)
set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE)
endfunction()
# === Flutter Library ===
# System-level dependencies.
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so")
# Published to parent scope for install step.
set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE)
list(APPEND FLUTTER_LIBRARY_HEADERS
"fl_basic_message_channel.h"
"fl_binary_codec.h"
"fl_binary_messenger.h"
"fl_dart_project.h"
"fl_engine.h"
"fl_json_message_codec.h"
"fl_json_method_codec.h"
"fl_message_codec.h"
"fl_method_call.h"
"fl_method_channel.h"
"fl_method_codec.h"
"fl_method_response.h"
"fl_plugin_registrar.h"
"fl_plugin_registry.h"
"fl_standard_message_codec.h"
"fl_standard_method_codec.h"
"fl_string_codec.h"
"fl_value.h"
"fl_view.h"
"flutter_linux.h"
)
list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/")
add_library(flutter INTERFACE)
target_include_directories(flutter INTERFACE
"${EPHEMERAL_DIR}"
)
target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}")
target_link_libraries(flutter INTERFACE
PkgConfig::GTK
PkgConfig::GLIB
PkgConfig::GIO
)
add_dependencies(flutter flutter_assemble)
# === Flutter tool backend ===
# _phony_ is a non-existent file to force this command to run every time,
# since currently there's no way to get a full input/output list from the
# flutter tool.
add_custom_command(
OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
${CMAKE_CURRENT_BINARY_DIR}/_phony_
COMMAND ${CMAKE_COMMAND} -E env
${FLUTTER_TOOL_ENVIRONMENT}
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE}
VERBATIM
)
add_custom_target(flutter_assemble DEPENDS
"${FLUTTER_LIBRARY}"
${FLUTTER_LIBRARY_HEADERS}
)
@@ -1,11 +0,0 @@
//
// Generated file. Do not edit.
//
// clang-format off
#include "generated_plugin_registrant.h"
void fl_register_plugins(FlPluginRegistry* registry) {
}
@@ -1,15 +0,0 @@
//
// Generated file. Do not edit.
//
// clang-format off
#ifndef GENERATED_PLUGIN_REGISTRANT_
#define GENERATED_PLUGIN_REGISTRANT_
#include <flutter_linux/flutter_linux.h>
// Registers Flutter plugins.
void fl_register_plugins(FlPluginRegistry* registry);
#endif // GENERATED_PLUGIN_REGISTRANT_
-24
View File
@@ -1,24 +0,0 @@
#
# Generated file, do not edit.
#
list(APPEND FLUTTER_PLUGIN_LIST
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
jni
)
set(PLUGIN_BUNDLED_LIBRARIES)
foreach(plugin ${FLUTTER_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
endforeach(plugin)
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
endforeach(ffi_plugin)
-26
View File
@@ -1,26 +0,0 @@
cmake_minimum_required(VERSION 3.13)
project(runner LANGUAGES CXX)
# Define the application target. To change its name, change BINARY_NAME in the
# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer
# work.
#
# Any new source files that you add to the application should be added here.
add_executable(${BINARY_NAME}
"main.cc"
"my_application.cc"
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
)
# Apply the standard set of build settings. This can be removed for applications
# that need different build settings.
apply_standard_settings(${BINARY_NAME})
# Add preprocessor definitions for the application ID.
add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
# Add dependency libraries. Add any application-specific dependencies here.
target_link_libraries(${BINARY_NAME} PRIVATE flutter)
target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
-6
View File
@@ -1,6 +0,0 @@
#include "my_application.h"
int main(int argc, char** argv) {
g_autoptr(MyApplication) app = my_application_new();
return g_application_run(G_APPLICATION(app), argc, argv);
}
-144
View File
@@ -1,144 +0,0 @@
#include "my_application.h"
#include <flutter_linux/flutter_linux.h>
#ifdef GDK_WINDOWING_X11
#include <gdk/gdkx.h>
#endif
#include "flutter/generated_plugin_registrant.h"
struct _MyApplication {
GtkApplication parent_instance;
char** dart_entrypoint_arguments;
};
G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
// Called when first Flutter frame received.
static void first_frame_cb(MyApplication* self, FlView *view)
{
gtk_widget_show(gtk_widget_get_toplevel(GTK_WIDGET(view)));
}
// Implements GApplication::activate.
static void my_application_activate(GApplication* application) {
MyApplication* self = MY_APPLICATION(application);
GtkWindow* window =
GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
// Use a header bar when running in GNOME as this is the common style used
// by applications and is the setup most users will be using (e.g. Ubuntu
// desktop).
// If running on X and not using GNOME then just use a traditional title bar
// in case the window manager does more exotic layout, e.g. tiling.
// If running on Wayland assume the header bar will work (may need changing
// if future cases occur).
gboolean use_header_bar = TRUE;
#ifdef GDK_WINDOWING_X11
GdkScreen* screen = gtk_window_get_screen(window);
if (GDK_IS_X11_SCREEN(screen)) {
const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);
if (g_strcmp0(wm_name, "GNOME Shell") != 0) {
use_header_bar = FALSE;
}
}
#endif
if (use_header_bar) {
GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
gtk_widget_show(GTK_WIDGET(header_bar));
gtk_header_bar_set_title(header_bar, "battery_health");
gtk_header_bar_set_show_close_button(header_bar, TRUE);
gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
} else {
gtk_window_set_title(window, "battery_health");
}
gtk_window_set_default_size(window, 1280, 720);
g_autoptr(FlDartProject) project = fl_dart_project_new();
fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments);
FlView* view = fl_view_new(project);
GdkRGBA background_color;
// Background defaults to black, override it here if necessary, e.g. #00000000 for transparent.
gdk_rgba_parse(&background_color, "#000000");
fl_view_set_background_color(view, &background_color);
gtk_widget_show(GTK_WIDGET(view));
gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));
// Show the window when Flutter renders.
// Requires the view to be realized so we can start rendering.
g_signal_connect_swapped(view, "first-frame", G_CALLBACK(first_frame_cb), self);
gtk_widget_realize(GTK_WIDGET(view));
fl_register_plugins(FL_PLUGIN_REGISTRY(view));
gtk_widget_grab_focus(GTK_WIDGET(view));
}
// Implements GApplication::local_command_line.
static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) {
MyApplication* self = MY_APPLICATION(application);
// Strip out the first argument as it is the binary name.
self->dart_entrypoint_arguments = g_strdupv(*arguments + 1);
g_autoptr(GError) error = nullptr;
if (!g_application_register(application, nullptr, &error)) {
g_warning("Failed to register: %s", error->message);
*exit_status = 1;
return TRUE;
}
g_application_activate(application);
*exit_status = 0;
return TRUE;
}
// Implements GApplication::startup.
static void my_application_startup(GApplication* application) {
//MyApplication* self = MY_APPLICATION(object);
// Perform any actions required at application startup.
G_APPLICATION_CLASS(my_application_parent_class)->startup(application);
}
// Implements GApplication::shutdown.
static void my_application_shutdown(GApplication* application) {
//MyApplication* self = MY_APPLICATION(object);
// Perform any actions required at application shutdown.
G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application);
}
// Implements GObject::dispose.
static void my_application_dispose(GObject* object) {
MyApplication* self = MY_APPLICATION(object);
g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);
G_OBJECT_CLASS(my_application_parent_class)->dispose(object);
}
static void my_application_class_init(MyApplicationClass* klass) {
G_APPLICATION_CLASS(klass)->activate = my_application_activate;
G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line;
G_APPLICATION_CLASS(klass)->startup = my_application_startup;
G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown;
G_OBJECT_CLASS(klass)->dispose = my_application_dispose;
}
static void my_application_init(MyApplication* self) {}
MyApplication* my_application_new() {
// Set the program name to the application ID, which helps various systems
// like GTK and desktop environments map this running application to its
// corresponding .desktop file. This ensures better integration by allowing
// the application to be recognized beyond its binary name.
g_set_prgname(APPLICATION_ID);
return MY_APPLICATION(g_object_new(my_application_get_type(),
"application-id", APPLICATION_ID,
"flags", G_APPLICATION_NON_UNIQUE,
nullptr));
}
-18
View File
@@ -1,18 +0,0 @@
#ifndef FLUTTER_MY_APPLICATION_H_
#define FLUTTER_MY_APPLICATION_H_
#include <gtk/gtk.h>
G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION,
GtkApplication)
/**
* my_application_new:
*
* Creates a new Flutter-based application.
*
* Returns: a new #MyApplication.
*/
MyApplication* my_application_new();
#endif // FLUTTER_MY_APPLICATION_H_
-7
View File
@@ -1,7 +0,0 @@
# Flutter-related
**/Flutter/ephemeral/
**/Pods/
# Xcode-related
**/dgph
**/xcuserdata/
-1
View File
@@ -1 +0,0 @@
#include "ephemeral/Flutter-Generated.xcconfig"
-1
View File
@@ -1 +0,0 @@
#include "ephemeral/Flutter-Generated.xcconfig"
@@ -1,16 +0,0 @@
//
// Generated file. Do not edit.
//
import FlutterMacOS
import Foundation
import device_info_plus
import shared_preferences_foundation
import sqflite_darwin
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
}
-705
View File
@@ -1,705 +0,0 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objects = {
/* Begin PBXAggregateTarget section */
33CC111A2044C6BA0003C045 /* Flutter Assemble */ = {
isa = PBXAggregateTarget;
buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */;
buildPhases = (
33CC111E2044C6BF0003C045 /* ShellScript */,
);
dependencies = (
);
name = "Flutter Assemble";
productName = FLX;
};
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; };
335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };
33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 33CC10EC2044A3C60003C045;
remoteInfo = Runner;
};
33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 33CC111A2044C6BA0003C045;
remoteInfo = FLX;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
33CC110E2044A8840003C045 /* Bundle Framework */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
);
name = "Bundle Framework";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
33CC10ED2044A3C60003C045 /* battery_health.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "battery_health.app"; sourceTree = BUILT_PRODUCTS_DIR; };
33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = "<group>"; };
33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = "<group>"; };
33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = "<group>"; };
33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = "<group>"; };
33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = "<group>"; };
33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = "<group>"; };
33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; };
33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
331C80D2294CF70F00263BE5 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
33CC10EA2044A3C60003C045 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
331C80D6294CF71000263BE5 /* RunnerTests */ = {
isa = PBXGroup;
children = (
331C80D7294CF71000263BE5 /* RunnerTests.swift */,
);
path = RunnerTests;
sourceTree = "<group>";
};
33BA886A226E78AF003329D5 /* Configs */ = {
isa = PBXGroup;
children = (
33E5194F232828860026EE4D /* AppInfo.xcconfig */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
333000ED22D3DE5D00554162 /* Warnings.xcconfig */,
);
path = Configs;
sourceTree = "<group>";
};
33CC10E42044A3C60003C045 = {
isa = PBXGroup;
children = (
33FAB671232836740065AC1E /* Runner */,
33CEB47122A05771004F2AC0 /* Flutter */,
331C80D6294CF71000263BE5 /* RunnerTests */,
33CC10EE2044A3C60003C045 /* Products */,
D73912EC22F37F3D000D13A0 /* Frameworks */,
);
sourceTree = "<group>";
};
33CC10EE2044A3C60003C045 /* Products */ = {
isa = PBXGroup;
children = (
33CC10ED2044A3C60003C045 /* battery_health.app */,
331C80D5294CF71000263BE5 /* RunnerTests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
33CC11242044D66E0003C045 /* Resources */ = {
isa = PBXGroup;
children = (
33CC10F22044A3C60003C045 /* Assets.xcassets */,
33CC10F42044A3C60003C045 /* MainMenu.xib */,
33CC10F72044A3C60003C045 /* Info.plist */,
);
name = Resources;
path = ..;
sourceTree = "<group>";
};
33CEB47122A05771004F2AC0 /* Flutter */ = {
isa = PBXGroup;
children = (
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */,
33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */,
33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */,
33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */,
);
path = Flutter;
sourceTree = "<group>";
};
33FAB671232836740065AC1E /* Runner */ = {
isa = PBXGroup;
children = (
33CC10F02044A3C60003C045 /* AppDelegate.swift */,
33CC11122044BFA00003C045 /* MainFlutterWindow.swift */,
33E51913231747F40026EE4D /* DebugProfile.entitlements */,
33E51914231749380026EE4D /* Release.entitlements */,
33CC11242044D66E0003C045 /* Resources */,
33BA886A226E78AF003329D5 /* Configs */,
);
path = Runner;
sourceTree = "<group>";
};
D73912EC22F37F3D000D13A0 /* Frameworks */ = {
isa = PBXGroup;
children = (
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
331C80D4294CF70F00263BE5 /* RunnerTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
buildPhases = (
331C80D1294CF70F00263BE5 /* Sources */,
331C80D2294CF70F00263BE5 /* Frameworks */,
331C80D3294CF70F00263BE5 /* Resources */,
);
buildRules = (
);
dependencies = (
331C80DA294CF71000263BE5 /* PBXTargetDependency */,
);
name = RunnerTests;
productName = RunnerTests;
productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
33CC10EC2044A3C60003C045 /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
33CC10E92044A3C60003C045 /* Sources */,
33CC10EA2044A3C60003C045 /* Frameworks */,
33CC10EB2044A3C60003C045 /* Resources */,
33CC110E2044A8840003C045 /* Bundle Framework */,
3399D490228B24CF009A79C7 /* ShellScript */,
);
buildRules = (
);
dependencies = (
33CC11202044C79F0003C045 /* PBXTargetDependency */,
);
name = Runner;
productName = Runner;
productReference = 33CC10ED2044A3C60003C045 /* battery_health.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
33CC10E52044A3C60003C045 /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = YES;
LastSwiftUpdateCheck = 0920;
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
331C80D4294CF70F00263BE5 = {
CreatedOnToolsVersion = 14.0;
TestTargetID = 33CC10EC2044A3C60003C045;
};
33CC10EC2044A3C60003C045 = {
CreatedOnToolsVersion = 9.2;
LastSwiftMigration = 1100;
ProvisioningStyle = Automatic;
SystemCapabilities = {
com.apple.Sandbox = {
enabled = 1;
};
};
};
33CC111A2044C6BA0003C045 = {
CreatedOnToolsVersion = 9.2;
ProvisioningStyle = Manual;
};
};
};
buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 33CC10E42044A3C60003C045;
productRefGroup = 33CC10EE2044A3C60003C045 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
33CC10EC2044A3C60003C045 /* Runner */,
331C80D4294CF70F00263BE5 /* RunnerTests */,
33CC111A2044C6BA0003C045 /* Flutter Assemble */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
331C80D3294CF70F00263BE5 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
33CC10EB2044A3C60003C045 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */,
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
3399D490228B24CF009A79C7 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n";
};
33CC111E2044C6BF0003C045 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
Flutter/ephemeral/FlutterInputs.xcfilelist,
);
inputPaths = (
Flutter/ephemeral/tripwire,
);
outputFileListPaths = (
Flutter/ephemeral/FlutterOutputs.xcfilelist,
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
331C80D1294CF70F00263BE5 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
33CC10E92044A3C60003C045 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */,
33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */,
335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
331C80DA294CF71000263BE5 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 33CC10EC2044A3C60003C045 /* Runner */;
targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */;
};
33CC11202044C79F0003C045 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */;
targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
33CC10F42044A3C60003C045 /* MainMenu.xib */ = {
isa = PBXVariantGroup;
children = (
33CC10F52044A3C60003C045 /* Base */,
);
name = MainMenu.xib;
path = Runner;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
331C80DB294CF71000263BE5 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.example.batteryHealth.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/battery_health.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/battery_health";
};
name = Debug;
};
331C80DC294CF71000263BE5 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.example.batteryHealth.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/battery_health.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/battery_health";
};
name = Release;
};
331C80DD294CF71000263BE5 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.example.batteryHealth.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/battery_health.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/battery_health";
};
name = Profile;
};
338D0CE9231458BD00FA5F75 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.15;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
};
name = Profile;
};
338D0CEA231458BD00FA5F75 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0;
};
name = Profile;
};
338D0CEB231458BD00FA5F75 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Manual;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Profile;
};
33CC10F92044A3C60003C045 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.15;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
33CC10FA2044A3C60003C045 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.15;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
};
name = Release;
};
33CC10FC2044A3C60003C045 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
};
name = Debug;
};
33CC10FD2044A3C60003C045 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0;
};
name = Release;
};
33CC111C2044C6BA0003C045 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Manual;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
33CC111D2044C6BA0003C045 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
331C80DB294CF71000263BE5 /* Debug */,
331C80DC294CF71000263BE5 /* Release */,
331C80DD294CF71000263BE5 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
33CC10F92044A3C60003C045 /* Debug */,
33CC10FA2044A3C60003C045 /* Release */,
338D0CE9231458BD00FA5F75 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
33CC10FC2044A3C60003C045 /* Debug */,
33CC10FD2044A3C60003C045 /* Release */,
338D0CEA231458BD00FA5F75 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = {
isa = XCConfigurationList;
buildConfigurations = (
33CC111C2044C6BA0003C045 /* Debug */,
33CC111D2044C6BA0003C045 /* Release */,
338D0CEB231458BD00FA5F75 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 33CC10E52044A3C60003C045 /* Project object */;
}
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
@@ -1,99 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1510"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
BuildableName = "battery_health.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
BuildableName = "battery_health.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "331C80D4294CF70F00263BE5"
BuildableName = "RunnerTests.xctest"
BlueprintName = "RunnerTests"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
enableGPUValidationMode = "1"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
BuildableName = "battery_health.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Profile"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
BuildableName = "battery_health.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
-7
View File
@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
</Workspace>
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
-13
View File
@@ -1,13 +0,0 @@
import Cocoa
import FlutterMacOS
@main
class AppDelegate: FlutterAppDelegate {
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
return true
}
override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
return true
}
}
@@ -1,68 +0,0 @@
{
"images" : [
{
"size" : "16x16",
"idiom" : "mac",
"filename" : "app_icon_16.png",
"scale" : "1x"
},
{
"size" : "16x16",
"idiom" : "mac",
"filename" : "app_icon_32.png",
"scale" : "2x"
},
{
"size" : "32x32",
"idiom" : "mac",
"filename" : "app_icon_32.png",
"scale" : "1x"
},
{
"size" : "32x32",
"idiom" : "mac",
"filename" : "app_icon_64.png",
"scale" : "2x"
},
{
"size" : "128x128",
"idiom" : "mac",
"filename" : "app_icon_128.png",
"scale" : "1x"
},
{
"size" : "128x128",
"idiom" : "mac",
"filename" : "app_icon_256.png",
"scale" : "2x"
},
{
"size" : "256x256",
"idiom" : "mac",
"filename" : "app_icon_256.png",
"scale" : "1x"
},
{
"size" : "256x256",
"idiom" : "mac",
"filename" : "app_icon_512.png",
"scale" : "2x"
},
{
"size" : "512x512",
"idiom" : "mac",
"filename" : "app_icon_512.png",
"scale" : "1x"
},
{
"size" : "512x512",
"idiom" : "mac",
"filename" : "app_icon_1024.png",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 520 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Some files were not shown because too many files have changed in this diff Show More