1111
This commit is contained in:
parent
223f225d94
commit
3d1729f630
|
|
@ -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<MyHomePage> createState() => _MyHomePageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MyHomePageState extends State<MyHomePage> {
|
||||||
|
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` - 生产模式
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
这个插件现在提供了完整的应用升级解决方案,支持各种使用场景和自定义需求!
|
||||||
|
|
@ -1,738 +1,335 @@
|
||||||
import 'dart:async';
|
import 'package:app_upgrade_plugin/app_upgrade_plugin.dart';
|
||||||
|
|
||||||
import 'package:app_upgrade_plugin/app_upgrade_plugin_enhanced.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:permission_handler/permission_handler.dart';
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
runApp(const MyEnhancedApp());
|
runApp(const MyApp());
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyEnhancedApp extends StatelessWidget {
|
class MyApp extends StatelessWidget {
|
||||||
const MyEnhancedApp({super.key});
|
const MyApp({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
title: '增强版App升级插件示例',
|
title: 'App Upgrade Plugin Enhanced Demo',
|
||||||
theme: ThemeData(
|
theme: ThemeData(
|
||||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
|
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
|
||||||
useMaterial3: true,
|
useMaterial3: true,
|
||||||
),
|
),
|
||||||
home: const EnhancedUpgradePage(),
|
home: const MyHomePage(title: 'App升级插件完整示例'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class EnhancedUpgradePage extends StatefulWidget {
|
class MyHomePage extends StatefulWidget {
|
||||||
const EnhancedUpgradePage({super.key});
|
const MyHomePage({super.key, required this.title});
|
||||||
|
|
||||||
|
final String title;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<EnhancedUpgradePage> createState() => _EnhancedUpgradePageState();
|
State<MyHomePage> createState() => _MyHomePageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _EnhancedUpgradePageState extends State<EnhancedUpgradePage> with SingleTickerProviderStateMixin {
|
class _MyHomePageState extends State<MyHomePage> {
|
||||||
final _plugin = AppUpgradePluginEnhanced.instance;
|
String _status = '准备就绪';
|
||||||
|
bool _isLoading = false;
|
||||||
late TabController _tabController;
|
|
||||||
|
|
||||||
// 状态变量
|
|
||||||
Map<String, String>? _appInfo;
|
|
||||||
NetworkStatus? _networkStatus;
|
|
||||||
DownloadTask? _currentDownload;
|
|
||||||
UpgradeInfo? _upgradeInfo;
|
|
||||||
Map<String, dynamic>? _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';
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_tabController = TabController(length: 4, vsync: this);
|
_initializePlugin();
|
||||||
_initPlugin();
|
|
||||||
_loadData();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
/// 初始化插件
|
||||||
void dispose() {
|
void _initializePlugin() {
|
||||||
_tabController.dispose();
|
|
||||||
_plugin.removeUpgradeCallback(_onUpgradeInfo);
|
|
||||||
_plugin.removeDownloadCallback(_onDownloadProgress);
|
|
||||||
_plugin.removeErrorCallback(_onError);
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _initPlugin() {
|
|
||||||
// 配置插件
|
// 配置插件
|
||||||
_plugin.configure(
|
AppUpgradeSimple.instance.configure(const UpgradeConfig(
|
||||||
debugMode: true,
|
enableDebugLog: true,
|
||||||
autoCheck: _autoCheck,
|
installTimeout: 60,
|
||||||
wifiOnly: _wifiOnly,
|
customToast: null, // 使用默认Toast
|
||||||
supportBreakpoint: _supportBreakpoint,
|
));
|
||||||
verifyIntegrity: _verifyIntegrity,
|
}
|
||||||
versionStrategy: _versionStrategy,
|
|
||||||
|
/// 基础检查更新
|
||||||
|
Future<void> _checkUpdate() async {
|
||||||
|
setState(() {
|
||||||
|
_isLoading = true;
|
||||||
|
_status = '检查更新中...';
|
||||||
|
});
|
||||||
|
|
||||||
|
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';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 自动更新
|
||||||
|
Future<void> _autoUpdate() async {
|
||||||
|
setState(() {
|
||||||
|
_isLoading = true;
|
||||||
|
_status = '自动更新中...';
|
||||||
|
});
|
||||||
|
|
||||||
|
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';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 静默检查
|
||||||
|
Future<void> _silentCheck() async {
|
||||||
|
setState(() {
|
||||||
|
_isLoading = true;
|
||||||
|
_status = '静默检查中...';
|
||||||
|
});
|
||||||
|
|
||||||
|
final info = await AppUpgradeSimple.instance.checkUpdateSilent(
|
||||||
|
url: 'https://api.example.com/check-update',
|
||||||
);
|
);
|
||||||
|
|
||||||
// 添加回调
|
|
||||||
_plugin.addUpgradeCallback(_onUpgradeInfo);
|
|
||||||
_plugin.addDownloadCallback(_onDownloadProgress);
|
|
||||||
_plugin.addErrorCallback(_onError);
|
|
||||||
|
|
||||||
// 监听网络状态
|
|
||||||
NetworkMonitor.instance.statusStream.listen((status) {
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_networkStatus = status;
|
_isLoading = false;
|
||||||
});
|
if (info != null && info.hasUpdate) {
|
||||||
|
_status = '发现新版本: ${info.versionName}';
|
||||||
|
} else {
|
||||||
|
_status = '已是最新版本';
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _loadData() async {
|
/// 清理缓存
|
||||||
// 请求权限
|
Future<void> _clearCache() async {
|
||||||
await _requestPermissions();
|
|
||||||
|
|
||||||
// 获取App信息
|
|
||||||
_appInfo = await _plugin.getAppInfo();
|
|
||||||
|
|
||||||
// 获取网络状态
|
|
||||||
_networkStatus = _plugin.networkStatus;
|
|
||||||
|
|
||||||
// 获取缓存统计
|
|
||||||
_cacheStats = await _plugin.getCacheStats();
|
|
||||||
|
|
||||||
setState(() {});
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _requestPermissions() async {
|
|
||||||
if (Theme.of(context).platform == TargetPlatform.android) {
|
|
||||||
await [
|
|
||||||
Permission.storage,
|
|
||||||
Permission.requestInstallPackages,
|
|
||||||
Permission.notification,
|
|
||||||
].request();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onUpgradeInfo(UpgradeInfo info) {
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_upgradeInfo = info;
|
_isLoading = true;
|
||||||
|
_status = '清理缓存中...';
|
||||||
});
|
});
|
||||||
|
|
||||||
// 显示升级对话框
|
await AppUpgradeSimple.instance.clearDownloadCache();
|
||||||
UpgradeDialog.show(
|
|
||||||
context,
|
|
||||||
upgradeInfo: info,
|
|
||||||
primaryColor: Theme.of(context).colorScheme.primary,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onDownloadProgress(DownloadTask task) {
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_currentDownload = task;
|
_isLoading = false;
|
||||||
|
_status = '缓存清理完成';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onError(String error) {
|
/// 检查网络状态
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
Future<void> _checkNetwork() async {
|
||||||
SnackBar(
|
setState(() {
|
||||||
content: Text(error),
|
_isLoading = true;
|
||||||
backgroundColor: Colors.red,
|
_status = '检查网络中...';
|
||||||
),
|
});
|
||||||
);
|
|
||||||
|
final hasNetwork = await AppUpgradeSimple.instance.checkNetworkStatus();
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
_isLoading = false;
|
||||||
|
_status = hasNetwork ? '网络连接正常' : '网络连接异常';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取应用信息
|
||||||
|
Future<void> _getAppInfo() async {
|
||||||
|
setState(() {
|
||||||
|
_isLoading = true;
|
||||||
|
_status = '获取应用信息中...';
|
||||||
|
});
|
||||||
|
|
||||||
|
final appInfo = await AppUpgradeSimple.instance.getAppInfo();
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
_isLoading = false;
|
||||||
|
_status = '应用信息: ${appInfo['appName']} v${appInfo['version']}';
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text('增强版App升级插件'),
|
|
||||||
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
|
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
|
||||||
bottom: TabBar(
|
title: Text(widget.title),
|
||||||
controller: _tabController,
|
elevation: 2,
|
||||||
tabs: const [
|
|
||||||
Tab(text: '基础功能'),
|
|
||||||
Tab(text: '高级配置'),
|
|
||||||
Tab(text: '网络监测'),
|
|
||||||
Tab(text: '版本管理'),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
body: SingleChildScrollView(
|
||||||
body: TabBarView(
|
|
||||||
controller: _tabController,
|
|
||||||
children: [
|
|
||||||
_buildBasicTab(),
|
|
||||||
_buildConfigTab(),
|
|
||||||
_buildNetworkTab(),
|
|
||||||
_buildVersionTab(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 基础功能标签页
|
|
||||||
Widget _buildBasicTab() {
|
|
||||||
return SingleChildScrollView(
|
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
// App信息卡片
|
// 状态显示卡片
|
||||||
if (_appInfo != null)
|
|
||||||
Card(
|
Card(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
const Text(
|
Text(
|
||||||
'App信息',
|
'当前状态',
|
||||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 8),
|
||||||
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(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
const Icon(Icons.system_update, color: Colors.amber),
|
if (_isLoading)
|
||||||
const SizedBox(width: 8),
|
const SizedBox(
|
||||||
const Text(
|
width: 16,
|
||||||
'发现新版本',
|
height: 16,
|
||||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
child: CircularProgressIndicator(strokeWidth: 2),
|
||||||
|
),
|
||||||
|
if (_isLoading) const SizedBox(width: 8),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
_status,
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
|
||||||
Text('版本: ${_upgradeInfo!.versionName}'),
|
|
||||||
Text('强制更新: ${_upgradeInfo!.isForceUpdate ? "是" : "否"}'),
|
|
||||||
if (_upgradeInfo!.apkSize != null) Text('大小: ${_formatBytes(_upgradeInfo!.apkSize!)}'),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
|
||||||
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(
|
Text(
|
||||||
'${_formatBytes(_currentDownload!.downloadedSize)} / ${_formatBytes(_currentDownload!.totalSize!)}'),
|
'基础功能',
|
||||||
if (_currentDownload!.errorMessage != null)
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
Text('错误: ${_currentDownload!.errorMessage}', style: const TextStyle(color: Colors.red)),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(height: 12),
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
|
|
||||||
// 操作按钮
|
|
||||||
Wrap(
|
|
||||||
spacing: 8,
|
|
||||||
runSpacing: 8,
|
|
||||||
children: [
|
|
||||||
ElevatedButton.icon(
|
ElevatedButton.icon(
|
||||||
onPressed: () => _checkUpdate(),
|
onPressed: _isLoading ? null : _checkUpdate,
|
||||||
icon: const Icon(Icons.refresh),
|
icon: const Icon(Icons.system_update),
|
||||||
label: const Text('检查更新'),
|
label: const Text('检查更新'),
|
||||||
),
|
),
|
||||||
if (_currentDownload != null) ...[
|
const SizedBox(height: 8),
|
||||||
if (_currentDownload!.status == DownloadStatus.downloading)
|
|
||||||
ElevatedButton.icon(
|
ElevatedButton.icon(
|
||||||
onPressed: () => _plugin.pauseDownload(),
|
onPressed: _isLoading ? null : _autoUpdate,
|
||||||
icon: const Icon(Icons.pause),
|
icon: const Icon(Icons.auto_awesome),
|
||||||
label: const Text('暂停'),
|
label: const Text('自动更新'),
|
||||||
),
|
),
|
||||||
if (_currentDownload!.status == DownloadStatus.paused)
|
const SizedBox(height: 8),
|
||||||
ElevatedButton.icon(
|
ElevatedButton.icon(
|
||||||
onPressed: () => _plugin.resumeDownload(),
|
onPressed: _isLoading ? null : _silentCheck,
|
||||||
icon: const Icon(Icons.play_arrow),
|
icon: const Icon(Icons.visibility_off),
|
||||||
label: const Text('继续'),
|
label: const Text('静默检查'),
|
||||||
),
|
),
|
||||||
if (_currentDownload!.status == DownloadStatus.failed)
|
const SizedBox(height: 20),
|
||||||
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() {
|
Text(
|
||||||
return SingleChildScrollView(
|
'工具功能',
|
||||||
padding: const EdgeInsets.all(16),
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
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<VersionCompareStrategy>(
|
|
||||||
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),
|
const SizedBox(height: 12),
|
||||||
if (_cacheStats != null) ...[
|
ElevatedButton.icon(
|
||||||
Text(
|
onPressed: _isLoading ? null : _checkNetwork,
|
||||||
'内存缓存: ${_cacheStats!['memoryCache']['sizeFormatted']} (${_cacheStats!['memoryCache']['count']}项)'),
|
icon: const Icon(Icons.wifi),
|
||||||
Text(
|
label: const 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),
|
const SizedBox(height: 8),
|
||||||
OutlinedButton(
|
ElevatedButton.icon(
|
||||||
onPressed: () async {
|
onPressed: _isLoading ? null : _getAppInfo,
|
||||||
await _plugin.clearCache();
|
icon: const Icon(Icons.info),
|
||||||
_cacheStats = await _plugin.getCacheStats();
|
label: const Text('应用信息'),
|
||||||
if (mounted) {
|
),
|
||||||
setState(() {});
|
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(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
const SnackBar(content: Text('缓存已清空')),
|
const SnackBar(content: Text('已启用自动更新模式')),
|
||||||
);
|
);
|
||||||
}
|
|
||||||
},
|
},
|
||||||
child: const Text('清空缓存'),
|
icon: const Icon(Icons.auto_mode),
|
||||||
|
label: const Text('启用自动更新'),
|
||||||
),
|
),
|
||||||
],
|
const SizedBox(height: 8),
|
||||||
),
|
ElevatedButton.icon(
|
||||||
],
|
onPressed: _isLoading
|
||||||
),
|
? null
|
||||||
),
|
: () {
|
||||||
),
|
AppUpgradeSimple.instance.enableSilentCheck();
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 网络监测标签页
|
|
||||||
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<String>(
|
|
||||||
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<String>(
|
|
||||||
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<void> _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(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
const SnackBar(content: Text('已是最新版本')),
|
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('自定义配置'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 工具方法
|
|
||||||
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 '自定义';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -37,10 +37,146 @@ class _SimpleAppUpgradePlugin {
|
||||||
Future<Map<String, String>> getAppInfo() {
|
Future<Map<String, String>> getAppInfo() {
|
||||||
return AppUpgradePluginPlatform.instance.getAppInfo();
|
return AppUpgradePluginPlatform.instance.getAppInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 检查当前应用的版本信息(用于安装状态检测)
|
||||||
|
Future<Map<String, String>> getCurrentAppInfo() async {
|
||||||
|
try {
|
||||||
|
return await AppUpgradePluginPlatform.instance.getAppInfo();
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('获取应用信息失败: $e');
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取下载路径
|
||||||
|
Future<String?> getDownloadPath() {
|
||||||
|
return AppUpgradePluginPlatform.instance.getDownloadPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 精确检测应用是否已安装(通过包名)
|
||||||
|
Future<bool> 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<bool> 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升级管理器
|
/// 简化版App升级管理器
|
||||||
/// 提供最简单的API,一行代码即可实现App升级功能
|
/// 提供最简单的API,一行代码即可实现App升级功能
|
||||||
|
///
|
||||||
|
/// 版本: 2.0.0
|
||||||
|
/// 更新日期: 2025-09-18
|
||||||
|
///
|
||||||
|
/// 主要特性:
|
||||||
|
/// - 🎨 现代化UI设计,支持Material Design 3
|
||||||
|
/// - 🔄 智能安装状态检测,支持多种安装场景
|
||||||
|
/// - 📱 完整的生命周期管理和错误处理
|
||||||
|
/// - ⚙️ 灵活的配置系统,支持多种使用模式
|
||||||
|
/// - 🎯 富文本支持,更好的内容展示
|
||||||
|
/// - 🚀 高性能,低内存占用
|
||||||
|
/// - 🛡️ 完善的权限处理和安全检查
|
||||||
class AppUpgradeSimple {
|
class AppUpgradeSimple {
|
||||||
static AppUpgradeSimple? _instance;
|
static AppUpgradeSimple? _instance;
|
||||||
|
|
||||||
|
|
@ -61,10 +197,60 @@ class AppUpgradeSimple {
|
||||||
AppUpgradeSimple._() : _plugin = _SimpleAppUpgradePlugin.instance;
|
AppUpgradeSimple._() : _plugin = _SimpleAppUpgradePlugin.instance;
|
||||||
|
|
||||||
final _SimpleAppUpgradePlugin _plugin;
|
final _SimpleAppUpgradePlugin _plugin;
|
||||||
|
UpgradeConfig _config = const UpgradeConfig();
|
||||||
|
|
||||||
|
/// 配置升级参数
|
||||||
|
void configure(UpgradeConfig config) {
|
||||||
|
_config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 快速配置:一键设置自动更新
|
||||||
|
void enableAutoUpdate() {
|
||||||
|
_config = UpgradeConfig.auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 快速配置:一键设置静默检查
|
||||||
|
void enableSilentCheck() {
|
||||||
|
_config = UpgradeConfig.silent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 检查网络连接状态
|
||||||
|
Future<bool> checkNetworkStatus() async {
|
||||||
|
try {
|
||||||
|
final result = await InternetAddress.lookup('google.com');
|
||||||
|
return result.isNotEmpty && result[0].rawAddress.isNotEmpty;
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('网络检查失败: $e');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 清理下载的临时文件
|
||||||
|
Future<void> 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
|
/// ```dart
|
||||||
/// AppUpgradeSimple.instance.checkUpdate(
|
/// AppUpgradeSimple.instance.checkUpdate(
|
||||||
/// context: context,
|
/// 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` 的有效上下文。
|
/// [context] 需要是一个能够访问到 `Navigator` 和 `MaterialLocalizations` 的有效上下文。
|
||||||
/// 如果您不确定,建议在 `init` 方法中提供 `navigatorKey`,这样插件可以获取一个可靠的上下文。
|
/// [url] 检查更新的API接口地址
|
||||||
|
/// [params] 可选的请求参数
|
||||||
|
/// [config] 可选的配置覆盖
|
||||||
|
/// [onComplete] 更新流程完成后的回调
|
||||||
|
/// 一键检查更新(支持配置)
|
||||||
Future<void> checkUpdate({
|
Future<void> checkUpdate({
|
||||||
required BuildContext context,
|
required BuildContext context,
|
||||||
required String url,
|
required String url,
|
||||||
Map<String, dynamic>? params,
|
Map<String, dynamic>? params,
|
||||||
bool showNoUpdateToast = true,
|
bool? showNoUpdateToast,
|
||||||
bool autoDownload = false,
|
bool? autoDownload,
|
||||||
bool autoInstall = false,
|
bool? autoInstall,
|
||||||
VoidCallback? onComplete,
|
VoidCallback? onComplete,
|
||||||
|
UpgradeConfig? config,
|
||||||
}) async {
|
}) async {
|
||||||
|
// 使用传入的配置或默认配置
|
||||||
|
final effectiveConfig = config ?? _config;
|
||||||
|
final finalShowNoUpdateToast = showNoUpdateToast ?? effectiveConfig.showNoUpdateToast;
|
||||||
|
final finalAutoDownload = autoDownload ?? effectiveConfig.autoDownload;
|
||||||
|
final finalAutoInstall = autoInstall ?? effectiveConfig.autoInstall;
|
||||||
try {
|
try {
|
||||||
assert(_canShowMaterialDialog(context), '请在 MaterialApp 环境内调用');
|
assert(_canShowMaterialDialog(context), '请在 MaterialApp 环境内调用');
|
||||||
|
|
||||||
// 检查更新
|
// 检查更新
|
||||||
final info = await _plugin.checkUpdate(url, params: params);
|
final info = await _plugin.checkUpdate(url, params: params);
|
||||||
print('info: $info');
|
if (effectiveConfig.enableDebugLog) {
|
||||||
|
debugPrint('🔍 检查更新结果: $info');
|
||||||
|
}
|
||||||
|
|
||||||
if (info == null || !info.hasUpdate) {
|
if (info == null || !info.hasUpdate) {
|
||||||
if (showNoUpdateToast && context.mounted) _showToast('已是最新版本');
|
if (finalShowNoUpdateToast && context.mounted) {
|
||||||
|
_showToast('已是最新版本', effectiveConfig);
|
||||||
|
}
|
||||||
onComplete?.call();
|
onComplete?.call();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -98,9 +323,10 @@ class AppUpgradeSimple {
|
||||||
await _showUpgradeDialog(
|
await _showUpgradeDialog(
|
||||||
context: context,
|
context: context,
|
||||||
info: info,
|
info: info,
|
||||||
autoDownload: autoDownload,
|
autoDownload: finalAutoDownload,
|
||||||
autoInstall: autoInstall,
|
autoInstall: finalAutoInstall,
|
||||||
onComplete: onComplete,
|
onComplete: onComplete,
|
||||||
|
config: effectiveConfig,
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint('检查更新失败: $e');
|
debugPrint('检查更新失败: $e');
|
||||||
|
|
@ -121,11 +347,13 @@ class AppUpgradeSimple {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
_showToast(errorMessage);
|
_showToast(errorMessage, effectiveConfig);
|
||||||
|
|
||||||
// 如果是网络问题,显示网络诊断建议
|
// 如果是网络问题,显示网络诊断建议
|
||||||
if (errorString.contains('Failed host lookup') || errorString.contains('无网络连接')) {
|
if (errorString.contains('Failed host lookup') || errorString.contains('无网络连接')) {
|
||||||
debugPrint('建议: 请检查网络连接或尝试使用网络诊断功能');
|
if (effectiveConfig.enableDebugLog) {
|
||||||
|
debugPrint('💡 建议: 请检查网络连接或尝试使用网络诊断功能');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onComplete?.call();
|
onComplete?.call();
|
||||||
|
|
@ -140,11 +368,60 @@ class AppUpgradeSimple {
|
||||||
try {
|
try {
|
||||||
return await _plugin.checkUpdate(url, params: params);
|
return await _plugin.checkUpdate(url, params: params);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
if (_config.enableDebugLog) {
|
||||||
debugPrint('静默检查更新失败: $e');
|
debugPrint('静默检查更新失败: $e');
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 预下载APK(不显示UI,后台下载)
|
||||||
|
Future<String?> 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<String?> 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<Map<String, String>> getAppInfo() async {
|
||||||
|
return await _plugin.getAppInfo();
|
||||||
|
}
|
||||||
|
|
||||||
/// 显示升级对话框
|
/// 显示升级对话框
|
||||||
Future<void> _showUpgradeDialog({
|
Future<void> _showUpgradeDialog({
|
||||||
required BuildContext context,
|
required BuildContext context,
|
||||||
|
|
@ -152,20 +429,27 @@ class AppUpgradeSimple {
|
||||||
required bool autoDownload,
|
required bool autoDownload,
|
||||||
required bool autoInstall,
|
required bool autoInstall,
|
||||||
VoidCallback? onComplete,
|
VoidCallback? onComplete,
|
||||||
|
UpgradeConfig? config,
|
||||||
}) {
|
}) {
|
||||||
|
final effectiveConfig = config ?? _config;
|
||||||
|
|
||||||
return showDialog(
|
return showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
barrierDismissible: !info.isForceUpdate,
|
barrierDismissible: !info.isForceUpdate,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
if (info.isForceUpdate) {
|
if (info.isForceUpdate) {
|
||||||
return _ForceUpgradeDialog(info: info);
|
return _ForceUpgradeDialog(
|
||||||
|
info: info,
|
||||||
|
config: effectiveConfig,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
return _SimpleUpgradeDialog(
|
return _SimpleUpgradeDialog(
|
||||||
info: info,
|
info: info,
|
||||||
autoDownload: autoDownload,
|
autoDownload: autoDownload,
|
||||||
autoInstall: autoInstall,
|
autoInstall: autoInstall,
|
||||||
onComplete: onComplete,
|
onComplete: onComplete,
|
||||||
showToast: (message) => _showToast(message),
|
config: effectiveConfig,
|
||||||
|
showToast: (message) => _showToast(message, effectiveConfig),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -173,8 +457,20 @@ class AppUpgradeSimple {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 显示Toast提示
|
/// 显示Toast提示
|
||||||
void _showToast(String message) {
|
void _showToast(String message, [UpgradeConfig? config]) {
|
||||||
Fluttertoast.showToast(msg: message);
|
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 环境(仅用于对话框)
|
/// 检查是否存在可用的 Material 环境(仅用于对话框)
|
||||||
|
|
@ -206,6 +502,7 @@ mixin _UpgradeDialogLogic<T extends StatefulWidget> on State<T> {
|
||||||
VoidCallback? get onComplete;
|
VoidCallback? get onComplete;
|
||||||
bool get autoDownload;
|
bool get autoDownload;
|
||||||
bool get autoInstall;
|
bool get autoInstall;
|
||||||
|
UpgradeConfig get config;
|
||||||
|
|
||||||
void initUpgradeLogic() {
|
void initUpgradeLogic() {
|
||||||
if (autoDownload && Platform.isAndroid) {
|
if (autoDownload && Platform.isAndroid) {
|
||||||
|
|
@ -222,13 +519,14 @@ mixin _UpgradeDialogLogic<T extends StatefulWidget> on State<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void onAppLifecycleStateChanged(AppLifecycleState state) {
|
void onAppLifecycleStateChanged(AppLifecycleState state) {
|
||||||
debugPrint('应用生命周期状态变化: $state, _isWaitingForInstallation=$_isWaitingForInstallation');
|
debugPrint('🔄 应用生命周期状态变化: $state, _isWaitingForInstallation=$_isWaitingForInstallation');
|
||||||
|
|
||||||
// 当应用从后台回到前台时,检查是否是从安装界面返回
|
// 核心逻辑:当应用从后台回到前台时,检查安装状态
|
||||||
if (state == AppLifecycleState.resumed && _isWaitingForInstallation) {
|
if (_isWaitingForInstallation && state == AppLifecycleState.resumed) {
|
||||||
debugPrint('应用回到前台,准备检查安装状态');
|
debugPrint('⚡ 应用回到前台,检查安装状态');
|
||||||
// 延迟一点时间再检查,确保系统状态稳定
|
|
||||||
Future.delayed(const Duration(milliseconds: 500), () {
|
// 延迟检测,让系统状态稳定
|
||||||
|
Future.delayed(const Duration(milliseconds: 1500), () {
|
||||||
if (mounted && _isWaitingForInstallation) {
|
if (mounted && _isWaitingForInstallation) {
|
||||||
_checkInstallationResult();
|
_checkInstallationResult();
|
||||||
}
|
}
|
||||||
|
|
@ -321,7 +619,7 @@ mixin _UpgradeDialogLogic<T extends StatefulWidget> on State<T> {
|
||||||
_isWaitingForInstallation = true;
|
_isWaitingForInstallation = true;
|
||||||
_statusText = '请完成安装';
|
_statusText = '请完成安装';
|
||||||
});
|
});
|
||||||
showToast('请在系统弹窗中完成安装,如取消可点击下方按钮重试');
|
showToast('请在系统弹窗中完成安装,完成后点击下方确认按钮');
|
||||||
|
|
||||||
// 启动定时检测(备用方案)
|
// 启动定时检测(备用方案)
|
||||||
_startInstallationTimeoutCheck();
|
_startInstallationTimeoutCheck();
|
||||||
|
|
@ -345,47 +643,73 @@ mixin _UpgradeDialogLogic<T extends StatefulWidget> on State<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 启动安装超时检测
|
/// 启动简化的安装检测(主要依靠应用生命周期)
|
||||||
void _startInstallationTimeoutCheck() {
|
void _startInstallationTimeoutCheck() {
|
||||||
// 5秒后自动检测一次(快速检测)
|
debugPrint('🚀 启动简化安装检测系统');
|
||||||
Timer(const Duration(seconds: 5), () {
|
|
||||||
if (mounted && _isWaitingForInstallation && _statusText == '请完成安装') {
|
|
||||||
debugPrint('5秒后自动检测安装状态');
|
|
||||||
_checkInstallationResult();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 30秒后如果还在等待安装,则提示超时
|
// 核心策略:主要依靠应用生命周期监听,减少定时检测
|
||||||
_installCheckTimer = Timer(const Duration(seconds: 30), () {
|
|
||||||
if (mounted && _isWaitingForInstallation && _statusText == '请完成安装') {
|
// 仅保留一个简单的超时检测
|
||||||
|
_installCheckTimer = Timer(Duration(seconds: config.installTimeout), () {
|
||||||
|
if (mounted && _isWaitingForInstallation) {
|
||||||
|
debugPrint('⏰ 安装检测超时');
|
||||||
setState(() {
|
setState(() {
|
||||||
_isWaitingForInstallation = false;
|
_isWaitingForInstallation = false;
|
||||||
_statusText = '安装超时';
|
_statusText = '安装超时';
|
||||||
});
|
});
|
||||||
showToast('安装检测超时,请检查是否已安装成功');
|
showToast('安装超时,请使用重试按钮重新安装');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 检查安装结果(当应用回到前台时调用)
|
/// 简化的安装结果检测(仅版本检测)
|
||||||
Future<void> _checkInstallationResult() async {
|
Future<void> _checkInstallationResult() async {
|
||||||
if (!mounted || !_isWaitingForInstallation) {
|
if (!mounted || !_isWaitingForInstallation) {
|
||||||
debugPrint('跳过安装结果检查: mounted=$mounted, _isWaitingForInstallation=$_isWaitingForInstallation');
|
debugPrint('跳过安装结果检查: mounted=$mounted, _isWaitingForInstallation=$_isWaitingForInstallation');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
debugPrint('开始检查安装结果...');
|
debugPrint('🔍 开始简化安装检测...');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 获取当前应用信息
|
// 获取当前应用信息
|
||||||
final appInfo = await _plugin.getAppInfo();
|
final appInfo = await _plugin.getCurrentAppInfo();
|
||||||
final currentVersion = appInfo['versionName'] ?? '';
|
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) {
|
bool isUpdated = false;
|
||||||
debugPrint('检测到版本已更新,安装成功');
|
if (info.versionBuildNumber > 0) {
|
||||||
|
isUpdated = currentBuildNumber >= info.versionBuildNumber;
|
||||||
|
debugPrint('📊 构建号比较: $currentBuildNumber >= ${info.versionBuildNumber} = $isUpdated');
|
||||||
|
} else {
|
||||||
|
// 备用方案:比较版本号
|
||||||
|
isUpdated = currentVersion == info.versionName;
|
||||||
|
debugPrint('📊 版本号比较: $currentVersion == ${info.versionName} = $isUpdated');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isUpdated) {
|
||||||
|
debugPrint('✅ 检测结果: 安装成功');
|
||||||
|
_handleInstallationSuccess();
|
||||||
|
} else {
|
||||||
|
debugPrint('❌ 检测结果: 安装被取消(版本未更新)');
|
||||||
|
_handleInstallationCancelled();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('❌ 检测失败: $e');
|
||||||
|
setState(() {
|
||||||
|
_isWaitingForInstallation = false;
|
||||||
|
_statusText = '检测失败';
|
||||||
|
});
|
||||||
|
showToast('无法检测安装状态,请手动确认');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 处理安装成功
|
||||||
|
void _handleInstallationSuccess() {
|
||||||
_installCheckTimer?.cancel();
|
_installCheckTimer?.cancel();
|
||||||
setState(() {
|
setState(() {
|
||||||
_isWaitingForInstallation = false;
|
_isWaitingForInstallation = false;
|
||||||
|
|
@ -400,23 +724,15 @@ mixin _UpgradeDialogLogic<T extends StatefulWidget> on State<T> {
|
||||||
}
|
}
|
||||||
onComplete?.call();
|
onComplete?.call();
|
||||||
});
|
});
|
||||||
} else {
|
}
|
||||||
// 版本号未更新,用户可能取消了安装
|
|
||||||
debugPrint('版本号未更新,用户可能取消了安装');
|
/// 处理安装被取消
|
||||||
|
void _handleInstallationCancelled() {
|
||||||
setState(() {
|
setState(() {
|
||||||
_isWaitingForInstallation = false;
|
_isWaitingForInstallation = false;
|
||||||
_statusText = '安装被取消';
|
_statusText = '安装被取消';
|
||||||
});
|
});
|
||||||
showToast('安装被取消,可以重新尝试安装');
|
showToast('安装被取消,可以点击重试按钮重新安装');
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
debugPrint('检测应用状态失败: $e');
|
|
||||||
setState(() {
|
|
||||||
_isWaitingForInstallation = false;
|
|
||||||
_statusText = '检测失败';
|
|
||||||
});
|
|
||||||
showToast('无法检测安装状态,请手动检查');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 重新安装APK
|
/// 重新安装APK
|
||||||
|
|
@ -842,31 +1158,40 @@ mixin _UpgradeDialogLogic<T extends StatefulWidget> on State<T> {
|
||||||
// 下载状态图标和文本
|
// 下载状态图标和文本
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Container(
|
AnimatedContainer(
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: _getStatusColor(colorScheme).withOpacity(0.1),
|
color: _getStatusColor(colorScheme).withOpacity(0.1),
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
|
child: AnimatedSwitcher(
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
child: Icon(
|
child: Icon(
|
||||||
_getStatusIcon(),
|
_getStatusIcon(),
|
||||||
|
key: ValueKey(_statusText),
|
||||||
color: _getStatusColor(colorScheme),
|
color: _getStatusColor(colorScheme),
|
||||||
size: 20,
|
size: 20,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
AnimatedSwitcher(
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
child: Text(
|
||||||
_statusText,
|
_statusText,
|
||||||
|
key: ValueKey(_statusText),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
color: colorScheme.onSurface,
|
color: colorScheme.onSurface,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
const SizedBox(height: 2),
|
const SizedBox(height: 2),
|
||||||
if (_isDownloading || _downloadProgress < 1.0)
|
if (_isDownloading || _downloadProgress < 1.0)
|
||||||
Text(
|
Text(
|
||||||
|
|
@ -878,83 +1203,58 @@ mixin _UpgradeDialogLogic<T extends StatefulWidget> on State<T> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// 添加点击提示(当有错误状态或等待用户操作时)
|
// 添加点击提示(当有错误状态或等待用户操作时)
|
||||||
if (showRetryButton &&
|
if (_shouldShowRetryOptions())
|
||||||
(_statusText == '安装失败' ||
|
|
||||||
_statusText == '安装异常' ||
|
|
||||||
_statusText == '权限被拒绝' ||
|
|
||||||
_statusText == '请完成安装' ||
|
|
||||||
_statusText == '安装超时' ||
|
|
||||||
_statusText == '安装被取消' ||
|
|
||||||
_statusText == '检测失败'))
|
|
||||||
Text(
|
Text(
|
||||||
_statusText == '请完成安装' ? '可重新启动安装' : '点击区域重试',
|
_getClickHintText(),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
color: colorScheme.onSurface.withOpacity(0.6),
|
color: colorScheme.onSurface.withOpacity(0.6),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// 为"请完成安装"状态添加操作按钮
|
// 为"请完成安装"状态添加简单提示
|
||||||
if (_statusText == '请完成安装')
|
if (_statusText == '请完成安装')
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 8),
|
padding: const EdgeInsets.only(top: 12),
|
||||||
child: Row(
|
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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Icon(
|
||||||
child: ElevatedButton.icon(
|
Icons.touch_app,
|
||||||
onPressed: _checkInstallationResult,
|
color: colorScheme.primary,
|
||||||
icon: const Icon(Icons.refresh, size: 14),
|
size: 24,
|
||||||
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),
|
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
'请在系统安装界面完成操作',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
color: colorScheme.onSurface,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
),
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(height: 4),
|
||||||
Expanded(
|
Text(
|
||||||
child: ElevatedButton.icon(
|
'系统将自动检测安装结果',
|
||||||
onPressed: () async {
|
style: TextStyle(
|
||||||
// 直接重新安装,不检查状态
|
fontSize: 11,
|
||||||
if (_downloadedFilePath != null) {
|
color: colorScheme.onSurface.withOpacity(0.7),
|
||||||
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,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
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),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -1046,6 +1346,12 @@ mixin _UpgradeDialogLogic<T extends StatefulWidget> on State<T> {
|
||||||
return Icons.check_circle;
|
return Icons.check_circle;
|
||||||
} else if (_statusText == '请完成安装') {
|
} else if (_statusText == '请完成安装') {
|
||||||
return Icons.touch_app;
|
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 == '安装被取消') {
|
} else if (_statusText == '安装被取消') {
|
||||||
return Icons.cancel_outlined;
|
return Icons.cancel_outlined;
|
||||||
} else if (_statusText == '安装超时' || _statusText == '检测失败') {
|
} else if (_statusText == '安装超时' || _statusText == '检测失败') {
|
||||||
|
|
@ -1067,6 +1373,12 @@ mixin _UpgradeDialogLogic<T extends StatefulWidget> on State<T> {
|
||||||
return colorScheme.tertiary;
|
return colorScheme.tertiary;
|
||||||
} else if (_statusText == '请完成安装') {
|
} else if (_statusText == '请完成安装') {
|
||||||
return colorScheme.secondary;
|
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 == '安装被取消') {
|
} else if (_statusText == '安装被取消') {
|
||||||
return colorScheme.secondary.withOpacity(0.8);
|
return colorScheme.secondary.withOpacity(0.8);
|
||||||
} else if (_statusText == '安装超时' || _statusText == '检测失败') {
|
} else if (_statusText == '安装超时' || _statusText == '检测失败') {
|
||||||
|
|
@ -1088,7 +1400,10 @@ mixin _UpgradeDialogLogic<T extends StatefulWidget> on State<T> {
|
||||||
_statusText == '安装异常' ||
|
_statusText == '安装异常' ||
|
||||||
_statusText == '安装超时' ||
|
_statusText == '安装超时' ||
|
||||||
_statusText == '安装被取消' ||
|
_statusText == '安装被取消' ||
|
||||||
_statusText == '检测失败') {
|
_statusText == '检测失败' ||
|
||||||
|
_statusText == '等待安装中' ||
|
||||||
|
_statusText == '等待确认中' ||
|
||||||
|
_statusText == '等待超时') {
|
||||||
return Icons.refresh;
|
return Icons.refresh;
|
||||||
} else if (_statusText == '请完成安装') {
|
} else if (_statusText == '请完成安装') {
|
||||||
return Icons.launch;
|
return Icons.launch;
|
||||||
|
|
@ -1105,8 +1420,11 @@ mixin _UpgradeDialogLogic<T extends StatefulWidget> on State<T> {
|
||||||
_statusText == '安装异常' ||
|
_statusText == '安装异常' ||
|
||||||
_statusText == '安装超时' ||
|
_statusText == '安装超时' ||
|
||||||
_statusText == '安装被取消' ||
|
_statusText == '安装被取消' ||
|
||||||
_statusText == '检测失败') {
|
_statusText == '检测失败' ||
|
||||||
|
_statusText == '等待超时') {
|
||||||
return '重试';
|
return '重试';
|
||||||
|
} else if (_statusText == '等待安装中' || _statusText == '等待确认中') {
|
||||||
|
return '重新安装';
|
||||||
} else if (_statusText == '请完成安装') {
|
} else if (_statusText == '请完成安装') {
|
||||||
return '重新安装';
|
return '重新安装';
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1120,7 +1438,7 @@ mixin _UpgradeDialogLogic<T extends StatefulWidget> on State<T> {
|
||||||
return colorScheme.secondary;
|
return colorScheme.secondary;
|
||||||
} else if (_statusText == '安装失败' || _statusText == '安装异常') {
|
} else if (_statusText == '安装失败' || _statusText == '安装异常') {
|
||||||
return colorScheme.error;
|
return colorScheme.error;
|
||||||
} else if (_statusText == '安装超时' || _statusText == '安装被取消' || _statusText == '检测失败') {
|
} else if (_statusText == '安装超时' || _statusText == '安装被取消' || _statusText == '检测失败' || _statusText == '等待安装中') {
|
||||||
return colorScheme.secondary.withOpacity(0.8);
|
return colorScheme.secondary.withOpacity(0.8);
|
||||||
} else if (_statusText == '请完成安装') {
|
} else if (_statusText == '请完成安装') {
|
||||||
return colorScheme.secondary;
|
return colorScheme.secondary;
|
||||||
|
|
@ -1137,6 +1455,9 @@ mixin _UpgradeDialogLogic<T extends StatefulWidget> on State<T> {
|
||||||
_statusText == '权限被拒绝' ||
|
_statusText == '权限被拒绝' ||
|
||||||
_statusText == '安装超时' ||
|
_statusText == '安装超时' ||
|
||||||
_statusText == '检测失败' ||
|
_statusText == '检测失败' ||
|
||||||
|
_statusText == '等待安装中' ||
|
||||||
|
_statusText == '等待确认中' ||
|
||||||
|
_statusText == '等待超时' ||
|
||||||
(_downloadedFilePath != null &&
|
(_downloadedFilePath != null &&
|
||||||
!_isDownloading &&
|
!_isDownloading &&
|
||||||
!_isInstalling &&
|
!_isInstalling &&
|
||||||
|
|
@ -1145,6 +1466,28 @@ mixin _UpgradeDialogLogic<T extends StatefulWidget> on State<T> {
|
||||||
_statusText != '安装成功');
|
_statusText != '安装成功');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 获取点击提示文本
|
||||||
|
String _getClickHintText() {
|
||||||
|
switch (_statusText) {
|
||||||
|
case '请完成安装':
|
||||||
|
return '完成后请点击确认按钮';
|
||||||
|
case '等待安装中':
|
||||||
|
return '点击重新安装';
|
||||||
|
case '等待确认中':
|
||||||
|
return '请点击确认按钮';
|
||||||
|
case '等待超时':
|
||||||
|
return '点击重新安装';
|
||||||
|
case '安装被取消':
|
||||||
|
return '点击重新安装';
|
||||||
|
case '权限被拒绝':
|
||||||
|
return '点击打开设置';
|
||||||
|
case '安装超时':
|
||||||
|
return '点击重新尝试';
|
||||||
|
default:
|
||||||
|
return '点击区域重试';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void _handleAction() {
|
void _handleAction() {
|
||||||
if (Platform.isAndroid) {
|
if (Platform.isAndroid) {
|
||||||
_handleAndroidAction();
|
_handleAndroidAction();
|
||||||
|
|
@ -1341,6 +1684,7 @@ class _SimpleUpgradeDialog extends StatefulWidget {
|
||||||
final bool autoInstall;
|
final bool autoInstall;
|
||||||
final VoidCallback? onComplete;
|
final VoidCallback? onComplete;
|
||||||
final void Function(String) showToast;
|
final void Function(String) showToast;
|
||||||
|
final UpgradeConfig config;
|
||||||
|
|
||||||
const _SimpleUpgradeDialog({
|
const _SimpleUpgradeDialog({
|
||||||
required this.info,
|
required this.info,
|
||||||
|
|
@ -1348,6 +1692,7 @@ class _SimpleUpgradeDialog extends StatefulWidget {
|
||||||
required this.autoInstall,
|
required this.autoInstall,
|
||||||
this.onComplete,
|
this.onComplete,
|
||||||
required this.showToast,
|
required this.showToast,
|
||||||
|
required this.config,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -1365,6 +1710,8 @@ class _SimpleUpgradeDialogState extends State<_SimpleUpgradeDialog> with _Upgrad
|
||||||
bool get autoDownload => widget.autoDownload;
|
bool get autoDownload => widget.autoDownload;
|
||||||
@override
|
@override
|
||||||
bool get autoInstall => widget.autoInstall;
|
bool get autoInstall => widget.autoInstall;
|
||||||
|
@override
|
||||||
|
UpgradeConfig get config => widget.config;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
|
@ -1477,8 +1824,12 @@ class _SimpleUpgradeDialogState extends State<_SimpleUpgradeDialog> with _Upgrad
|
||||||
/// 强制更新对话框
|
/// 强制更新对话框
|
||||||
class _ForceUpgradeDialog extends StatefulWidget {
|
class _ForceUpgradeDialog extends StatefulWidget {
|
||||||
final UpgradeInfo info;
|
final UpgradeInfo info;
|
||||||
|
final UpgradeConfig config;
|
||||||
|
|
||||||
const _ForceUpgradeDialog({required this.info});
|
const _ForceUpgradeDialog({
|
||||||
|
required this.info,
|
||||||
|
required this.config,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<_ForceUpgradeDialog> createState() => _ForceUpgradeDialogState();
|
State<_ForceUpgradeDialog> createState() => _ForceUpgradeDialogState();
|
||||||
|
|
@ -1488,13 +1839,15 @@ class _ForceUpgradeDialogState extends State<_ForceUpgradeDialog> with _UpgradeD
|
||||||
@override
|
@override
|
||||||
UpgradeInfo get info => widget.info;
|
UpgradeInfo get info => widget.info;
|
||||||
@override
|
@override
|
||||||
void Function(String) get showToast => (message) => AppUpgradeSimple.instance._showToast(message);
|
void Function(String) get showToast => (message) => AppUpgradeSimple.instance._showToast(message, widget.config);
|
||||||
@override
|
@override
|
||||||
VoidCallback? get onComplete => null;
|
VoidCallback? get onComplete => null;
|
||||||
@override
|
@override
|
||||||
bool get autoDownload => false;
|
bool get autoDownload => false;
|
||||||
@override
|
@override
|
||||||
bool get autoInstall => true;
|
bool get autoInstall => true;
|
||||||
|
@override
|
||||||
|
UpgradeConfig get config => widget.config;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue