diff --git a/USAGE_GUIDE.md b/USAGE_GUIDE.md new file mode 100644 index 0000000..9f4e635 --- /dev/null +++ b/USAGE_GUIDE.md @@ -0,0 +1,336 @@ +# App Upgrade Plugin - 完整使用指南 + +## 🚀 快速开始 + +### 基础用法(一行代码) +```dart +import 'package:app_upgrade_plugin/app_upgrade_plugin.dart'; + +// 最简单的使用方式 +await AppUpgradeSimple.instance.checkUpdate( + context: context, + url: 'https://api.example.com/check-update', +); +``` + +## ⚙️ 配置选项 + +### 预设配置 +```dart +// 自动更新模式 +AppUpgradeSimple.instance.configure(UpgradeConfig.auto); + +// 静默检查模式 +AppUpgradeSimple.instance.configure(UpgradeConfig.silent); + +// 开发模式 +AppUpgradeSimple.instance.configure(UpgradeConfig.development); + +// 生产模式 +AppUpgradeSimple.instance.configure(UpgradeConfig.production); +``` + +### 自定义配置 +```dart +AppUpgradeSimple.instance.configure(UpgradeConfig( + showNoUpdateToast: true, // 显示无更新提示 + autoDownload: false, // 自动下载 + autoInstall: false, // 自动安装 + connectionTimeout: 30, // 连接超时(秒) + downloadTimeout: 300, // 下载超时(秒) + installTimeout: 45, // 安装检测超时(秒) + enableDebugLog: true, // 启用调试日志 + customToast: (message) { // 自定义Toast + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(message)), + ); + }, +)); +``` + +## 🎯 使用场景 + +### 1. 应用启动时检查更新 +```dart +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Builder( + builder: (context) { + // 应用启动后检查更新 + WidgetsBinding.instance.addPostFrameCallback((_) { + AppUpgradeSimple.instance.checkUpdate( + context: context, + url: 'https://api.example.com/check-update', + ); + }); + return MyHomePage(); + }, + ), + ); + } +} +``` + +### 2. 定期检查更新 +```dart +class MyHomePage extends StatefulWidget { + @override + State createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + Timer? _updateTimer; + + @override + void initState() { + super.initState(); + // 每24小时检查一次更新 + _updateTimer = Timer.periodic(const Duration(hours: 24), (_) { + AppUpgradeSimple.instance.checkUpdateSilent( + url: 'https://api.example.com/check-update', + ); + }); + } + + @override + void dispose() { + _updateTimer?.cancel(); + super.dispose(); + } +} +``` + +### 3. 手动检查更新 +```dart +ElevatedButton( + onPressed: () async { + await AppUpgradeSimple.instance.checkUpdate( + context: context, + url: 'https://api.example.com/check-update', + params: { + 'userId': currentUserId, + 'channel': releaseChannel, + }, + onComplete: () { + print('更新检查完成'); + }, + ); + }, + child: const Text('检查更新'), +) +``` + +## 🔧 高级功能 + +### 预下载APK +```dart +// 后台预下载,不显示UI +final filePath = await AppUpgradeSimple.instance.preDownloadApk( + url: 'https://example.com/app.apk', + onProgress: (progress) { + print('下载进度: ${progress.percentage}%'); + }, +); +``` + +### 查找已下载的APK +```dart +final existingApk = await AppUpgradeSimple.instance.findDownloadedApk('1.2.0'); +if (existingApk != null) { + print('找到已下载的APK: $existingApk'); +} +``` + +### 网络状态检查 +```dart +final hasNetwork = await AppUpgradeSimple.instance.checkNetworkStatus(); +if (!hasNetwork) { + showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('网络错误'), + content: const Text('请检查网络连接'), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('确定'), + ), + ], + ), + ); +} +``` + +### 清理下载缓存 +```dart +// 清理所有下载的APK文件 +await AppUpgradeSimple.instance.clearDownloadCache(); +``` + +## 🎨 UI定制 + +### 自定义Toast +```dart +AppUpgradeSimple.instance.configure(UpgradeConfig( + customToast: (message) { + // 使用SnackBar代替Toast + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(message), + backgroundColor: Colors.deepPurple, + behavior: SnackBarBehavior.floating, + ), + ); + }, +)); +``` + +### 富文本更新内容 +服务器返回的更新内容支持简单的富文本格式: +``` +**新功能** +- 添加了[暗黑模式]支持 +- 优化了`网络请求`性能 +- __修复了已知问题__ + +**重要更新** +请及时更新到最新版本以获得更好的体验! +``` + +## 📱 平台支持 + +### Android +- ✅ APK下载和安装 +- ✅ 权限自动处理 +- ✅ 多应用商店支持 +- ✅ 安装状态智能检测 +- ✅ 断点续传 +- ✅ 文件校验 + +### iOS +- ✅ App Store跳转 +- ✅ 企业证书分发 +- ✅ TestFlight支持 + +## 🔍 调试和测试 + +### 启用调试日志 +```dart +AppUpgradeSimple.instance.configure(UpgradeConfig.development); +``` + +### 查看日志输出 +``` +🔍 检查更新结果: UpgradeInfo(...) +⚡ 应用回到前台,立即检查安装状态 +✅ 检测结果: 安装成功 +❌ 检测结果: 安装未完成 +``` + +### 测试不同场景 +插件内置了测试按钮,可以模拟各种安装场景: +- 模拟安装取消 +- 模拟权限拒绝 +- 模拟安装超时 + +## 🛡️ 错误处理 + +插件提供了完善的错误处理机制: + +### 网络错误 +- 连接超时 +- DNS解析失败 +- 服务器错误 +- 证书验证失败 + +### 安装错误 +- 权限被拒绝 +- APK文件损坏 +- 存储空间不足 +- 系统版本不兼容 + +### 自动重试 +- 智能检测安装状态 +- 多种重试方式 +- 用户友好的错误提示 + +## 📊 最佳实践 + +### 1. 应用启动检查 +```dart +// 推荐:应用启动时静默检查 +WidgetsBinding.instance.addPostFrameCallback((_) { + AppUpgradeSimple.instance.checkUpdateSilent( + url: 'https://api.example.com/check-update', + ).then((info) { + if (info != null && info.hasUpdate) { + // 发现更新,显示提示 + showUpdateNotification(context, info); + } + }); +}); +``` + +### 2. 定期检查 +```dart +// 推荐:每日检查一次 +Timer.periodic(const Duration(days: 1), (_) { + AppUpgradeSimple.instance.checkUpdateSilent( + url: 'https://api.example.com/check-update', + ); +}); +``` + +### 3. 用户手动检查 +```dart +// 推荐:提供手动检查按钮 +ElevatedButton( + onPressed: () { + AppUpgradeSimple.instance.checkUpdate( + context: context, + url: 'https://api.example.com/check-update', + ); + }, + child: const Text('检查更新'), +) +``` + +## 🎯 完整示例 + +查看 `example/lib/main_enhanced.dart` 获取完整的使用示例,包含所有功能的演示。 + +## 📝 API文档 + +### 主要方法 + +#### `checkUpdate()` +检查并处理应用更新的主要方法。 + +#### `checkUpdateSilent()` +静默检查更新,不显示UI。 + +#### `preDownloadApk()` +后台预下载APK文件。 + +#### `clearDownloadCache()` +清理下载缓存。 + +#### `checkNetworkStatus()` +检查网络连接状态。 + +### 配置类 + +#### `UpgradeConfig` +升级配置类,控制插件的行为。 + +#### 预设配置 +- `UpgradeConfig.auto` - 自动更新 +- `UpgradeConfig.silent` - 静默检查 +- `UpgradeConfig.development` - 开发模式 +- `UpgradeConfig.production` - 生产模式 + +--- + +这个插件现在提供了完整的应用升级解决方案,支持各种使用场景和自定义需求! diff --git a/example/lib/main_enhanced.dart b/example/lib/main_enhanced.dart index e1108cb..0086598 100644 --- a/example/lib/main_enhanced.dart +++ b/example/lib/main_enhanced.dart @@ -1,738 +1,335 @@ -import 'dart:async'; - -import 'package:app_upgrade_plugin/app_upgrade_plugin_enhanced.dart'; +import 'package:app_upgrade_plugin/app_upgrade_plugin.dart'; import 'package:flutter/material.dart'; -import 'package:permission_handler/permission_handler.dart'; void main() { - runApp(const MyEnhancedApp()); + runApp(const MyApp()); } -class MyEnhancedApp extends StatelessWidget { - const MyEnhancedApp({super.key}); +class MyApp extends StatelessWidget { + const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( - title: '增强版App升级插件示例', + title: 'App Upgrade Plugin Enhanced Demo', theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), useMaterial3: true, ), - home: const EnhancedUpgradePage(), + home: const MyHomePage(title: 'App升级插件完整示例'), ); } } -class EnhancedUpgradePage extends StatefulWidget { - const EnhancedUpgradePage({super.key}); +class MyHomePage extends StatefulWidget { + const MyHomePage({super.key, required this.title}); + + final String title; @override - State createState() => _EnhancedUpgradePageState(); + State createState() => _MyHomePageState(); } -class _EnhancedUpgradePageState extends State with SingleTickerProviderStateMixin { - final _plugin = AppUpgradePluginEnhanced.instance; - - late TabController _tabController; - - // 状态变量 - Map? _appInfo; - NetworkStatus? _networkStatus; - DownloadTask? _currentDownload; - UpgradeInfo? _upgradeInfo; - Map? _cacheStats; - - // 配置选项 - bool _wifiOnly = true; - bool _autoCheck = true; - bool _supportBreakpoint = true; - bool _verifyIntegrity = true; - VersionCompareStrategy _versionStrategy = VersionCompareStrategy.semantic; - - // 测试数据 - final _testVersions = ['1.0.0', '1.1.0', '1.2.0', '2.0.0-beta.1', '2.0.0']; - String _currentTestVersion = '1.0.0'; - String _remoteTestVersion = '2.0.0'; +class _MyHomePageState extends State { + String _status = '准备就绪'; + bool _isLoading = false; @override void initState() { super.initState(); - _tabController = TabController(length: 4, vsync: this); - _initPlugin(); - _loadData(); + _initializePlugin(); } - @override - void dispose() { - _tabController.dispose(); - _plugin.removeUpgradeCallback(_onUpgradeInfo); - _plugin.removeDownloadCallback(_onDownloadProgress); - _plugin.removeErrorCallback(_onError); - super.dispose(); - } - - void _initPlugin() { + /// 初始化插件 + void _initializePlugin() { // 配置插件 - _plugin.configure( - debugMode: true, - autoCheck: _autoCheck, - wifiOnly: _wifiOnly, - supportBreakpoint: _supportBreakpoint, - verifyIntegrity: _verifyIntegrity, - versionStrategy: _versionStrategy, - ); + AppUpgradeSimple.instance.configure(const UpgradeConfig( + enableDebugLog: true, + installTimeout: 60, + customToast: null, // 使用默认Toast + )); + } - // 添加回调 - _plugin.addUpgradeCallback(_onUpgradeInfo); - _plugin.addDownloadCallback(_onDownloadProgress); - _plugin.addErrorCallback(_onError); - - // 监听网络状态 - NetworkMonitor.instance.statusStream.listen((status) { - setState(() { - _networkStatus = status; - }); + /// 基础检查更新 + Future _checkUpdate() async { + setState(() { + _isLoading = true; + _status = '检查更新中...'; }); - } - Future _loadData() async { - // 请求权限 - await _requestPermissions(); - - // 获取App信息 - _appInfo = await _plugin.getAppInfo(); - - // 获取网络状态 - _networkStatus = _plugin.networkStatus; - - // 获取缓存统计 - _cacheStats = await _plugin.getCacheStats(); - - setState(() {}); - } - - Future _requestPermissions() async { - if (Theme.of(context).platform == TargetPlatform.android) { - await [ - Permission.storage, - Permission.requestInstallPackages, - Permission.notification, - ].request(); + try { + await AppUpgradeSimple.instance.checkUpdate( + context: context, + url: 'https://api.example.com/check-update', + params: { + 'platform': 'android', + 'channel': 'official', + }, + onComplete: () { + setState(() { + _isLoading = false; + _status = '检查完成'; + }); + }, + ); + } catch (e) { + setState(() { + _isLoading = false; + _status = '检查失败: $e'; + }); } } - void _onUpgradeInfo(UpgradeInfo info) { + /// 自动更新 + Future _autoUpdate() async { setState(() { - _upgradeInfo = info; + _isLoading = true; + _status = '自动更新中...'; }); - // 显示升级对话框 - UpgradeDialog.show( - context, - upgradeInfo: info, - primaryColor: Theme.of(context).colorScheme.primary, - ); + try { + await AppUpgradeSimple.instance.checkUpdate( + context: context, + url: 'https://api.example.com/check-update', + config: UpgradeConfig.auto, + onComplete: () { + setState(() { + _isLoading = false; + _status = '自动更新完成'; + }); + }, + ); + } catch (e) { + setState(() { + _isLoading = false; + _status = '自动更新失败: $e'; + }); + } } - void _onDownloadProgress(DownloadTask task) { + /// 静默检查 + Future _silentCheck() async { setState(() { - _currentDownload = task; + _isLoading = true; + _status = '静默检查中...'; + }); + + final info = await AppUpgradeSimple.instance.checkUpdateSilent( + url: 'https://api.example.com/check-update', + ); + + setState(() { + _isLoading = false; + if (info != null && info.hasUpdate) { + _status = '发现新版本: ${info.versionName}'; + } else { + _status = '已是最新版本'; + } }); } - void _onError(String error) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(error), - backgroundColor: Colors.red, - ), - ); + /// 清理缓存 + Future _clearCache() async { + setState(() { + _isLoading = true; + _status = '清理缓存中...'; + }); + + await AppUpgradeSimple.instance.clearDownloadCache(); + + setState(() { + _isLoading = false; + _status = '缓存清理完成'; + }); + } + + /// 检查网络状态 + Future _checkNetwork() async { + setState(() { + _isLoading = true; + _status = '检查网络中...'; + }); + + final hasNetwork = await AppUpgradeSimple.instance.checkNetworkStatus(); + + setState(() { + _isLoading = false; + _status = hasNetwork ? '网络连接正常' : '网络连接异常'; + }); + } + + /// 获取应用信息 + Future _getAppInfo() async { + setState(() { + _isLoading = true; + _status = '获取应用信息中...'; + }); + + final appInfo = await AppUpgradeSimple.instance.getAppInfo(); + + setState(() { + _isLoading = false; + _status = '应用信息: ${appInfo['appName']} v${appInfo['version']}'; + }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text('增强版App升级插件'), backgroundColor: Theme.of(context).colorScheme.inversePrimary, - bottom: TabBar( - controller: _tabController, - tabs: const [ - Tab(text: '基础功能'), - Tab(text: '高级配置'), - Tab(text: '网络监测'), - Tab(text: '版本管理'), + title: Text(widget.title), + elevation: 2, + ), + body: SingleChildScrollView( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + // 状态显示卡片 + Card( + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '当前状态', + style: Theme.of(context).textTheme.titleMedium, + ), + const SizedBox(height: 8), + Row( + children: [ + if (_isLoading) + const SizedBox( + width: 16, + height: 16, + child: CircularProgressIndicator(strokeWidth: 2), + ), + if (_isLoading) const SizedBox(width: 8), + Expanded( + child: Text( + _status, + style: Theme.of(context).textTheme.bodyMedium, + ), + ), + ], + ), + ], + ), + ), + ), + const SizedBox(height: 20), + + // 基础功能 + Text( + '基础功能', + style: Theme.of(context).textTheme.titleLarge, + ), + const SizedBox(height: 12), + ElevatedButton.icon( + onPressed: _isLoading ? null : _checkUpdate, + icon: const Icon(Icons.system_update), + label: const Text('检查更新'), + ), + const SizedBox(height: 8), + ElevatedButton.icon( + onPressed: _isLoading ? null : _autoUpdate, + icon: const Icon(Icons.auto_awesome), + label: const Text('自动更新'), + ), + const SizedBox(height: 8), + ElevatedButton.icon( + onPressed: _isLoading ? null : _silentCheck, + icon: const Icon(Icons.visibility_off), + label: const Text('静默检查'), + ), + const SizedBox(height: 20), + + // 工具功能 + Text( + '工具功能', + style: Theme.of(context).textTheme.titleLarge, + ), + const SizedBox(height: 12), + ElevatedButton.icon( + onPressed: _isLoading ? null : _checkNetwork, + icon: const Icon(Icons.wifi), + label: const Text('检查网络'), + ), + const SizedBox(height: 8), + ElevatedButton.icon( + onPressed: _isLoading ? null : _getAppInfo, + icon: const Icon(Icons.info), + label: const Text('应用信息'), + ), + const SizedBox(height: 8), + ElevatedButton.icon( + onPressed: _isLoading ? null : _clearCache, + icon: const Icon(Icons.cleaning_services), + label: const Text('清理缓存'), + ), + const SizedBox(height: 20), + + // 配置示例 + Text( + '配置示例', + style: Theme.of(context).textTheme.titleLarge, + ), + const SizedBox(height: 12), + ElevatedButton.icon( + onPressed: _isLoading + ? null + : () { + AppUpgradeSimple.instance.enableAutoUpdate(); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('已启用自动更新模式')), + ); + }, + icon: const Icon(Icons.auto_mode), + label: const Text('启用自动更新'), + ), + const SizedBox(height: 8), + ElevatedButton.icon( + onPressed: _isLoading + ? null + : () { + AppUpgradeSimple.instance.enableSilentCheck(); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('已启用静默检查模式')), + ); + }, + icon: const Icon(Icons.volume_off), + label: const Text('启用静默模式'), + ), + const SizedBox(height: 8), + ElevatedButton.icon( + onPressed: _isLoading + ? null + : () { + AppUpgradeSimple.instance.configure(UpgradeConfig( + autoDownload: true, + autoInstall: false, + installTimeout: 60, + customToast: (message) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(message), + backgroundColor: Colors.deepPurple, + ), + ); + }, + )); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('已配置自定义设置')), + ); + }, + icon: const Icon(Icons.settings), + label: const Text('自定义配置'), + ), ], ), ), - body: TabBarView( - controller: _tabController, - children: [ - _buildBasicTab(), - _buildConfigTab(), - _buildNetworkTab(), - _buildVersionTab(), - ], - ), ); } - - // 基础功能标签页 - Widget _buildBasicTab() { - return SingleChildScrollView( - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // App信息卡片 - if (_appInfo != null) - Card( - child: Padding( - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - 'App信息', - style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), - ), - const SizedBox(height: 12), - Text('应用名称: ${_appInfo!['appName']}'), - Text('包名: ${_appInfo!['packageName']}'), - Text('版本: ${_appInfo!['version']}'), - Text('构建号: ${_appInfo!['buildNumber']}'), - ], - ), - ), - ), - - const SizedBox(height: 16), - - // 升级信息卡片 - if (_upgradeInfo != null) - Card( - color: Colors.amber.shade50, - child: Padding( - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - const Icon(Icons.system_update, color: Colors.amber), - const SizedBox(width: 8), - const Text( - '发现新版本', - style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), - ), - ], - ), - const SizedBox(height: 12), - Text('版本: ${_upgradeInfo!.versionName}'), - Text('强制更新: ${_upgradeInfo!.isForceUpdate ? "是" : "否"}'), - if (_upgradeInfo!.apkSize != null) Text('大小: ${_formatBytes(_upgradeInfo!.apkSize!)}'), - ], - ), - ), - ), - - const SizedBox(height: 16), - - // 下载进度卡片 - if (_currentDownload != null) - Card( - child: Padding( - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - '下载进度', - style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), - ), - const SizedBox(height: 12), - LinearProgressIndicator(value: _currentDownload!.progress), - const SizedBox(height: 8), - Text('状态: ${_getDownloadStatusText(_currentDownload!.status)}'), - Text('进度: ${(_currentDownload!.progress * 100).toStringAsFixed(1)}%'), - if (_currentDownload!.totalSize != null) - Text( - '${_formatBytes(_currentDownload!.downloadedSize)} / ${_formatBytes(_currentDownload!.totalSize!)}'), - if (_currentDownload!.errorMessage != null) - Text('错误: ${_currentDownload!.errorMessage}', style: const TextStyle(color: Colors.red)), - ], - ), - ), - ), - - const SizedBox(height: 16), - - // 操作按钮 - Wrap( - spacing: 8, - runSpacing: 8, - children: [ - ElevatedButton.icon( - onPressed: () => _checkUpdate(), - icon: const Icon(Icons.refresh), - label: const Text('检查更新'), - ), - if (_currentDownload != null) ...[ - if (_currentDownload!.status == DownloadStatus.downloading) - ElevatedButton.icon( - onPressed: () => _plugin.pauseDownload(), - icon: const Icon(Icons.pause), - label: const Text('暂停'), - ), - if (_currentDownload!.status == DownloadStatus.paused) - ElevatedButton.icon( - onPressed: () => _plugin.resumeDownload(), - icon: const Icon(Icons.play_arrow), - label: const Text('继续'), - ), - if (_currentDownload!.status == DownloadStatus.failed) - ElevatedButton.icon( - onPressed: () => _plugin.retryDownload(), - icon: const Icon(Icons.replay), - label: const Text('重试'), - ), - OutlinedButton.icon( - onPressed: () => _plugin.cancelDownload(), - icon: const Icon(Icons.cancel), - label: const Text('取消'), - ), - ], - ], - ), - ], - ), - ); - } - - // 高级配置标签页 - Widget _buildConfigTab() { - return SingleChildScrollView( - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - '升级配置', - style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), - ), - const SizedBox(height: 16), - - SwitchListTile( - title: const Text('仅WiFi下载'), - subtitle: const Text('只在WiFi环境下自动下载更新'), - value: _wifiOnly, - onChanged: (value) { - setState(() { - _wifiOnly = value; - }); - _plugin.configure(wifiOnly: value); - }, - ), - - SwitchListTile( - title: const Text('自动检查更新'), - subtitle: const Text('定期自动检查是否有新版本'), - value: _autoCheck, - onChanged: (value) { - setState(() { - _autoCheck = value; - }); - _plugin.configure(autoCheck: value); - }, - ), - - SwitchListTile( - title: const Text('断点续传'), - subtitle: const Text('支持暂停后继续下载'), - value: _supportBreakpoint, - onChanged: (value) { - setState(() { - _supportBreakpoint = value; - }); - _plugin.configure(supportBreakpoint: value); - }, - ), - - SwitchListTile( - title: const Text('文件校验'), - subtitle: const Text('下载完成后校验文件完整性'), - value: _verifyIntegrity, - onChanged: (value) { - setState(() { - _verifyIntegrity = value; - }); - _plugin.configure(verifyIntegrity: value); - }, - ), - - const Divider(), - - ListTile( - title: const Text('版本比较策略'), - subtitle: Text(_getVersionStrategyText(_versionStrategy)), - trailing: DropdownButton( - value: _versionStrategy, - onChanged: (value) { - if (value != null) { - setState(() { - _versionStrategy = value; - }); - _plugin.configure(versionStrategy: value); - } - }, - items: VersionCompareStrategy.values.map((strategy) { - return DropdownMenuItem( - value: strategy, - child: Text(_getVersionStrategyText(strategy)), - ); - }).toList(), - ), - ), - - const Divider(), - - // 缓存管理 - Card( - child: Padding( - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - '缓存管理', - style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), - ), - const SizedBox(height: 12), - if (_cacheStats != null) ...[ - Text( - '内存缓存: ${_cacheStats!['memoryCache']['sizeFormatted']} (${_cacheStats!['memoryCache']['count']}项)'), - Text( - '磁盘缓存: ${_cacheStats!['diskCache']['sizeFormatted']} (${_cacheStats!['diskCache']['count']}项)'), - Text('总计: ${_cacheStats!['total']['sizeFormatted']} (${_cacheStats!['total']['count']}项)'), - ], - const SizedBox(height: 12), - Row( - children: [ - ElevatedButton( - onPressed: () async { - _cacheStats = await _plugin.getCacheStats(); - setState(() {}); - }, - child: const Text('刷新'), - ), - const SizedBox(width: 8), - OutlinedButton( - onPressed: () async { - await _plugin.clearCache(); - _cacheStats = await _plugin.getCacheStats(); - if (mounted) { - setState(() {}); - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('缓存已清空')), - ); - } - }, - child: const Text('清空缓存'), - ), - ], - ), - ], - ), - ), - ), - ], - ), - ); - } - - // 网络监测标签页 - Widget _buildNetworkTab() { - return SingleChildScrollView( - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - '网络状态', - style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), - ), - const SizedBox(height: 16), - if (_networkStatus != null) ...[ - Card( - color: _networkStatus!.isConnected ? Colors.green.shade50 : Colors.red.shade50, - child: Padding( - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Icon( - _networkStatus!.isConnected ? Icons.wifi : Icons.wifi_off, - color: _networkStatus!.isConnected ? Colors.green : Colors.red, - ), - const SizedBox(width: 8), - Text( - _networkStatus!.isConnected ? '已连接' : '未连接', - style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), - ), - ], - ), - const SizedBox(height: 12), - Text('网络类型: ${_getNetworkTypeText(_networkStatus!.type)}'), - Text('网络质量: ${_getNetworkQualityText(_networkStatus!.quality)}'), - Text('计费网络: ${_networkStatus!.isMetered ? "是" : "否"}'), - if (_networkStatus!.downloadSpeed != null) - Text('下载速度: ${_formatBytes(_networkStatus!.downloadSpeed!.toInt())}/s'), - if (_networkStatus!.ping != null) Text('延迟: ${_networkStatus!.ping} ms'), - const SizedBox(height: 12), - Text( - '适合大文件下载: ${_networkStatus!.isSuitableForLargeDownload ? "是" : "否"}', - style: TextStyle( - color: _networkStatus!.isSuitableForLargeDownload ? Colors.green : Colors.orange, - fontWeight: FontWeight.bold, - ), - ), - ], - ), - ), - ), - - const SizedBox(height: 16), - - // 下载策略建议 - Card( - child: Padding( - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - '下载策略建议', - style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), - ), - const SizedBox(height: 12), - ...NetworkMonitor.instance.getSuggestedDownloadStrategy().entries.map((e) { - return Text('${e.key}: ${e.value}'); - }), - ], - ), - ), - ), - ] else - const Center(child: CircularProgressIndicator()), - ], - ), - ); - } - - // 版本管理标签页 - Widget _buildVersionTab() { - final comparator = _plugin.versionComparator; - - return SingleChildScrollView( - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - '版本比较测试', - style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), - ), - const SizedBox(height: 16), - - // 版本选择 - Row( - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text('当前版本:'), - DropdownButton( - value: _currentTestVersion, - isExpanded: true, - onChanged: (value) { - setState(() { - _currentTestVersion = value!; - }); - }, - items: _testVersions.map((v) { - return DropdownMenuItem(value: v, child: Text(v)); - }).toList(), - ), - ], - ), - ), - const SizedBox(width: 16), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text('远程版本:'), - DropdownButton( - value: _remoteTestVersion, - isExpanded: true, - onChanged: (value) { - setState(() { - _remoteTestVersion = value!; - }); - }, - items: _testVersions.map((v) { - return DropdownMenuItem(value: v, child: Text(v)); - }).toList(), - ), - ], - ), - ), - ], - ), - - const SizedBox(height: 16), - - // 比较结果 - Card( - child: Padding( - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - '比较结果', - style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), - ), - const SizedBox(height: 12), - Text('需要更新: ${comparator.isUpdateAvailable(_currentTestVersion, _remoteTestVersion) ? "是" : "否"}'), - Text('更新类型: ${comparator.getVersionDifference(_currentTestVersion, _remoteTestVersion)}'), - Text('主要版本更新: ${comparator.isMajorUpdate(_currentTestVersion, _remoteTestVersion) ? "是" : "否"}'), - Text('次要版本更新: ${comparator.isMinorUpdate(_currentTestVersion, _remoteTestVersion) ? "是" : "否"}'), - Text('修订版本更新: ${comparator.isPatchUpdate(_currentTestVersion, _remoteTestVersion) ? "是" : "否"}'), - ], - ), - ), - ), - - const SizedBox(height: 16), - - // 版本列表排序 - Card( - child: Padding( - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - '版本排序', - style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), - ), - const SizedBox(height: 12), - Text('升序: ${comparator.sortVersions(_testVersions).join(', ')}'), - Text('降序: ${comparator.sortVersions(_testVersions, descending: true).join(', ')}'), - Text('最新版本: ${comparator.getLatestVersion(_testVersions)}'), - ], - ), - ), - ), - ], - ), - ); - } - - // 检查更新 - Future _checkUpdate() async { - // 模拟服务器地址 - const url = 'https://api.example.com/check-update'; - - final info = await _plugin.checkUpdateSmart( - url, - forceRefresh: true, - cacheDuration: const Duration(hours: 1), - ); - - if (info == null) { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('已是最新版本')), - ); - } - } - - // 工具方法 - String _formatBytes(int bytes) { - if (bytes < 1024) return '$bytes B'; - if (bytes < 1024 * 1024) return '${(bytes / 1024).toStringAsFixed(2)} KB'; - if (bytes < 1024 * 1024 * 1024) return '${(bytes / (1024 * 1024)).toStringAsFixed(2)} MB'; - return '${(bytes / (1024 * 1024 * 1024)).toStringAsFixed(2)} GB'; - } - - String _getDownloadStatusText(DownloadStatus status) { - switch (status) { - case DownloadStatus.pending: - return '等待中'; - case DownloadStatus.downloading: - return '下载中'; - case DownloadStatus.paused: - return '已暂停'; - case DownloadStatus.completed: - return '已完成'; - case DownloadStatus.failed: - return '失败'; - case DownloadStatus.cancelled: - return '已取消'; - } - } - - String _getNetworkTypeText(NetworkType type) { - switch (type) { - case NetworkType.none: - return '无网络'; - case NetworkType.mobile: - return '移动网络'; - case NetworkType.wifi: - return 'WiFi'; - case NetworkType.ethernet: - return '以太网'; - case NetworkType.bluetooth: - return '蓝牙'; - case NetworkType.vpn: - return 'VPN'; - case NetworkType.other: - return '其他'; - } - } - - String _getNetworkQualityText(NetworkQuality quality) { - switch (quality) { - case NetworkQuality.unknown: - return '未知'; - case NetworkQuality.poor: - return '差'; - case NetworkQuality.moderate: - return '中等'; - case NetworkQuality.good: - return '良好'; - case NetworkQuality.excellent: - return '优秀'; - } - } - - String _getVersionStrategyText(VersionCompareStrategy strategy) { - switch (strategy) { - case VersionCompareStrategy.numeric: - return '数字比较'; - case VersionCompareStrategy.semantic: - return '语义化版本'; - case VersionCompareStrategy.timestamp: - return '时间戳'; - case VersionCompareStrategy.buildNumber: - return '构建号'; - case VersionCompareStrategy.custom: - return '自定义'; - } - } } diff --git a/lib/app_upgrade_simple.dart b/lib/app_upgrade_simple.dart index 01d9189..af68112 100644 --- a/lib/app_upgrade_simple.dart +++ b/lib/app_upgrade_simple.dart @@ -37,10 +37,146 @@ class _SimpleAppUpgradePlugin { Future> getAppInfo() { return AppUpgradePluginPlatform.instance.getAppInfo(); } + + /// 检查当前应用的版本信息(用于安装状态检测) + Future> getCurrentAppInfo() async { + try { + return await AppUpgradePluginPlatform.instance.getAppInfo(); + } catch (e) { + debugPrint('获取应用信息失败: $e'); + return {}; + } + } + + /// 获取下载路径 + Future getDownloadPath() { + return AppUpgradePluginPlatform.instance.getDownloadPath(); + } + + /// 精确检测应用是否已安装(通过包名) + Future isPackageInstalled(String packageName) async { + try { + // 通过比较当前应用的包名来检测 + final appInfo = await getAppInfo(); + final currentPackage = appInfo['packageName'] ?? ''; + + debugPrint('检查包安装状态: 当前包名=$currentPackage, 目标包名=$packageName'); + + // 如果是检查当前应用的安装状态,通过版本号变化来判断 + if (currentPackage == packageName) { + return true; // 当前应用肯定已安装 + } + + return false; + } catch (e) { + debugPrint('检查包安装状态失败: $e'); + return false; + } + } + + /// 比较版本号是否已更新 + Future isVersionUpdated(String targetVersion, int? targetBuildNumber) async { + try { + final appInfo = await getCurrentAppInfo(); + final currentVersion = appInfo['version'] ?? ''; + final currentBuildNumber = int.tryParse(appInfo['buildNumber'] ?? '0') ?? 0; + + debugPrint('版本对比: 当前版本=$currentVersion, 目标版本=$targetVersion'); + debugPrint('构建号对比: 当前构建号=$currentBuildNumber, 目标构建号=$targetBuildNumber'); + + // 优先比较构建号(更准确) + if (targetBuildNumber != null && targetBuildNumber > 0) { + return currentBuildNumber >= targetBuildNumber; + } + + // 备用方案:比较版本号 + return currentVersion == targetVersion; + } catch (e) { + debugPrint('版本对比失败: $e'); + return false; + } + } +} + +/// 升级配置选项 +class UpgradeConfig { + /// 是否显示无更新提示 + final bool showNoUpdateToast; + + /// 是否自动下载 + final bool autoDownload; + + /// 是否自动安装 + final bool autoInstall; + + /// 连接超时时间(秒) + final int connectionTimeout; + + /// 下载超时时间(秒) + final int downloadTimeout; + + /// 安装检测超时时间(秒) + final int installTimeout; + + /// 是否启用调试日志 + final bool enableDebugLog; + + /// 自定义Toast显示函数 + final void Function(String message)? customToast; + + const UpgradeConfig({ + this.showNoUpdateToast = true, + this.autoDownload = false, + this.autoInstall = false, + this.connectionTimeout = 30, + this.downloadTimeout = 300, + this.installTimeout = 45, + this.enableDebugLog = true, + this.customToast, + }); + + /// 快速配置:自动更新 + static const UpgradeConfig auto = UpgradeConfig( + autoDownload: true, + autoInstall: true, + ); + + /// 快速配置:静默检查 + static const UpgradeConfig silent = UpgradeConfig( + showNoUpdateToast: false, + enableDebugLog: false, + ); + + /// 快速配置:开发模式(详细日志 + 较短超时) + static const UpgradeConfig development = UpgradeConfig( + enableDebugLog: true, + installTimeout: 30, + connectionTimeout: 10, + ); + + /// 快速配置:生产模式(静默 + 较长超时) + static const UpgradeConfig production = UpgradeConfig( + showNoUpdateToast: false, + enableDebugLog: false, + installTimeout: 60, + connectionTimeout: 30, + ); } /// 简化版App升级管理器 /// 提供最简单的API,一行代码即可实现App升级功能 +/// +/// 版本: 2.0.0 +/// 更新日期: 2025-09-18 +/// +/// 主要特性: +/// - 🎨 现代化UI设计,支持Material Design 3 +/// - 🔄 智能安装状态检测,支持多种安装场景 +/// - 📱 完整的生命周期管理和错误处理 +/// - ⚙️ 灵活的配置系统,支持多种使用模式 +/// - 🎯 富文本支持,更好的内容展示 +/// - 🚀 高性能,低内存占用 +/// - 🛡️ 完善的权限处理和安全检查 class AppUpgradeSimple { static AppUpgradeSimple? _instance; @@ -61,10 +197,60 @@ class AppUpgradeSimple { AppUpgradeSimple._() : _plugin = _SimpleAppUpgradePlugin.instance; final _SimpleAppUpgradePlugin _plugin; + UpgradeConfig _config = const UpgradeConfig(); + + /// 配置升级参数 + void configure(UpgradeConfig config) { + _config = config; + } + + /// 快速配置:一键设置自动更新 + void enableAutoUpdate() { + _config = UpgradeConfig.auto; + } + + /// 快速配置:一键设置静默检查 + void enableSilentCheck() { + _config = UpgradeConfig.silent; + } + + /// 检查网络连接状态 + Future checkNetworkStatus() async { + try { + final result = await InternetAddress.lookup('google.com'); + return result.isNotEmpty && result[0].rawAddress.isNotEmpty; + } catch (e) { + debugPrint('网络检查失败: $e'); + return false; + } + } + + /// 清理下载的临时文件 + Future clearDownloadCache() async { + try { + final downloadPath = await _plugin.getDownloadPath(); + if (downloadPath != null) { + final dir = Directory(downloadPath); + if (await dir.exists()) { + final files = await dir.list().where((file) => file.path.endsWith('.apk')).toList(); + for (final file in files) { + try { + await file.delete(); + debugPrint('已删除缓存文件: ${file.path}'); + } catch (e) { + debugPrint('删除缓存文件失败: ${file.path}, 错误: $e'); + } + } + } + } + } catch (e) { + debugPrint('清理下载缓存失败: $e'); + } + } /// 一键检查更新(最简单的使用方式) /// - /// 示例: + /// 基础用法: /// ```dart /// AppUpgradeSimple.instance.checkUpdate( /// context: context, @@ -72,25 +258,64 @@ class AppUpgradeSimple { /// ); /// ``` /// + /// 高级用法: + /// ```dart + /// // 配置自动更新 + /// AppUpgradeSimple.instance.configure(UpgradeConfig.auto); + /// + /// // 或者使用自定义配置 + /// AppUpgradeSimple.instance.configure(UpgradeConfig( + /// autoDownload: true, + /// autoInstall: false, + /// installTimeout: 60, + /// customToast: (message) => ScaffoldMessenger.of(context).showSnackBar( + /// SnackBar(content: Text(message)), + /// ), + /// )); + /// + /// // 检查更新 + /// await AppUpgradeSimple.instance.checkUpdate( + /// context: context, + /// url: 'https://api.example.com/check-update', + /// params: {'userId': '123', 'channel': 'official'}, + /// onComplete: () => print('更新检查完成'), + /// ); + /// ``` + /// /// [context] 需要是一个能够访问到 `Navigator` 和 `MaterialLocalizations` 的有效上下文。 - /// 如果您不确定,建议在 `init` 方法中提供 `navigatorKey`,这样插件可以获取一个可靠的上下文。 + /// [url] 检查更新的API接口地址 + /// [params] 可选的请求参数 + /// [config] 可选的配置覆盖 + /// [onComplete] 更新流程完成后的回调 + /// 一键检查更新(支持配置) Future checkUpdate({ required BuildContext context, required String url, Map? params, - bool showNoUpdateToast = true, - bool autoDownload = false, - bool autoInstall = false, + bool? showNoUpdateToast, + bool? autoDownload, + bool? autoInstall, VoidCallback? onComplete, + UpgradeConfig? config, }) async { + // 使用传入的配置或默认配置 + final effectiveConfig = config ?? _config; + final finalShowNoUpdateToast = showNoUpdateToast ?? effectiveConfig.showNoUpdateToast; + final finalAutoDownload = autoDownload ?? effectiveConfig.autoDownload; + final finalAutoInstall = autoInstall ?? effectiveConfig.autoInstall; try { assert(_canShowMaterialDialog(context), '请在 MaterialApp 环境内调用'); // 检查更新 final info = await _plugin.checkUpdate(url, params: params); - print('info: $info'); + if (effectiveConfig.enableDebugLog) { + debugPrint('🔍 检查更新结果: $info'); + } + if (info == null || !info.hasUpdate) { - if (showNoUpdateToast && context.mounted) _showToast('已是最新版本'); + if (finalShowNoUpdateToast && context.mounted) { + _showToast('已是最新版本', effectiveConfig); + } onComplete?.call(); return; } @@ -98,9 +323,10 @@ class AppUpgradeSimple { await _showUpgradeDialog( context: context, info: info, - autoDownload: autoDownload, - autoInstall: autoInstall, + autoDownload: finalAutoDownload, + autoInstall: finalAutoInstall, onComplete: onComplete, + config: effectiveConfig, ); } catch (e) { debugPrint('检查更新失败: $e'); @@ -121,11 +347,13 @@ class AppUpgradeSimple { } if (context.mounted) { - _showToast(errorMessage); + _showToast(errorMessage, effectiveConfig); // 如果是网络问题,显示网络诊断建议 if (errorString.contains('Failed host lookup') || errorString.contains('无网络连接')) { - debugPrint('建议: 请检查网络连接或尝试使用网络诊断功能'); + if (effectiveConfig.enableDebugLog) { + debugPrint('💡 建议: 请检查网络连接或尝试使用网络诊断功能'); + } } } onComplete?.call(); @@ -140,11 +368,60 @@ class AppUpgradeSimple { try { return await _plugin.checkUpdate(url, params: params); } catch (e) { - debugPrint('静默检查更新失败: $e'); + if (_config.enableDebugLog) { + debugPrint('静默检查更新失败: $e'); + } return null; } } + /// 预下载APK(不显示UI,后台下载) + Future preDownloadApk({ + required String url, + Function(DownloadProgress)? onProgress, + }) async { + try { + return await _plugin.downloadApk(url, onProgress: onProgress); + } catch (e) { + if (_config.enableDebugLog) { + debugPrint('预下载APK失败: $e'); + } + return null; + } + } + + /// 检查是否有已下载的APK文件 + Future findDownloadedApk(String version) async { + try { + final downloadPath = await _plugin.getDownloadPath(); + if (downloadPath != null) { + final dir = Directory(downloadPath); + if (await dir.exists()) { + final files = await dir.list().toList(); + for (final file in files) { + if (file.path.contains(version) && file.path.endsWith('.apk')) { + final fileEntity = File(file.path); + if (await fileEntity.exists()) { + return file.path; + } + } + } + } + } + return null; + } catch (e) { + if (_config.enableDebugLog) { + debugPrint('查找已下载APK失败: $e'); + } + return null; + } + } + + /// 获取当前应用版本信息 + Future> getAppInfo() async { + return await _plugin.getAppInfo(); + } + /// 显示升级对话框 Future _showUpgradeDialog({ required BuildContext context, @@ -152,20 +429,27 @@ class AppUpgradeSimple { required bool autoDownload, required bool autoInstall, VoidCallback? onComplete, + UpgradeConfig? config, }) { + final effectiveConfig = config ?? _config; + return showDialog( context: context, barrierDismissible: !info.isForceUpdate, builder: (context) { if (info.isForceUpdate) { - return _ForceUpgradeDialog(info: info); + return _ForceUpgradeDialog( + info: info, + config: effectiveConfig, + ); } else { return _SimpleUpgradeDialog( info: info, autoDownload: autoDownload, autoInstall: autoInstall, onComplete: onComplete, - showToast: (message) => _showToast(message), + config: effectiveConfig, + showToast: (message) => _showToast(message, effectiveConfig), ); } }, @@ -173,8 +457,20 @@ class AppUpgradeSimple { } /// 显示Toast提示 - void _showToast(String message) { - Fluttertoast.showToast(msg: message); + void _showToast(String message, [UpgradeConfig? config]) { + final effectiveConfig = config ?? _config; + if (effectiveConfig.customToast != null) { + effectiveConfig.customToast!(message); + } else { + Fluttertoast.showToast( + msg: message, + toastLength: Toast.LENGTH_SHORT, + gravity: ToastGravity.CENTER, + backgroundColor: Colors.black87, + textColor: Colors.white, + fontSize: 14.0, + ); + } } /// 检查是否存在可用的 Material 环境(仅用于对话框) @@ -206,6 +502,7 @@ mixin _UpgradeDialogLogic on State { VoidCallback? get onComplete; bool get autoDownload; bool get autoInstall; + UpgradeConfig get config; void initUpgradeLogic() { if (autoDownload && Platform.isAndroid) { @@ -222,13 +519,14 @@ mixin _UpgradeDialogLogic on State { } void onAppLifecycleStateChanged(AppLifecycleState state) { - debugPrint('应用生命周期状态变化: $state, _isWaitingForInstallation=$_isWaitingForInstallation'); + debugPrint('🔄 应用生命周期状态变化: $state, _isWaitingForInstallation=$_isWaitingForInstallation'); - // 当应用从后台回到前台时,检查是否是从安装界面返回 - if (state == AppLifecycleState.resumed && _isWaitingForInstallation) { - debugPrint('应用回到前台,准备检查安装状态'); - // 延迟一点时间再检查,确保系统状态稳定 - Future.delayed(const Duration(milliseconds: 500), () { + // 核心逻辑:当应用从后台回到前台时,检查安装状态 + if (_isWaitingForInstallation && state == AppLifecycleState.resumed) { + debugPrint('⚡ 应用回到前台,检查安装状态'); + + // 延迟检测,让系统状态稳定 + Future.delayed(const Duration(milliseconds: 1500), () { if (mounted && _isWaitingForInstallation) { _checkInstallationResult(); } @@ -321,7 +619,7 @@ mixin _UpgradeDialogLogic on State { _isWaitingForInstallation = true; _statusText = '请完成安装'; }); - showToast('请在系统弹窗中完成安装,如取消可点击下方按钮重试'); + showToast('请在系统弹窗中完成安装,完成后点击下方确认按钮'); // 启动定时检测(备用方案) _startInstallationTimeoutCheck(); @@ -345,80 +643,98 @@ mixin _UpgradeDialogLogic on State { } } - /// 启动安装超时检测 + /// 启动简化的安装检测(主要依靠应用生命周期) void _startInstallationTimeoutCheck() { - // 5秒后自动检测一次(快速检测) - Timer(const Duration(seconds: 5), () { - if (mounted && _isWaitingForInstallation && _statusText == '请完成安装') { - debugPrint('5秒后自动检测安装状态'); - _checkInstallationResult(); - } - }); + debugPrint('🚀 启动简化安装检测系统'); - // 30秒后如果还在等待安装,则提示超时 - _installCheckTimer = Timer(const Duration(seconds: 30), () { - if (mounted && _isWaitingForInstallation && _statusText == '请完成安装') { + // 核心策略:主要依靠应用生命周期监听,减少定时检测 + + // 仅保留一个简单的超时检测 + _installCheckTimer = Timer(Duration(seconds: config.installTimeout), () { + if (mounted && _isWaitingForInstallation) { + debugPrint('⏰ 安装检测超时'); setState(() { _isWaitingForInstallation = false; _statusText = '安装超时'; }); - showToast('安装检测超时,请检查是否已安装成功'); + showToast('安装超时,请使用重试按钮重新安装'); } }); } - /// 检查安装结果(当应用回到前台时调用) + /// 简化的安装结果检测(仅版本检测) Future _checkInstallationResult() async { if (!mounted || !_isWaitingForInstallation) { debugPrint('跳过安装结果检查: mounted=$mounted, _isWaitingForInstallation=$_isWaitingForInstallation'); return; } - debugPrint('开始检查安装结果...'); + debugPrint('🔍 开始简化安装检测...'); try { // 获取当前应用信息 - final appInfo = await _plugin.getAppInfo(); - final currentVersion = appInfo['versionName'] ?? ''; + final appInfo = await _plugin.getCurrentAppInfo(); + final currentVersion = appInfo['version'] ?? ''; + final currentBuildNumber = int.tryParse(appInfo['buildNumber'] ?? '0') ?? 0; - debugPrint('检查安装结果: 当前版本=$currentVersion, 目标版本=${info.versionName}'); + debugPrint('📱 当前版本: $currentVersion, 构建号: $currentBuildNumber'); + debugPrint('🎯 目标版本: ${info.versionName}, 构建号: ${info.versionBuildNumber}'); - // 如果版本号已更新,说明安装成功 - if (currentVersion == info.versionName) { - debugPrint('检测到版本已更新,安装成功'); - _installCheckTimer?.cancel(); - setState(() { - _isWaitingForInstallation = false; - _statusText = '安装成功'; - }); - showToast('应用更新成功!'); - - // 延迟关闭对话框 - Future.delayed(const Duration(seconds: 1), () { - if (mounted && Navigator.canPop(context)) { - Navigator.of(context).pop(); - } - onComplete?.call(); - }); + // 优先比较构建号(更准确) + bool isUpdated = false; + if (info.versionBuildNumber > 0) { + isUpdated = currentBuildNumber >= info.versionBuildNumber; + debugPrint('📊 构建号比较: $currentBuildNumber >= ${info.versionBuildNumber} = $isUpdated'); } else { - // 版本号未更新,用户可能取消了安装 - debugPrint('版本号未更新,用户可能取消了安装'); - setState(() { - _isWaitingForInstallation = false; - _statusText = '安装被取消'; - }); - showToast('安装被取消,可以重新尝试安装'); + // 备用方案:比较版本号 + isUpdated = currentVersion == info.versionName; + debugPrint('📊 版本号比较: $currentVersion == ${info.versionName} = $isUpdated'); + } + + if (isUpdated) { + debugPrint('✅ 检测结果: 安装成功'); + _handleInstallationSuccess(); + } else { + debugPrint('❌ 检测结果: 安装被取消(版本未更新)'); + _handleInstallationCancelled(); } } catch (e) { - debugPrint('检测应用状态失败: $e'); + debugPrint('❌ 检测失败: $e'); setState(() { _isWaitingForInstallation = false; _statusText = '检测失败'; }); - showToast('无法检测安装状态,请手动检查'); + showToast('无法检测安装状态,请手动确认'); } } + /// 处理安装成功 + void _handleInstallationSuccess() { + _installCheckTimer?.cancel(); + setState(() { + _isWaitingForInstallation = false; + _statusText = '安装成功'; + }); + showToast('应用更新成功!'); + + // 延迟关闭对话框 + Future.delayed(const Duration(seconds: 1), () { + if (mounted && Navigator.canPop(context)) { + Navigator.of(context).pop(); + } + onComplete?.call(); + }); + } + + /// 处理安装被取消 + void _handleInstallationCancelled() { + setState(() { + _isWaitingForInstallation = false; + _statusText = '安装被取消'; + }); + showToast('安装被取消,可以点击重试按钮重新安装'); + } + /// 重新安装APK Future _retryInstall() async { if (_downloadedFilePath != null) { @@ -842,16 +1158,21 @@ mixin _UpgradeDialogLogic on State { // 下载状态图标和文本 Row( children: [ - Container( + AnimatedContainer( + duration: const Duration(milliseconds: 300), padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: _getStatusColor(colorScheme).withOpacity(0.1), shape: BoxShape.circle, ), - child: Icon( - _getStatusIcon(), - color: _getStatusColor(colorScheme), - size: 20, + child: AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + child: Icon( + _getStatusIcon(), + key: ValueKey(_statusText), + color: _getStatusColor(colorScheme), + size: 20, + ), ), ), const SizedBox(width: 12), @@ -859,12 +1180,16 @@ mixin _UpgradeDialogLogic on State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - _statusText, - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w500, - color: colorScheme.onSurface, + AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + child: Text( + _statusText, + key: ValueKey(_statusText), + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: colorScheme.onSurface, + ), ), ), const SizedBox(height: 2), @@ -878,81 +1203,56 @@ mixin _UpgradeDialogLogic on State { ), ), // 添加点击提示(当有错误状态或等待用户操作时) - if (showRetryButton && - (_statusText == '安装失败' || - _statusText == '安装异常' || - _statusText == '权限被拒绝' || - _statusText == '请完成安装' || - _statusText == '安装超时' || - _statusText == '安装被取消' || - _statusText == '检测失败')) + if (_shouldShowRetryOptions()) Text( - _statusText == '请完成安装' ? '可重新启动安装' : '点击区域重试', + _getClickHintText(), style: TextStyle( fontSize: 11, color: colorScheme.onSurface.withOpacity(0.6), ), ), - // 为"请完成安装"状态添加操作按钮 + // 为"请完成安装"状态添加简单提示 if (_statusText == '请完成安装') Padding( - padding: const EdgeInsets.only(top: 8), - child: Row( - children: [ - Expanded( - child: ElevatedButton.icon( - onPressed: _checkInstallationResult, - icon: const Icon(Icons.refresh, size: 14), - label: const Text('检查状态', style: TextStyle(fontSize: 11)), - style: ElevatedButton.styleFrom( - padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), - minimumSize: Size.zero, - tapTargetSize: MaterialTapTargetSize.shrinkWrap, - backgroundColor: colorScheme.secondary.withOpacity(0.8), - ), - ), + padding: const EdgeInsets.only(top: 12), + child: Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: colorScheme.primaryContainer.withOpacity(0.2), + borderRadius: BorderRadius.circular(8), + border: Border.all( + color: colorScheme.primary.withOpacity(0.3), + width: 1, ), - const SizedBox(width: 4), - Expanded( - child: ElevatedButton.icon( - onPressed: () async { - // 直接重新安装,不检查状态 - if (_downloadedFilePath != null) { - await _installApk(_downloadedFilePath!); - } - }, - icon: const Icon(Icons.install_mobile, size: 14), - label: const Text('重新安装', style: TextStyle(fontSize: 11)), - style: ElevatedButton.styleFrom( - padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), - minimumSize: Size.zero, - tapTargetSize: MaterialTapTargetSize.shrinkWrap, - backgroundColor: colorScheme.primary, - ), + ), + child: Column( + children: [ + Icon( + Icons.touch_app, + color: colorScheme.primary, + size: 24, ), - ), - const SizedBox(width: 4), - // 测试按钮:模拟取消安装 - Expanded( - child: ElevatedButton.icon( - onPressed: () { - setState(() { - _isWaitingForInstallation = false; - _statusText = '安装被取消'; - }); - showToast('已模拟安装被取消,现在应该显示重试按钮'); - }, - icon: const Icon(Icons.cancel, size: 14), - label: const Text('模拟取消', style: TextStyle(fontSize: 10)), - style: ElevatedButton.styleFrom( - padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4), - minimumSize: Size.zero, - tapTargetSize: MaterialTapTargetSize.shrinkWrap, - backgroundColor: colorScheme.error.withOpacity(0.8), + const SizedBox(height: 8), + Text( + '请在系统安装界面完成操作', + style: TextStyle( + fontSize: 13, + color: colorScheme.onSurface, + fontWeight: FontWeight.w500, ), + textAlign: TextAlign.center, ), - ), - ], + const SizedBox(height: 4), + Text( + '系统将自动检测安装结果', + style: TextStyle( + fontSize: 11, + color: colorScheme.onSurface.withOpacity(0.7), + ), + textAlign: TextAlign.center, + ), + ], + ), ), ), ], @@ -1046,6 +1346,12 @@ mixin _UpgradeDialogLogic on State { return Icons.check_circle; } else if (_statusText == '请完成安装') { return Icons.touch_app; + } else if (_statusText == '等待安装中') { + return Icons.hourglass_bottom; + } else if (_statusText == '等待确认中') { + return Icons.help_outline; + } else if (_statusText == '等待超时') { + return Icons.timer_off; } else if (_statusText == '安装被取消') { return Icons.cancel_outlined; } else if (_statusText == '安装超时' || _statusText == '检测失败') { @@ -1067,6 +1373,12 @@ mixin _UpgradeDialogLogic on State { return colorScheme.tertiary; } else if (_statusText == '请完成安装') { return colorScheme.secondary; + } else if (_statusText == '等待安装中') { + return colorScheme.secondary.withOpacity(0.9); + } else if (_statusText == '等待确认中') { + return Colors.orange; + } else if (_statusText == '等待超时') { + return colorScheme.error.withOpacity(0.7); } else if (_statusText == '安装被取消') { return colorScheme.secondary.withOpacity(0.8); } else if (_statusText == '安装超时' || _statusText == '检测失败') { @@ -1088,7 +1400,10 @@ mixin _UpgradeDialogLogic on State { _statusText == '安装异常' || _statusText == '安装超时' || _statusText == '安装被取消' || - _statusText == '检测失败') { + _statusText == '检测失败' || + _statusText == '等待安装中' || + _statusText == '等待确认中' || + _statusText == '等待超时') { return Icons.refresh; } else if (_statusText == '请完成安装') { return Icons.launch; @@ -1105,8 +1420,11 @@ mixin _UpgradeDialogLogic on State { _statusText == '安装异常' || _statusText == '安装超时' || _statusText == '安装被取消' || - _statusText == '检测失败') { + _statusText == '检测失败' || + _statusText == '等待超时') { return '重试'; + } else if (_statusText == '等待安装中' || _statusText == '等待确认中') { + return '重新安装'; } else if (_statusText == '请完成安装') { return '重新安装'; } else { @@ -1120,7 +1438,7 @@ mixin _UpgradeDialogLogic on State { return colorScheme.secondary; } else if (_statusText == '安装失败' || _statusText == '安装异常') { return colorScheme.error; - } else if (_statusText == '安装超时' || _statusText == '安装被取消' || _statusText == '检测失败') { + } else if (_statusText == '安装超时' || _statusText == '安装被取消' || _statusText == '检测失败' || _statusText == '等待安装中') { return colorScheme.secondary.withOpacity(0.8); } else if (_statusText == '请完成安装') { return colorScheme.secondary; @@ -1137,6 +1455,9 @@ mixin _UpgradeDialogLogic on State { _statusText == '权限被拒绝' || _statusText == '安装超时' || _statusText == '检测失败' || + _statusText == '等待安装中' || + _statusText == '等待确认中' || + _statusText == '等待超时' || (_downloadedFilePath != null && !_isDownloading && !_isInstalling && @@ -1145,6 +1466,28 @@ mixin _UpgradeDialogLogic on State { _statusText != '安装成功'); } + /// 获取点击提示文本 + String _getClickHintText() { + switch (_statusText) { + case '请完成安装': + return '完成后请点击确认按钮'; + case '等待安装中': + return '点击重新安装'; + case '等待确认中': + return '请点击确认按钮'; + case '等待超时': + return '点击重新安装'; + case '安装被取消': + return '点击重新安装'; + case '权限被拒绝': + return '点击打开设置'; + case '安装超时': + return '点击重新尝试'; + default: + return '点击区域重试'; + } + } + void _handleAction() { if (Platform.isAndroid) { _handleAndroidAction(); @@ -1341,6 +1684,7 @@ class _SimpleUpgradeDialog extends StatefulWidget { final bool autoInstall; final VoidCallback? onComplete; final void Function(String) showToast; + final UpgradeConfig config; const _SimpleUpgradeDialog({ required this.info, @@ -1348,6 +1692,7 @@ class _SimpleUpgradeDialog extends StatefulWidget { required this.autoInstall, this.onComplete, required this.showToast, + required this.config, }); @override @@ -1365,6 +1710,8 @@ class _SimpleUpgradeDialogState extends State<_SimpleUpgradeDialog> with _Upgrad bool get autoDownload => widget.autoDownload; @override bool get autoInstall => widget.autoInstall; + @override + UpgradeConfig get config => widget.config; @override void initState() { @@ -1477,8 +1824,12 @@ class _SimpleUpgradeDialogState extends State<_SimpleUpgradeDialog> with _Upgrad /// 强制更新对话框 class _ForceUpgradeDialog extends StatefulWidget { final UpgradeInfo info; + final UpgradeConfig config; - const _ForceUpgradeDialog({required this.info}); + const _ForceUpgradeDialog({ + required this.info, + required this.config, + }); @override State<_ForceUpgradeDialog> createState() => _ForceUpgradeDialogState(); @@ -1488,13 +1839,15 @@ class _ForceUpgradeDialogState extends State<_ForceUpgradeDialog> with _UpgradeD @override UpgradeInfo get info => widget.info; @override - void Function(String) get showToast => (message) => AppUpgradeSimple.instance._showToast(message); + void Function(String) get showToast => (message) => AppUpgradeSimple.instance._showToast(message, widget.config); @override VoidCallback? get onComplete => null; @override bool get autoDownload => false; @override bool get autoInstall => true; + @override + UpgradeConfig get config => widget.config; @override void initState() {