You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
307 lines
8.9 KiB
307 lines
8.9 KiB
import 'package:flutter/material.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:path/path.dart';
|
|
import 'dart:async';
|
|
import '../../../data/services/network/api.dart';
|
|
import '../model/ota_upgrade_model.dart';
|
|
import 'dart:io';
|
|
import 'package:dio/dio.dart';
|
|
import 'package:path_provider/path_provider.dart';
|
|
import 'package:jl_ota/jl_ota.dart';
|
|
import '../../../data/services/ble_manager.dart';
|
|
|
|
class OtaUpgradeController extends GetxController {
|
|
final RxString errorMessage = ''.obs;
|
|
final RxBool isFetchingDeviceInfo = false.obs;
|
|
final Dio _dio = Dio();
|
|
final RxString latestVersion = ''.obs;
|
|
final RxString currentVersion = ''.obs;
|
|
String updatepackageaddress = '';
|
|
final RxDouble downloadProgress = 0.0.obs;
|
|
final RxBool isUpdating = false.obs;
|
|
final isUpdateAvailable = true.obs;
|
|
|
|
final RxDouble progress = 0.0.obs;
|
|
|
|
// 蓝牙服务
|
|
final _bleManager = Get.find<BleManager>();
|
|
@override
|
|
void onInit() {
|
|
super.onInit();
|
|
_getCurrentVersion();
|
|
}
|
|
|
|
// 添加重试方法
|
|
void retryConnection() {
|
|
errorMessage.value = '';
|
|
_getCurrentVersion();
|
|
}
|
|
|
|
// 添加更新方法
|
|
Future<void> startUpdate() async {
|
|
try {
|
|
isUpdating.value = true;
|
|
checkUpdate();
|
|
|
|
// 实现更新逻辑...
|
|
} catch (e) {
|
|
errorMessage.value = e.toString();
|
|
Get.snackbar('更新失败', e.toString());
|
|
} finally {
|
|
isUpdating.value = false;
|
|
}
|
|
}
|
|
|
|
Future<void> _getCurrentVersion() async {
|
|
JlOta().getPlatformVersion();
|
|
//try {
|
|
|
|
// // 添加获取设备信息的重试机制
|
|
// DeviceInfo? deviceInfo = _bleManager.deviceInfo.value;
|
|
|
|
// // 第一次检查设备信息
|
|
// if (deviceInfo == null) {
|
|
// // 显示加载提示
|
|
// isFetchingDeviceInfo.value = true;
|
|
|
|
// // 尝试重新获取设备信息
|
|
// await _bleManager.fetchDeviceInfo();
|
|
|
|
// // 等待后再次获取设备信息
|
|
// deviceInfo = _bleManager.deviceInfo.value;
|
|
// isFetchingDeviceInfo.value = false;
|
|
|
|
// // 仍然为空则抛出异常
|
|
// if (deviceInfo == null) {
|
|
// throw const FormatException('无法获取设备信息,请检查设备连接');
|
|
// }
|
|
// }
|
|
|
|
// // 检查固件信息有效性
|
|
// if (deviceInfo.firmwareVersion.isEmpty
|
|
// // ||
|
|
// // deviceInfo.firmwareProductId.isEmpty
|
|
// ) {
|
|
// throw const FormatException('设备固件信息不完整');
|
|
// }
|
|
|
|
// // 正常流程
|
|
// currentVersion.value = "0"; //deviceInfo.firmwareVersion;
|
|
|
|
// // API请求
|
|
// final response = await Api.getequipmentinfo({
|
|
// "id": "s3",
|
|
// });
|
|
|
|
// // 处理API响应
|
|
// if (response['info'] == null) {
|
|
// throw const FormatException('API 响应格式错误');
|
|
// }
|
|
|
|
// final resp = OtaConfigResp.fromJson(response);
|
|
// latestVersion.value = resp.systemversion!;
|
|
// updatepackageaddress = resp.updatepackageaddress!;
|
|
|
|
// // 版本检查
|
|
// isUpdateAvailable.value = _isNewVersionAvailable(resp.systemversion!);
|
|
// } catch (e) {
|
|
// // 统一错误处理
|
|
// errorMessage.value = e.toString();
|
|
// Get.snackbar('错误', e.toString(),
|
|
// snackPosition: SnackPosition.BOTTOM,
|
|
// backgroundColor: Colors.red,
|
|
// colorText: Colors.white);
|
|
// // 重要:重新抛出异常以便上层处理(可选)
|
|
// } finally {
|
|
// isFetchingDeviceInfo.value = false;
|
|
// }
|
|
}
|
|
|
|
// 版本比较方法优化
|
|
bool _isNewVersionAvailable(String latest) {
|
|
try {
|
|
List<int> currentParts = currentVersion.value
|
|
.split('.')
|
|
.map((e) => int.parse(e.trim()))
|
|
.toList();
|
|
List<int> latestParts =
|
|
latest.split('.').map((e) => int.parse(e.trim())).toList();
|
|
|
|
for (int i = 0; i < latestParts.length; i++) {
|
|
int current = (i < currentParts.length) ? currentParts[i] : 0;
|
|
if (latestParts[i] > current) return true;
|
|
if (latestParts[i] < current) return false;
|
|
}
|
|
return false;
|
|
} catch (e) {
|
|
print('版本比较错误: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// 版本更新
|
|
Future<void> checkUpdate() async {
|
|
try {
|
|
if (latestVersion.value != "")
|
|
_showUpdateDialog(latestVersion.value);
|
|
else {
|
|
Get.snackbar('错误', '获取最新版本信息失败');
|
|
}
|
|
} catch (e) {
|
|
Get.snackbar('错误', '获取版本信息失败');
|
|
}
|
|
}
|
|
|
|
// 显示更新对话框
|
|
void _showUpdateDialog(String latest) {
|
|
Get.dialog(
|
|
AlertDialog(
|
|
title: Text('发现新版本 $latest'),
|
|
content: Text('是否立即更新?'),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Get.back(),
|
|
child: Text('取消'),
|
|
),
|
|
TextButton(
|
|
onPressed: () {
|
|
Get.back();
|
|
startDownload();
|
|
},
|
|
child: Text('更新'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
// 修改后的startDownload方法
|
|
Future<void> startDownload() async {
|
|
isUpdating.value = true;
|
|
int lastIndex = updatepackageaddress.lastIndexOf('/');
|
|
String fileName = updatepackageaddress.substring(lastIndex + 1);
|
|
String directoryName = "ota_upgrades";
|
|
|
|
final dio = Dio(BaseOptions(
|
|
connectTimeout: const Duration(seconds: 5),
|
|
receiveTimeout: Duration.zero,
|
|
sendTimeout: const Duration(seconds: 5),
|
|
headers: {'Accept-Encoding': 'gzip'},
|
|
));
|
|
|
|
Get.snackbar('升级命令已发送', '开始下载...');
|
|
|
|
try {
|
|
final Directory? downloadsDir = await getDownloadsDirectory();
|
|
if (downloadsDir == null) {
|
|
throw FileSystemException('无法访问下载目录');
|
|
}
|
|
|
|
final targetDir = Directory('${downloadsDir.path}/$directoryName');
|
|
if (!await targetDir.exists()) {
|
|
await targetDir.create(recursive: true);
|
|
}
|
|
|
|
final savePath = '${targetDir.path}/$fileName';
|
|
|
|
// 新增:检查同名文件逻辑
|
|
bool existingSameFile = false;
|
|
try {
|
|
var files = await targetDir.list().toList();
|
|
for (var entity in files) {
|
|
if (await FileSystemEntity.isFile(entity.path)) {
|
|
String name = basename(entity.path); // 使用path包中的basename
|
|
if (name == fileName) {
|
|
existingSameFile = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} catch (e) {
|
|
Get.snackbar('错误', '检查本地文件失败: $e');
|
|
isUpdating.value = false;
|
|
return;
|
|
}
|
|
|
|
if (existingSameFile) {
|
|
Get.snackbar('文件已存在', '跳过下载,直接更新');
|
|
isUpdating.value = false;
|
|
_installApp(savePath);
|
|
return;
|
|
}
|
|
|
|
// 新增:删除其他文件逻辑
|
|
try {
|
|
var files = await targetDir.list().toList();
|
|
for (var entity in files) {
|
|
if (await FileSystemEntity.isFile(entity.path)) {
|
|
await entity.delete();
|
|
}
|
|
}
|
|
} catch (e) {
|
|
Get.snackbar('错误', '删除旧文件失败: $e');
|
|
isUpdating.value = false;
|
|
return;
|
|
}
|
|
|
|
print('文件保存路径: $savePath');
|
|
|
|
final options = Options(
|
|
receiveTimeout: Duration.zero,
|
|
followRedirects: true,
|
|
maxRedirects: 5,
|
|
);
|
|
|
|
DateTime lastUpdate = DateTime.now();
|
|
await dio.download(
|
|
updatepackageaddress,
|
|
savePath,
|
|
options: options,
|
|
onReceiveProgress: (received, total) {
|
|
final now = DateTime.now();
|
|
if (now.difference(lastUpdate).inMilliseconds > 1000) {
|
|
lastUpdate = now;
|
|
String progressMsg = total != -1
|
|
? '${(received / total * 100).toStringAsFixed(1)}%'
|
|
: '已下载:${(received / 1024 / 1024).toStringAsFixed(1)}MB';
|
|
Get.snackbar(
|
|
'升级进度',
|
|
progressMsg,
|
|
duration: const Duration(seconds: 1),
|
|
);
|
|
}
|
|
},
|
|
);
|
|
|
|
File file = File(savePath);
|
|
if (await file.exists()) {
|
|
Get.snackbar('下载完成', '文件已保存至: $savePath');
|
|
} else {
|
|
Get.snackbar('错误', '文件未找到,请检查写入权限');
|
|
}
|
|
isUpdating.value = false;
|
|
_installApp(savePath);
|
|
} on DioException catch (e) {
|
|
Get.snackbar('下载失败', '错误: ${e.message}');
|
|
isUpdating.value = false;
|
|
} on FileSystemException catch (e) {
|
|
Get.snackbar('存储失败', '无法写入文件: ${e.message}');
|
|
isUpdating.value = false;
|
|
} catch (e) {
|
|
Get.snackbar('未知错误', e.toString());
|
|
isUpdating.value = false;
|
|
}
|
|
}
|
|
|
|
// 开始升级
|
|
void _installApp(String path) async {
|
|
// JlOtaPlugin()
|
|
// final File file = File(path);
|
|
// if (await file.exists()) {
|
|
// // OpenResult result = await OpenFile.open(path);
|
|
// // if (result.type != ResultType.done) {
|
|
// // Get.snackbar('错误', '安装失败: ${result.message}');
|
|
// // }
|
|
// }
|
|
}
|
|
}
|
|
|