import 'dart:io'; import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:path_provider/path_provider.dart'; import 'package:open_file/open_file.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:permission_handler/permission_handler.dart'; class UpgraderService extends GetxService { final Dio _dio = Dio(); final String _versionUrl = 'http://xhota.anxincloud.cn/problem/version.json'; // 您的版本信息 JSON 地址 var isUpdateAvailable = false.obs; var updateInfo = {}.obs; var downloadProgress = '0'.obs; var totalSize = '0'.obs; var isDownloading = false.obs; @override void onInit() { super.onInit(); checkForUpdate(); } // 检查更新 Future checkForUpdate() async { try { // 获取当前应用版本信息 PackageInfo packageInfo = await PackageInfo.fromPlatform(); String currentVersion = packageInfo.version; final response = await _dio.get(_versionUrl); if (response.statusCode == 200) { final latestVersion = response.data['version']; if (latestVersion.compareTo(currentVersion) > 0) { isUpdateAvailable.value = true; updateInfo.value = response.data; showUpdateDialog(); } } } catch (e) { Get.log('检查更新失败: $e'); } } // 显示更新提示对话框 void showUpdateDialog() { Get.dialog( AlertDialog( title: Text('发现新版本'), content: Text(updateInfo['description'] ?? '有新的更新可用。'), actions: [ TextButton(onPressed: () => Get.back(), child: Text('稍后')), TextButton( onPressed: () { Get.back(); startDownload(); }, child: Text('立即更新'), ), ], ), barrierDismissible: false, ); } // 开始下载 Future startDownload() async { isDownloading.value = true; showDownloadProgressDialog(); try { Directory? dir = await getExternalStorageDirectory(); if (dir == null) { Get.back(); // 关闭进度对话框 Get.snackbar('错误', '无法获取外部存储目录。'); print('错误: getExternalStorageDirectory() 返回 null'); isDownloading.value = false; return; // 提前退出 } String savePath = '${dir.path}/app-release.apk'; print('--- 调试信息 ---'); print('目标下载路径: $savePath'); print('--- 调试信息 ---'); // 确保父目录存在 final file = File(savePath); if (!await file.parent.exists()) { await file.parent.create(recursive: true); print('--- 调试信息 ---'); print('已尝试创建父目录: ${file.parent.path}'); print('--- 调试信息 ---'); } await _dio.download( updateInfo['url'], savePath, onReceiveProgress: (received, total) { if (total != -1) { downloadProgress.value = (received / 1024 / 1024).toStringAsFixed( 2, ); totalSize.value = (total / 1024 / 1024).toStringAsFixed(2); // 在进度回调中也打印路径,确保一直有效 // print('下载进度 - 目标路径: $savePath'); } }, options: Options( receiveTimeout: const Duration(seconds: 30), // 增加超时时间 sendTimeout: const Duration(seconds: 30), ), ); isDownloading.value = false; Get.back(); // 关闭下载进度对话框 // 再次验证文件是否存在 if (await file.exists()) { final fileSize = await file.length(); Get.log('文件验证成功!路径: $savePath'); Get.log('文件大小: ${(fileSize / 1024 / 1024).toStringAsFixed(2)} MB'); installApk(savePath); } else { Get.snackbar('下载失败', '文件下载完毕但未找到,请联系管理员。'); Get.log('错误:文件下载完毕但未找到!'); } } on DioException catch (e) { // 使用 DioException 捕获更具体的 Dio 错误 isDownloading.value = false; Get.back(); String errorMessage = '下载失败: 网络或服务器问题。'; if (e.type == DioExceptionType.badResponse) { errorMessage = '下载失败: 服务器响应错误 (${e.response?.statusCode})'; Get.log('Dio响应错误: ${e.response?.data}'); } else if (e.type == DioExceptionType.connectionError) { errorMessage = '下载失败: 连接错误,请检查网络。'; } else if (e.type == DioExceptionType.unknown) { // 可能是文件系统错误、权限错误等 errorMessage = '下载失败: 未知错误。请检查存储权限或设备空间。'; Get.log('Dio未知错误: $e'); } Get.snackbar('下载失败', errorMessage); Get.log('下载失败 (DioException): $e'); } catch (e) { isDownloading.value = false; Get.back(); Get.snackbar('下载失败', '发生未知错误。'); Get.log('下载失败 (通用异常): $e'); } } // 显示下载进度对话框 void showDownloadProgressDialog() { Get.dialog( AlertDialog( title: Text('正在下载更新...'), content: Obx( () => Column( mainAxisSize: MainAxisSize.min, children: [ LinearProgressIndicator( value: totalSize.value == '0' ? null : double.parse(downloadProgress.value) / double.parse(totalSize.value), ), SizedBox(height: 16), Text('${downloadProgress.value} MB / ${totalSize.value} MB'), ], ), ), ), barrierDismissible: false, ); } // 安装 APK Future installApk(String path) async { // 检查安装权限 var status = await Permission.requestInstallPackages.status; if (status.isGranted) { // 权限已授予,直接安装 _openApkFile(path); } else { // 权限未授予,发起请求 // 这会打开系统设置页面,让用户手动授权 status = await Permission.requestInstallPackages.request(); if (status.isGranted) { _openApkFile(path); } else { // 如果用户拒绝了权限,给出提示 Get.snackbar( '权限被拒绝', '需要授权才能安装更新。请在系统设置中为本应用开启“安装未知应用”的权限。', duration: Duration(seconds: 5), ); } } } // 抽离出打开文件的逻辑 Future _openApkFile(String path) async { final result = await OpenFile.open(path); print('OpenFile result: ${result.type}, message: ${result.message}'); if (result.type != ResultType.done) { Get.snackbar('安装失败', '无法启动安装程序: ${result.message}'); } } }