import 'dart:io'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter/services.dart'; import 'package:logger/logger.dart'; import 'package:open_file/open_file.dart'; import 'package:path_provider/path_provider.dart'; import 'package:url_launcher/url_launcher.dart'; import 'version_check_service.dart'; import 'update_state.dart'; /// 更新 Cubit class UpdateCubit extends Cubit { final VersionCheckService _versionService; final Logger _logger; UpdateCubit(this._versionService) : _logger = Logger(), super(UpdateInitial()); /// 检查版本更新 Future checkUpdate() async { emit(UpdateChecking()); try { // 从 pubspec.yaml 读取版本信息 final String versionString = await rootBundle.loadString('pubspec.yaml'); final versionMatch = RegExp(r'version:\s*(\d+\.\d+\.\d+)\+(\d+)').firstMatch(versionString); final currentVersion = versionMatch?.group(1) ?? '1.0.0'; final currentVersionCode = int.tryParse(versionMatch?.group(2) ?? '1') ?? 1; _logger.i('📱 当前应用版本: $currentVersion ($currentVersionCode)'); // 检查更新 final versionInfo = await _versionService.checkUpdate( currentVersion: currentVersion, currentVersionCode: currentVersionCode, ); if (versionInfo == null) { _logger.i('✅ 已是最新版本'); emit(UpdateUpToDate()); return; } _logger.i('🔄 发现新版本: ${versionInfo.version}'); _logger.i('📦 更新类型: ${versionInfo.updateType}'); _logger.i('⚠️ 强制更新: ${versionInfo.forceUpdate}'); emit(UpdateAvailable(versionInfo)); } catch (e) { _logger.e('❌ 检查更新失败: $e'); emit(UpdateFailure('检查更新失败: $e')); } } /// 应用差量补丁 Future applyPatch(String patchUrl, String version, int targetVersionCode, {String? md5}) async { emit(const UpdateDownloading(0.0, isPatch: true)); try { _logger.i('⬇️ 开始下载并应用补丁'); final success = await _versionService.applyPatch( patchUrl: patchUrl, version: version, targetVersionCode: targetVersionCode, md5: md5, onProgress: (progress) { // 🔥 实时更新下载进度 emit(UpdateDownloading(progress, isPatch: true)); }, ); if (success) { emit(const UpdateInstalling(isPatch: true)); emit(UpdateSuccess()); } else { emit(const UpdateFailure('补丁应用失败')); } } catch (e) { _logger.e('❌ 应用补丁异常: $e'); emit(UpdateFailure('应用补丁失败: $e')); } } /// 自动下载并安装 APK(App 内下载) Future downloadAndInstallApk(String apkUrl) async { emit(const UpdateDownloading(0.0, isPatch: false)); try { _logger.i('⬇️ 开始下载完整 APK'); final apkPath = await _versionService.downloadApk(apkUrl, (progress) { emit(UpdateDownloading(progress, isPatch: false)); }); if (apkPath != null) { _logger.i('✅ APK 下载完成: $apkPath'); // 🔥 关键:先清理版本号记录,并等待用户看到提示 emit(const UpdateClearingVersion()); _logger.i('🗑️ 开始清除补丁版本记录'); await _versionService.clearPatchVersionInfo(); _logger.i('✅ 补丁版本记录已清除'); // 🔥 短暂延迟,让用户看到清理完成的提示 await Future.delayed(const Duration(milliseconds: 800)); // 🔥 直接调起系统安装页面,不再等待用户点击 _logger.i('📦 自动调起系统安装页面'); await triggerInstall(apkPath); } else { emit(const UpdateFailure('APK 下载失败')); } } catch (e) { _logger.e('❌ 下载安装 APK 异常: $e'); emit(UpdateFailure('下载安装 APK 失败: $e')); } } /// 触发系统安装页面 Future triggerInstall(String apkPath) async { emit(const UpdateInstalling(isPatch: false)); try { _logger.i('📦 开始调起系统安装页面'); _logger.i('📁 APK 路径: $apkPath'); // 🔥 验证文件是否存在 final file = File(apkPath); if (!await file.exists()) { _logger.e('❌ APK 文件不存在: $apkPath'); emit(UpdateFailure('APK 文件不存在,请重新下载')); return; } final fileSize = await file.length(); _logger.i('📏 APK 文件大小: ${fileSize / 1024 / 1024} MB'); // 🔥 关键修复:将APK复制到外部存储,避免某些Android版本的安装限制 final externalDir = await getExternalStorageDirectory(); String finalApkPath = apkPath; if (externalDir != null) { final publicApkPath = '${externalDir.path}/app-update-final.apk'; _logger.i('📋 复制APK到外部存储: $publicApkPath'); try { await file.copy(publicApkPath); finalApkPath = publicApkPath; _logger.i('✅ APK复制成功'); } catch (e) { _logger.w('⚠️ 复制APK失败,使用原路径: $e'); } } // 打开 APK 文件,触发系统安装界面 _logger.i('🚀 调用 OpenFile.open...'); _logger.i('🚀 最终APK路径: $finalApkPath'); final result = await OpenFile.open(finalApkPath); _logger.i('📦 安装结果: ${result.message} (type: ${result.type})'); // 如果用户取消安装,显示提示信息并保留文件路径 if (result.type != ResultType.done) { _logger.w('⚠️ 用户取消了安装或安装失败 (type: ${result.type})'); // 🔥 如果是权限问题,提供更友好的提示 String errorMsg = '您取消了安装'; if (result.type == ResultType.noAppToOpen) { errorMsg = '没有找到可以安装 APK 的应用,请检查系统设置'; } else if (result.type == ResultType.permissionDenied) { errorMsg = '没有安装权限,请在系统设置中允许"安装未知应用"'; } else if (result.type == ResultType.error) { errorMsg = '安装出错: ${result.message}'; } emit(UpdateFailure('$errorMsg,APK 文件位于: $finalApkPath')); } else { // 安装成功,关闭对话框 emit(UpdateSuccess()); } } catch (e) { _logger.e('❌ 调起安装页面异常: $e'); emit(UpdateFailure('调起安装页面失败: $e')); } } /// 整包更新:浏览器下载 + 引导弹窗 Future fullApkUpdateWithBrowser(String apkUrl) async { try { _logger.i('🌐 整包更新:打开浏览器下载'); final uri = Uri.parse(apkUrl); if (await canLaunchUrl(uri)) { await launchUrl(uri, mode: LaunchMode.externalApplication); _logger.i('✅ 已打开浏览器下载'); // 🔥 整包更新后,立即清除补丁版本记录(避免死循环) await _versionService.clearPatchVersionInfo(); _logger.i('🗑️ 已清除补丁版本记录'); emit(UpdateSuccess()); } else { _logger.e('❌ 无法打开浏览器'); emit(const UpdateFailure('无法打开浏览器')); } } catch (e) { _logger.e('❌ 打开浏览器异常: $e'); emit(UpdateFailure('打开浏览器失败: $e')); } } /// 取消更新 void cancelUpdate() { _logger.i('❌ 用户取消更新'); emit(UpdateInitial()); } }