diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 1fb10b1..15dc0cc 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,6 +1,6 @@ - + double _maxVolt = 0; double _minVolt = 0; bool _hasData = false; - final String _filePath = '/storage/emulated/0/mbattery_charger/'; // ---------- 趋势图数据 ---------- List> _trendData = []; // ---------- 充电状态 ---------- bool _isCharging = false; - String _chargeType = ''; // "EPP", "PPS", "SDP", "无线", "MZ_EPP" 等 + String _chargeType = ''; double _chargePower = 0.0; double _chargeVolt = 0.0; double _chargeCurr = 0.0; int? _wiredLevel; bool _isWireless = false; - - // 🟢 1.3.0-dev.3: 充电开始时间(UTC),用于时间窗口判断 DateTime? _chargingStartTime; + // ---------- SAF 状态 ---------- + final SafStorageService _safService = SafStorageService(); + SafPermissionState _safState = SafPermissionState.unauthorized; + // 动画控制 late AnimationController _chargeAnimationController; late Animation _bottomSlide; @@ -150,22 +152,25 @@ class _BatteryHealthPageState extends State } } - // ---------- 扫描目录 ---------- + // ---------- 扫描目录 (SAF 模式) ---------- Future _scanAndUpdateDB() async { - Directory dir = Directory(_filePath); - if (!await dir.exists()) return; + // 检查 SAF 授权状态 + _safState = await _safService.getPermissionState(); + if (_safState != SafPermissionState.authorized) { + _showSafAuthorizationDialog(); + return; + } - List files = await dir.list().toList(); - RegExp filePattern = RegExp(r'mbattery_charger_log_(\d{4}_\d{2}_\d{2})$'); + try { + final files = await _safService.listFiles(); + RegExp filePattern = RegExp(r'mbattery_charger_log_(\d{4}_\d{2}_\d{2})$'); - for (FileSystemEntity entity in files) { - if (entity is File) { - String fileName = path.basename(entity.path); + for (String fileName in files) { Match? match = filePattern.firstMatch(fileName); if (match != null) { String dateStr = match.group(1)!.replaceAll('_', '-'); try { - List lines = await entity.readAsLines(); + List lines = await _safService.readLogFile(fileName); if (lines.isEmpty) continue; List validLines = lines @@ -195,10 +200,12 @@ class _BatteryHealthPageState extends State } catch (_) {} } } + } catch (e) { + dev.log('[_scanAndUpdateDB] ❌ 扫描异常: $e', name: 'BatteryHealth'); } } - // ---------- 1.3.0-dev.1: 使用 UTC 生成文件名 ---------- + // ---------- UTC 文件名 ---------- String getTodayFileName() { DateTime nowUtc = DateTime.now().toUtc(); String year = nowUtc.year.toString(); @@ -208,7 +215,6 @@ class _BatteryHealthPageState extends State } // ---------- 时间戳工具方法 ---------- - /// 解析日志行中的时间戳,强制解析为 UTC DateTime? _parseLogTimestamp(String line) { RegExp reg = RegExp(r'\[(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2})\]'); Match? match = reg.firstMatch(line); @@ -231,20 +237,79 @@ class _BatteryHealthPageState extends State } } - /// 🟢 1.3.0-dev.3: 时间窗口以插入时刻为基准,只向前看 bool _isLogTimestampValid(DateTime? logTime) { if (logTime == null) return false; if (_chargingStartTime == null) { - // 边缘情况:回退到当前时间 ±3 分钟 DateTime nowUtc = DateTime.now().toUtc(); Duration diff = nowUtc.difference(logTime); return diff.abs().inMinutes <= 3; } - // 日志时间必须在 [充电开始时间, 充电开始时间 + 3 分钟] 范围内 DateTime endTime = _chargingStartTime!.add(const Duration(minutes: 3)); return logTime.isAfter(_chargingStartTime!) && logTime.isBefore(endTime); } + // ---------- SAF 授权引导 ---------- + void _showSafAuthorizationDialog() { + if (!mounted) return; + + showDialog( + context: context, + barrierDismissible: false, + builder: (context) => AlertDialog( + title: const Text('需要文件夹访问权限'), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: const [ + Text('请授权访问日志文件夹,以读取电池数据。'), + SizedBox(height: 8), + Text( + '目标路径:\n/storage/emulated/0/mbattery_charger/', + style: TextStyle(fontSize: 12, color: Colors.grey), + ), + SizedBox(height: 8), + Text('点击"选择文件夹"后,请在系统文件管理器中找到并选中上述文件夹。'), + ], + ), + actions: [ + TextButton( + onPressed: () { + Navigator.pop(context); + setState(() { + _safState = SafPermissionState.denied; + }); + }, + child: const Text('取消'), + ), + ElevatedButton( + onPressed: () async { + final ctx = context; + Navigator.pop(ctx); + final result = await _safService.requestPermission(); + if (result.success) { + if (!mounted) return; + setState(() { + _safState = SafPermissionState.authorized; + }); + readBatteryCapacity(); + } else { + if (!mounted) return; + ScaffoldMessenger.of(ctx).showSnackBar( + SnackBar( + content: Text(result.errorMessage ?? '授权失败,请重试'), + backgroundColor: Colors.red, + ), + ); + _showSafAuthorizationDialog(); + } + }, + child: const Text('选择文件夹'), // 🟢 加这行 + ), + ], + ), + ); + } + // ---------- 核心:系统API定调 ---------- void _applySystemChargeState(bool isCharging, String systemChargeType) { dev.log( @@ -263,12 +328,11 @@ class _BatteryHealthPageState extends State _chargeVolt = 0.0; _chargeCurr = 0.0; _wiredLevel = null; - _chargingStartTime = null; // 🟢 清空开始时间 + _chargingStartTime = null; _updateChargeState(); return; } - // 🟢 1.3.0-dev.3: 记录充电开始时刻(仅当从断开状态变为充电状态时) if (_chargingStartTime == null) { _chargingStartTime = DateTime.now().toUtc(); dev.log( @@ -297,7 +361,6 @@ class _BatteryHealthPageState extends State _isWireless = false; _chargeType = "SDP"; } else { - // UNKNOWN 时清空旧类型,等待日志修正 dev.log( '[_applySystemChargeState] ⚠️ 未知类型: $systemChargeType,清空类型等待日志', name: 'BatteryHealth', @@ -324,7 +387,6 @@ class _BatteryHealthPageState extends State name: 'BatteryHealth', ); - // ---------- 1. 收集所有在有效时间窗口内的数据行 ---------- List> validLines = []; for (String line in lines) { DateTime? timestamp = _parseLogTimestamp(line); @@ -349,10 +411,8 @@ class _BatteryHealthPageState extends State return; } - // 按时间戳排序(从新到旧) validLines.sort((a, b) => b['timestamp'].compareTo(a['timestamp'])); - // 取最新的一条 Map latest = validLines.first; String latestLine = latest['line']; String latestType = latest['type']; @@ -363,7 +423,7 @@ class _BatteryHealthPageState extends State name: 'BatteryHealth', ); - // ---------- 2. 无线处理 ---------- + // ---------- 无线 ---------- if (latestType == 'wls') { dev.log('[_parseChargeDataFromLog] 处理无线数据', name: 'BatteryHealth'); @@ -390,7 +450,7 @@ class _BatteryHealthPageState extends State return; } - // ---------- 3. 有线处理 ---------- + // ---------- 有线 ---------- if (latestType == 'usb') { dev.log('[_parseChargeDataFromLog] 处理有线数据', name: 'BatteryHealth'); @@ -411,7 +471,6 @@ class _BatteryHealthPageState extends State _isWireless = false; - // 🟢 1.3.0-dev.2: real_type 优先级最高 if (realType != null && realType == "PPS") { _chargeType = "PPS"; } else if (realType != null && @@ -438,14 +497,13 @@ class _BatteryHealthPageState extends State } } - // 🟢 1.3.0-dev.3: 增加超时保护 + // ---------- SAF 文件读取 ---------- Future _fetchChargeDataFromLog() async { if (!_isCharging) { dev.log('[_fetchChargeDataFromLog] 未充电,跳过读取', name: 'BatteryHealth'); return; } - // 超时检查:如果插入超过 5 分钟还没读到数据,不再频繁报错 if (_chargingStartTime != null) { Duration elapsed = DateTime.now().toUtc().difference(_chargingStartTime!); if (elapsed.inMinutes > 5) { @@ -457,24 +515,35 @@ class _BatteryHealthPageState extends State } } - String fullPath = _filePath + getTodayFileName(); - dev.log('[_fetchChargeDataFromLog] 尝试读取: $fullPath', name: 'BatteryHealth'); + // 检查 SAF 授权 + _safState = await _safService.getPermissionState(); + if (_safState != SafPermissionState.authorized) { + if (mounted) _showSafAuthorizationDialog(); + return; + } - File file = File(fullPath); - if (!await file.exists()) { + final fileName = getTodayFileName(); + dev.log('[_fetchChargeDataFromLog] 尝试读取: $fileName', name: 'BatteryHealth'); + + final exists = await _safService.logFileExists(fileName); + if (!exists) { dev.log( - '[_fetchChargeDataFromLog] ⚠️ 文件不存在: $fullPath', + '[_fetchChargeDataFromLog] ⚠️ 文件不存在: $fileName', name: 'BatteryHealth', ); return; } + try { - List lines = await file.readAsLines(); + final lines = await _safService.readLogFile(fileName); + if (lines.isEmpty) { + dev.log('[_fetchChargeDataFromLog] 文件为空', name: 'BatteryHealth'); + return; + } dev.log( '[_fetchChargeDataFromLog] ✅ 读取成功,共 ${lines.length} 行', name: 'BatteryHealth', ); - if (lines.isEmpty) return; _parseChargeDataFromLog(lines); } catch (e) { dev.log('[_fetchChargeDataFromLog] ❌ 读取异常: $e', name: 'BatteryHealth'); @@ -535,19 +604,20 @@ class _BatteryHealthPageState extends State // ---------- 读取当前数据 ---------- Future readBatteryCapacity() async { - var status = await Permission.manageExternalStorage.request(); - if (!status.isGranted) { - setState(() => _hasData = false); + // 检查 SAF 授权 + _safState = await _safService.getPermissionState(); + if (_safState != SafPermissionState.authorized) { + if (mounted) _showSafAuthorizationDialog(); return; } await _getDeviceInfo(); await _scanAndUpdateDB(); - String fullPath = _filePath + getTodayFileName(); - File file = File(fullPath); + final fileName = getTodayFileName(); + final exists = await _safService.logFileExists(fileName); - if (!await file.exists()) { + if (!exists) { List> recent = await _queryRecentData(1); if (recent.isNotEmpty) { setState(() { @@ -567,7 +637,7 @@ class _BatteryHealthPageState extends State } try { - List lines = await file.readAsLines(); + final lines = await _safService.readLogFile(fileName); if (lines.isEmpty) { setState(() => _hasData = false); if (_isCharging) _fetchChargeDataFromLog(); @@ -700,8 +770,16 @@ class _BatteryHealthPageState extends State } }); - WidgetsBinding.instance.addPostFrameCallback((_) { - readBatteryCapacity(); + // 延迟初始化 SAF 并读取数据 + WidgetsBinding.instance.addPostFrameCallback((_) async { + await _safService.init(); + _safState = await _safService.getPermissionState(); + if (_safState == SafPermissionState.unauthorized || + _safState == SafPermissionState.expired) { + if (mounted) _showSafAuthorizationDialog(); + } else { + readBatteryCapacity(); + } }); } @@ -799,9 +877,7 @@ class _BatteryHealthPageState extends State fontSize: 40, fontWeight: FontWeight.w500, height: 1.0, - color: healthPercent < 70 - ? Colors.red - : Colors.black, // 🟢 动态颜色 + color: healthPercent < 70 ? Colors.red : Colors.black, ), ), const SizedBox(width: 2), @@ -813,7 +889,7 @@ class _BatteryHealthPageState extends State height: 1.0, color: healthPercent < 70 ? Colors.red - : Colors.grey.shade600, // 🟢 动态颜色 + : Colors.grey.shade600, ), ), ], @@ -1047,7 +1123,7 @@ class _BatteryHealthPageState extends State } else if (value >= 70) { barColor = kHealthYellow; } else { - barColor = Colors.red; // 🟢 小于 70 显示红色 + barColor = Colors.red; } } else { barColor = Colors.grey.shade300; diff --git a/lib/services/saf_storage_service.dart b/lib/services/saf_storage_service.dart new file mode 100644 index 0000000..a109bac --- /dev/null +++ b/lib/services/saf_storage_service.dart @@ -0,0 +1,136 @@ +import 'dart:io'; +import 'package:flutter/foundation.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:file_picker/file_picker.dart'; +import '../models/saf_permission_state.dart'; + +/// 使用 file_picker 实现文件夹访问 +class SafStorageService { + static final SafStorageService _instance = SafStorageService._internal(); + factory SafStorageService() => _instance; + SafStorageService._internal(); + + static const String _prefKeySafPath = 'saf_storage_path'; + String? _cachedPath; + + Future init() async { + final prefs = await SharedPreferences.getInstance(); + _cachedPath = prefs.getString(_prefKeySafPath); + if (kDebugMode) { + print('[SAF] 初始化,缓存路径: $_cachedPath'); + } + } + + Future getPermissionState() async { + if (_cachedPath == null || _cachedPath!.isEmpty) { + return SafPermissionState.unauthorized; + } + try { + final dir = Directory(_cachedPath!); + if (!await dir.exists()) { + _clearCache(); + return SafPermissionState.expired; + } + return SafPermissionState.authorized; + } catch (e) { + _clearCache(); + return SafPermissionState.expired; + } + } + + Future requestPermission() async { + try { + // 使用 file_picker 选择文件夹 + String? selectedPath = await FilePicker.platform.getDirectoryPath(); + + if (selectedPath == null || selectedPath.isEmpty) { + return SafAuthorizationResult.denied(); + } + + // 验证路径是否包含 mbattery_charger(可选,提高容错) + if (!selectedPath.contains('mbattery_charger')) { + return SafAuthorizationResult.error('请选择 mbattery_charger 文件夹'); + } + + _cachedPath = selectedPath; + await _savePath(selectedPath); + + if (kDebugMode) { + print('[SAF] 授权成功,路径: $selectedPath'); + } + + return SafAuthorizationResult.success(selectedPath); + } catch (e) { + if (kDebugMode) { + print('[SAF] 授权失败: $e'); + } + return SafAuthorizationResult.error('授权失败: $e'); + } + } + + Future logFileExists(String fileName) async { + final state = await getPermissionState(); + if (state != SafPermissionState.authorized) return false; + try { + final file = File('${_cachedPath!}/$fileName'); + return await file.exists(); + } catch (e) { + return false; + } + } + + Future> readLogFile(String fileName) async { + final state = await getPermissionState(); + if (state != SafPermissionState.authorized) return []; + try { + final file = File('${_cachedPath!}/$fileName'); + if (!await file.exists()) return []; + return await file.readAsLines(); + } catch (e) { + if (kDebugMode) { + print('[SAF] 读取文件失败: $e'); + } + return []; + } + } + + Future> listFiles() async { + final state = await getPermissionState(); + if (state != SafPermissionState.authorized) return []; + try { + final dir = Directory(_cachedPath!); + if (!await dir.exists()) return []; + final entities = await dir.list().toList(); + return entities + .where((e) => e is File) + .map((e) => e.path.split(Platform.pathSeparator).last) + .toList(); + } catch (e) { + if (kDebugMode) { + print('[SAF] 列出文件失败: $e'); + } + return []; + } + } + + Future validatePermission() async { + final state = await getPermissionState(); + return state == SafPermissionState.authorized; + } + + void _clearCache() { + _cachedPath = null; + _savePath(null); + } + + Future _savePath(String? path) async { + final prefs = await SharedPreferences.getInstance(); + if (path != null && path.isNotEmpty) { + await prefs.setString(_prefKeySafPath, path); + } else { + await prefs.remove(_prefKeySafPath); + } + } + + String? get cachedPath => _cachedPath; +} diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 2e1de87..be1ee3e 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -6,6 +6,7 @@ list(APPEND FLUTTER_PLUGIN_LIST ) list(APPEND FLUTTER_FFI_PLUGIN_LIST + jni ) set(PLUGIN_BUNDLED_LIBRARIES) diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 0a7b7cb..852819c 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -6,11 +6,11 @@ import FlutterMacOS import Foundation import device_info_plus -import path_provider_foundation +import shared_preferences_foundation import sqflite_darwin func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) - PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) + SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) } diff --git a/pubspec.lock b/pubspec.lock index e440b90..a51ba58 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -6,7 +6,7 @@ packages: description: name: args sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.7.0" async: @@ -14,7 +14,7 @@ packages: description: name: async sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.13.1" boolean_selector: @@ -22,39 +22,55 @@ packages: description: name: boolean_selector sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.2" characters: dependency: transitive description: name: characters - sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 - url: "https://pub.dev" + sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b + url: "https://pub.flutter-io.cn" source: hosted - version: "1.4.0" + version: "1.4.1" clock: dependency: transitive description: name: clock sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.1.2" + code_assets: + dependency: transitive + description: + name: code_assets + sha256: bf394f466ba9205f1812a0433b392d6af280f155f56651eda7c18cc32ed493b8 + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.2.1" collection: dependency: transitive description: name: collection sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.19.1" + crypto: + dependency: transitive + description: + name: crypto + sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.7" cupertino_icons: dependency: "direct main" description: name: cupertino_icons sha256: "41e005c33bd814be4d3096aff55b1908d419fde52ca656c8c47719ec745873cd" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.0.9" device_info_plus: @@ -62,7 +78,7 @@ packages: description: name: device_info_plus sha256: a7fd703482b391a87d60b6061d04dfdeab07826b96f9abd8f5ed98068acc0074 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "10.1.2" device_info_plus_platform_interface: @@ -70,7 +86,7 @@ packages: description: name: device_info_plus_platform_interface sha256: e1ea89119e34903dca74b883d0dd78eb762814f97fb6c76f35e9ff74d261a18f - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "7.0.3" fake_async: @@ -78,7 +94,7 @@ packages: description: name: fake_async sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.3.3" ffi: @@ -86,7 +102,7 @@ packages: description: name: ffi sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.2.0" file: @@ -94,9 +110,17 @@ packages: description: name: file sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "7.0.1" + file_picker: + dependency: "direct main" + description: + name: file_picker + sha256: "1bbf65dd997458a08b531042ec3794112a6c39c07c37ff22113d2e7e4f81d4e4" + url: "https://pub.flutter-io.cn" + source: hosted + version: "6.2.1" flutter: dependency: "direct main" description: flutter @@ -107,15 +131,23 @@ packages: description: name: flutter_lints sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "5.0.0" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + sha256: "3854fe5e3bff0b113c658f260b90c95dea17c92db0f2addeac2e343dd9969785" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.0.35" flutter_svg: dependency: "direct main" description: name: flutter_svg sha256: "35882981abcbfb8c15b286f0cd690ff25bac12d95eff3e25ee207f37d4c42e7f" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.3.0" flutter_test: @@ -128,12 +160,20 @@ packages: description: flutter source: sdk version: "0.0.0" + hooks: + dependency: transitive + description: + name: hooks + sha256: "9a62a50b50b769a737bc0a8ff381f333529df3ab746b2f6b02e83760231455ba" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.0.2" http: dependency: transitive description: name: http sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.6.0" http_parser: @@ -141,15 +181,31 @@ packages: description: name: http_parser sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.1.2" + jni: + dependency: transitive + description: + name: jni + sha256: c2230682d5bc2362c1c9e8d3c7f406d9cbba23ab3f2e203a025dd47e0fb2e68f + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.0" + jni_flutter: + dependency: transitive + description: + name: jni_flutter + sha256: "8b59e590786050b1cd866677dddaf76b1ade5e7bc751abe04b86e84d379d3ba6" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.1" leak_tracker: dependency: transitive description: name: leak_tracker sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "11.0.2" leak_tracker_flutter_testing: @@ -157,7 +213,7 @@ packages: description: name: leak_tracker_flutter_testing sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.0.10" leak_tracker_testing: @@ -165,7 +221,7 @@ packages: description: name: leak_tracker_testing sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.0.2" lints: @@ -173,39 +229,63 @@ packages: description: name: lints sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "5.1.1" + logging: + dependency: transitive + description: + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.3.0" matcher: dependency: transitive description: name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 - url: "https://pub.dev" + sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 + url: "https://pub.flutter-io.cn" source: hosted - version: "0.12.17" + version: "0.12.19" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec - url: "https://pub.dev" + sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b" + url: "https://pub.flutter-io.cn" source: hosted - version: "0.11.1" + version: "0.13.0" meta: dependency: transitive description: name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c - url: "https://pub.dev" + sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349" + url: "https://pub.flutter-io.cn" source: hosted - version: "1.16.0" + version: "1.18.0" + objective_c: + dependency: transitive + description: + name: objective_c + sha256: "6cb691c686fa2838c6deb34980d426145c2a5d537491cb83d463c33cdbc726ed" + url: "https://pub.flutter-io.cn" + source: hosted + version: "9.4.1" + package_config: + dependency: transitive + description: + name: package_config + sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.2.0" path: dependency: "direct main" description: name: path sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.9.1" path_parsing: @@ -213,55 +293,55 @@ packages: description: name: path_parsing sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.1.0" path_provider: dependency: "direct main" description: name: path_provider - sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" - url: "https://pub.dev" + sha256: a7f4874f987173da295a61c181b8ee71dab59b332a486b391babf26a1b884825 + url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.5" + version: "2.1.6" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: "149441ca6e4f38193b2e004c0ca6376a3d11f51fa5a77552d8bd4d2b0c0912ba" - url: "https://pub.dev" + sha256: "69cbd515a62b94d32a7944f086b2f82b4ac40a1d45bebfc00813a430ab2dabcd" + url: "https://pub.flutter-io.cn" source: hosted - version: "2.2.23" + version: "2.3.1" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "6d13aece7b3f5c5a9731eaf553ff9dcbc2eff41087fd2df587fd0fed9a3eb0c4" - url: "https://pub.dev" + sha256: "2a376b7d6392d80cd3705782d2caa734ca4727776db0b6ec36ef3f1855197699" + url: "https://pub.flutter-io.cn" source: hosted - version: "2.5.1" + version: "2.6.0" path_provider_linux: dependency: transitive description: name: path_provider_linux - sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 - url: "https://pub.dev" + sha256: "58c2005f147315b11e9b4a7bc889cd5203e250cba8e3f012dae259b4972b5c16" + url: "https://pub.flutter-io.cn" source: hosted - version: "2.2.1" + version: "2.2.2" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface - sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" - url: "https://pub.dev" + sha256: "484838772624c3a4b94f1e44a3e19897fee738f2d5c4ce448443b0417f7c9dda" + url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.2" + version: "2.1.3" path_provider_windows: dependency: transitive description: name: path_provider_windows sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.3.0" permission_handler: @@ -269,7 +349,7 @@ packages: description: name: permission_handler sha256: "59adad729136f01ea9e35a48f5d1395e25cba6cea552249ddbe9cf950f5d7849" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "11.4.0" permission_handler_android: @@ -277,7 +357,7 @@ packages: description: name: permission_handler_android sha256: d3971dcdd76182a0c198c096b5db2f0884b0d4196723d21a866fc4cdea057ebc - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "12.1.0" permission_handler_apple: @@ -285,7 +365,7 @@ packages: description: name: permission_handler_apple sha256: "79dfa1df734798aa3cfdad166d3a3698c206d8813de13516ea1071b5d7e2f420" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "9.4.10" permission_handler_html: @@ -293,7 +373,7 @@ packages: description: name: permission_handler_html sha256: "38f000e83355abb3392140f6bc3030660cfaef189e1f87824facb76300b4ff24" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.1.3+5" permission_handler_platform_interface: @@ -301,7 +381,7 @@ packages: description: name: permission_handler_platform_interface sha256: eb99b295153abce5d683cac8c02e22faab63e50679b937fa1bf67d58bb282878 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.3.0" permission_handler_windows: @@ -309,7 +389,7 @@ packages: description: name: permission_handler_windows sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.2.1" petitparser: @@ -317,7 +397,7 @@ packages: description: name: petitparser sha256: "91bd59303e9f769f108f8df05e371341b15d59e995e6806aefab827b58336675" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "7.0.2" platform: @@ -325,7 +405,7 @@ packages: description: name: platform sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.1.6" plugin_platform_interface: @@ -333,9 +413,81 @@ packages: description: name: plugin_platform_interface sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.8" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.2.0" + record_use: + dependency: transitive + description: + name: record_use + sha256: "2551bd8eecfe95d14ae75f6021ad0248be5c27f138c2ec12fcb52b500b3ba1ed" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.6.0" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: c3025c5534b01739267eb7d76959bbc25a6d10f6988e1c2a3036940133dd10bf + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.5.5" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "93ae5884a9df5d3bb696825bceb3a17590754548b5d740eba51500afc8d088f5" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.4.26" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "4e7eaffc2b17ba398759f1151415869a34771ba11ebbccd1b0145472a619a64f" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.5.6" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.4.1" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "649dc798a33931919ea356c4305c2d1f81619ea6e92244070b520187b5140ef9" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.4.2" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.4.3" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.4.1" sky_engine: dependency: transitive description: flutter @@ -346,55 +498,55 @@ packages: description: name: source_span sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.10.2" sqflite: dependency: "direct main" description: name: sqflite - sha256: e2297b1da52f127bc7a3da11439985d9b536f75070f3325e62ada69a5c585d03 - url: "https://pub.dev" + sha256: "58a799e6ac17dd32fbab93813d39ed835a75ccc0f8f85b8955fe318c6712b082" + url: "https://pub.flutter-io.cn" source: hosted - version: "2.4.2" + version: "2.4.3" sqflite_android: dependency: transitive description: name: sqflite_android - sha256: ecd684501ebc2ae9a83536e8b15731642b9570dc8623e0073d227d0ee2bfea88 - url: "https://pub.dev" + sha256: d0548f9d7422a2dae99ec6f8b0a3074463b132d216fa5ba0d230eeefc901983b + url: "https://pub.flutter-io.cn" source: hosted - version: "2.4.2+2" + version: "2.4.3" sqflite_common: dependency: transitive description: name: sqflite_common - sha256: "6ef422a4525ecc601db6c0a2233ff448c731307906e92cabc9ba292afaae16a6" - url: "https://pub.dev" + sha256: "5bf6a55c166e73bf651ba7ec3ed486e577620e3dc8f3a9c6a258a8031b624590" + url: "https://pub.flutter-io.cn" source: hosted - version: "2.5.6" + version: "2.5.11" sqflite_darwin: dependency: transitive description: name: sqflite_darwin - sha256: "279832e5cde3fe99e8571879498c9211f3ca6391b0d818df4e17d9fff5c6ccb3" - url: "https://pub.dev" + sha256: c86ca18b8f666bbf903924687fe21cc16fc385d086005067e26619ca530bef9f + url: "https://pub.flutter-io.cn" source: hosted - version: "2.4.2" + version: "2.4.3+1" sqflite_platform_interface: dependency: transitive description: name: sqflite_platform_interface - sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920" - url: "https://pub.dev" + sha256: f84939f84350d92d04416f8bc4dc52d3896aec7716cc9e80cf0146342139dc50 + url: "https://pub.flutter-io.cn" source: hosted - version: "2.4.0" + version: "2.4.1" stack_trace: dependency: transitive description: name: stack_trace sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.12.1" stream_channel: @@ -402,7 +554,7 @@ packages: description: name: stream_channel sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.4" string_scanner: @@ -410,39 +562,39 @@ packages: description: name: string_scanner sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.4.1" synchronized: dependency: transitive description: name: synchronized - sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0 - url: "https://pub.dev" + sha256: "93b153dcb6a26dcddee6ca087dd634b53e38c10b5aa163e8e49501a776456153" + url: "https://pub.flutter-io.cn" source: hosted - version: "3.4.0" + version: "3.4.1" term_glyph: dependency: transitive description: name: term_glyph sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.2.2" test_api: dependency: transitive description: name: test_api - sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" - url: "https://pub.dev" + sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e" + url: "https://pub.flutter-io.cn" source: hosted - version: "0.7.6" + version: "0.7.11" typed_data: dependency: transitive description: name: typed_data sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.4.0" vector_graphics: @@ -450,7 +602,7 @@ packages: description: name: vector_graphics sha256: "2306c03da2ba81724afeb589c351ebbc0aa7d86005925be8f8735856dbe5e42d" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.2.2" vector_graphics_codec: @@ -458,23 +610,23 @@ packages: description: name: vector_graphics_codec sha256: "99fd9fbd34d9f9a32efd7b6a6aae14125d8237b10403b422a6a6dfeac2806146" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.1.13" vector_graphics_compiler: dependency: transitive description: name: vector_graphics_compiler - sha256: b9b3f391857781aa96acacef96066f2f49b4cd03cf9fce3ca4d8da2ef5ea129e - url: "https://pub.dev" + sha256: "142a9146f447d15b10bdc00e21d5f4d83e5b32bb5f8f8f5a04c75311344923a3" + url: "https://pub.flutter-io.cn" source: hosted - version: "1.2.3" + version: "1.2.6" vector_math: dependency: transitive description: name: vector_math sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.2.0" vm_service: @@ -482,7 +634,7 @@ packages: description: name: vm_service sha256: "0016aef94fc66495ac78af5859181e3f3bf2026bd8eecc72b9565601e19ab360" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "15.2.0" web: @@ -490,7 +642,7 @@ packages: description: name: web sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.1.1" win32: @@ -498,7 +650,7 @@ packages: description: name: win32 sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "5.15.0" win32_registry: @@ -506,7 +658,7 @@ packages: description: name: win32_registry sha256: "21ec76dfc731550fd3e2ce7a33a9ea90b828fdf19a5c3bcf556fa992cfa99852" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.1.5" xdg_directories: @@ -514,17 +666,25 @@ packages: description: name: xdg_directories sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.1.0" xml: dependency: transitive description: name: xml - sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025" - url: "https://pub.dev" + sha256: "67f0aff7be013d107995e9b75bf4e7f2c3ef2dfdb2c8e68024bba0a7fd5756a4" + url: "https://pub.flutter-io.cn" source: hosted - version: "6.6.1" + version: "7.0.1" + yaml: + dependency: transitive + description: + name: yaml + sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.1.3" sdks: - dart: ">=3.9.2 <4.0.0" - flutter: ">=3.35.0" + dart: ">=3.12.0 <4.0.0" + flutter: ">=3.44.0" diff --git a/pubspec.yaml b/pubspec.yaml index 207abaf..5aec786 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.3.0-dev.1 +version: 1.3.0-dev.2 environment: sdk: ^3.9.2 @@ -37,6 +37,9 @@ dependencies: path_provider: ^2.1.0 path: ^1.9.0 flutter_svg: ^2.0.10+1 + shared_preferences: ^2.2.0 + file_picker: ^6.0.0 + # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 0e69e40..d6ee74d 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -7,6 +7,7 @@ list(APPEND FLUTTER_PLUGIN_LIST ) list(APPEND FLUTTER_FFI_PLUGIN_LIST + jni ) set(PLUGIN_BUNDLED_LIBRARIES)