ar_tourism_flutter_unity/lib/services/update_service.dart
2025-05-14 17:04:13 +08:00

288 lines
8.8 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:dio/dio.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:open_file/open_file.dart';
import 'package:path_provider/path_provider.dart';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:device_info_plus/device_info_plus.dart';
import '../apis/app.dart';
// 版本信息模型
class VersionInfo {
final String version;
final String downloadUrl;
final String description;
final bool forceUpdate;
final int versionCode;
final String? platform;
final int? fileSize;
final String? md5;
final String? minVersion;
final String? createdAt;
final String? updatedAt;
final int? status;
VersionInfo({
required this.version,
required this.downloadUrl,
required this.description,
required this.forceUpdate,
required this.versionCode,
this.platform,
this.fileSize,
this.md5,
this.minVersion,
this.createdAt,
this.updatedAt,
this.status,
});
factory VersionInfo.fromJson(Map<String, dynamic> json) {
return VersionInfo(
version: json['version'] ?? '',
downloadUrl: json['downloadUrl'] ?? '',
description: json['description'] ?? '',
forceUpdate: json['forceUpdate'] == 1,
versionCode: json['versionCode'] ?? 0,
platform: json['platform'],
fileSize: json['fileSize'],
md5: json['md5'],
minVersion: json['minVersion'],
createdAt: json['createdAt'],
updatedAt: json['updatedAt'],
status: json['status'],
);
}
}
class UpdateService {
final Dio _dio = Dio();
final DeviceInfoPlugin _deviceInfo = DeviceInfoPlugin();
// 检查版本更新
Future<VersionInfo?> checkUpdate() async {
try {
// 获取当前应用版本信息
PackageInfo packageInfo = await PackageInfo.fromPlatform();
print('当前应用版本信息: ${packageInfo.version}');
String currentVersion = packageInfo.version;
String buildNumber = packageInfo.buildNumber;
// 调用后端API检查版本
final response = await userApi.getVersionUpdate(currentVersion);
if (response['code'] == 200) {
final data = response['data'];
// 转换接口数据为 VersionInfo 对象
final versionInfo = VersionInfo(
version: data['version'] ?? '',
downloadUrl: data['downloadUrl'] ?? '',
description: data['description'] ?? '',
forceUpdate: data['forceUpdate'] == 1, // 转换 0/1 为 boolean
versionCode: data['versionCode'] ?? 0,
fileSize: data['fileSize'],
platform: data['platform'],
md5: data['md5'],
minVersion: data['minVersion'],
createdAt: data['createdAt'],
updatedAt: data['updatedAt'],
status: data['status'],
);
// 比较版本号,判断是否需要更新
if (isNeedUpdate(currentVersion, versionInfo.version)) {
return versionInfo;
}
}
return null;
} catch (e) {
print('检查更新失败: $e');
return null;
}
}
Future<PackageInfo> getLatestPackageInfo() async {
// 使用系统API尝试刷新包信息
try {
// 使用延迟和多次读取来确保获取最新信息
await Future.delayed(const Duration(milliseconds: 300));
final info1 = await PackageInfo.fromPlatform();
await Future.delayed(const Duration(milliseconds: 200));
final info2 = await PackageInfo.fromPlatform();
// 比较两次读取的结果,如果不同则取最新的一个(版本号更大的)
if (info1.version != info2.version) {
if (isNeedUpdate(info1.version, info2.version)) {
return info2;
} else {
return info1;
}
}
return info2; // 返回最后读取的结果
} catch (e) {
print('获取最新包信息失败: $e');
return PackageInfo.fromPlatform();
}
}
// 检查和请求安装权限
Future<bool> _checkInstallPermission() async {
// 仅在Android平台需要检查
if (!Platform.isAndroid) return true;
// 获取Android版本
final androidInfo = await _deviceInfo.androidInfo;
// Android 8.0 (API 26) 及以上版本需要请求权限
if (androidInfo.version.sdkInt >= 26) {
if (await Permission.requestInstallPackages.status != PermissionStatus.granted) {
// 请求权限
final status = await Permission.requestInstallPackages.request();
return status == PermissionStatus.granted;
}
return true;
}
// 8.0以下版本不需要动态请求该权限
return true;
}
// 下载并安装更新
Future<(bool, String)> downloadAndInstall(String url, Function(double) onProgress) async {
try {
// 先检查安装权限
final hasPermission = await _checkInstallPermission();
if (!hasPermission) {
debugPrint('没有安装未知来源应用的权限');
return (false, '请授予安装权限后重试');
}
// 验证URL
String downloadUrl = url;
if (downloadUrl.isEmpty) {
debugPrint('下载地址为空');
return (false, '下载地址无效');
}
// 处理URL前缀问题确保是完整的URL
if (!downloadUrl.startsWith('http://') && !downloadUrl.startsWith('https://')) {
downloadUrl = 'http://$downloadUrl';
}
debugPrint('开始下载APK: $downloadUrl');
// 使用普通下载方式
return await _fallbackDownload(downloadUrl, onProgress);
} catch (e) {
debugPrint('下载或安装过程中发生异常: $e');
return (false, '更新过程中出错: ${e.toString()}');
}
}
// 普通下载方式
Future<(bool, String)> _fallbackDownload(String url, Function(double) onProgress) async {
try {
// 获取应用目录
final dir = await getApplicationDocumentsDirectory();
final savePath = '${dir.path}/app-update.apk';
// 检查文件是否已存在,如果存在则删除
final file = File(savePath);
if (await file.exists()) {
await file.delete();
debugPrint('删除旧的APK文件');
}
// 下载文件
await _dio.download(
url,
savePath,
onReceiveProgress: (received, total) {
if (total != -1) {
final progress = received / total;
onProgress(progress);
}
},
options: Options(
receiveTimeout: const Duration(minutes: 5),
sendTimeout: const Duration(minutes: 5),
followRedirects: true,
validateStatus: (status) {
return status! < 500;
}
),
);
debugPrint('APK下载完成: $savePath');
// 验证文件是否存在
if (!await File(savePath).exists()) {
debugPrint('下载完成但文件不存在');
return (false, '下载文件不存在');
}
// 检查文件大小
final fileSize = await File(savePath).length();
if (fileSize < 1000) { // 如果文件小于1KB很可能是错误的
debugPrint('下载的文件过小,可能是错误内容: $fileSize 字节');
return (false, '下载的文件无效');
}
// 安装APK仅Android
if (Platform.isAndroid) {
try {
debugPrint('开始安装APK');
final result = await OpenFile.open(savePath);
debugPrint('安装结果: ${result.type}, ${result.message}');
if (result.type != ResultType.done) {
return (false, '安装失败: ${result.message}');
}
return (true, '安装包已打开');
} catch (e) {
debugPrint('打开安装包时出错: $e');
return (false, '打开安装包失败: ${e.toString()}');
}
} else {
// iOS跳转App Store
return (false, '请在App Store中更新应用');
}
} catch (e) {
debugPrint('下载失败: $e');
return (false, '下载失败: ${e.toString()}');
}
}
// 比较版本号
bool isNeedUpdate(String currentVersion, String serverVersion) {
try {
List<int> current = currentVersion.split('.')
.map((e) => int.parse(e.trim()))
.toList();
List<int> server = serverVersion.split('.')
.map((e) => int.parse(e.trim()))
.toList();
// 确保两个列表长度相同
while (current.length < server.length) {
current.add(0);
}
while (server.length < current.length) {
server.add(0);
}
for (int i = 0; i < current.length; i++) {
if (server[i] > current[i]) return true;
if (server[i] < current[i]) return false;
}
return false;
} catch (e) {
print('版本号比较错误: $e');
return false;
}
}
}