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.
213 lines
6.4 KiB
213 lines
6.4 KiB
import 'package:flutter/material.dart';
|
|
import 'package:get/get.dart';
|
|
import 'dart:async';
|
|
import 'dart:convert';
|
|
import 'package:get_storage/get_storage.dart';
|
|
import '../../../data/services/network/api.dart';
|
|
import '../ota.dart';
|
|
import 'dart:io';
|
|
import 'package:dio/dio.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:package_info_plus/package_info_plus.dart';
|
|
import 'package:path_provider/path_provider.dart';
|
|
import 'package:json_annotation/json_annotation.dart';
|
|
|
|
class OtaUpgradeController extends GetxController {
|
|
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;
|
|
|
|
@override
|
|
void onInit() {
|
|
super.onInit();
|
|
_getCurrentVersion();
|
|
}
|
|
|
|
// 获取当前版本信息
|
|
Future<void> _getCurrentVersion() async {
|
|
//发送ble获取固件版本
|
|
var firmwareVersion = "1.1.0";
|
|
currentVersion.value = firmwareVersion;
|
|
//发送ble获取产品id
|
|
var id = "a2";
|
|
//发送api请求
|
|
final response = await Api.getequipmentinfo({
|
|
"id": id,
|
|
});
|
|
if (response['info'] != '') {
|
|
print('getLoginToken:进来了');
|
|
// 根据API设计调整
|
|
final resp = OtaConfigResp.fromJson(response);
|
|
|
|
print('成功解析: ${resp.id}, ${resp.factory}');
|
|
latestVersion.value = resp.systemversion!;
|
|
updatepackageaddress = resp.updatepackageaddress!;
|
|
if (_isNewVersionAvailable(resp.systemversion!)) {
|
|
//需要升级
|
|
print('需要更新');
|
|
isUpdateAvailable.value = false;
|
|
} else {
|
|
isUpdateAvailable.value = true;
|
|
//已经是最新版本更新ui,按钮隐藏
|
|
}
|
|
} else {
|
|
throw const FormatException('API 响应缺少 "info" 字段');
|
|
}
|
|
}
|
|
|
|
// 版本比较方法优化
|
|
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('更新'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
// 开始下载
|
|
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('无法访问下载目录');
|
|
}
|
|
|
|
// 创建子目录:Download/APP
|
|
final targetDir = Directory('${downloadsDir.path}/$directoryName');
|
|
if (!await targetDir.exists()) {
|
|
await targetDir.create(recursive: true);
|
|
}
|
|
|
|
final savePath = '${targetDir.path}/$fileName';
|
|
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}');
|
|
} on FileSystemException catch (e) {
|
|
Get.snackbar('存储失败', '无法写入文件: ${e.message}');
|
|
} catch (e) {
|
|
Get.snackbar('未知错误', e.toString());
|
|
}
|
|
}
|
|
|
|
// 开始升级
|
|
void _installApp(String path) async {
|
|
// final File file = File(path);
|
|
// if (await file.exists()) {
|
|
// // OpenResult result = await OpenFile.open(path);
|
|
// // if (result.type != ResultType.done) {
|
|
// // Get.snackbar('错误', '安装失败: ${result.message}');
|
|
// // }
|
|
// }
|
|
}
|
|
}
|
|
|