639 lines
18 KiB
Dart
639 lines
18 KiB
Dart
import 'dart:async';
|
||
import 'dart:io';
|
||
|
||
import 'package:dio/dio.dart';
|
||
import 'package:flutter/foundation.dart';
|
||
import 'package:flutter/services.dart'; // Added for MethodChannel
|
||
import 'package:flutter/widgets.dart'; // Added for WidgetsBinding
|
||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||
import 'package:package_info_plus/package_info_plus.dart';
|
||
import 'package:url_launcher/url_launcher.dart';
|
||
|
||
import 'app_upgrade_plugin_platform_interface.dart';
|
||
import 'core/cache_manager.dart';
|
||
import 'core/download_manager.dart';
|
||
import 'core/network_monitor.dart';
|
||
import 'core/notification_helper.dart';
|
||
import 'core/upgrade_config.dart';
|
||
import 'core/version_comparator.dart';
|
||
import 'models/upgrade_info.dart';
|
||
|
||
export 'core/cache_manager.dart';
|
||
export 'core/download_manager.dart';
|
||
export 'core/network_monitor.dart';
|
||
export 'core/upgrade_config.dart';
|
||
export 'core/version_comparator.dart';
|
||
export 'models/upgrade_info.dart';
|
||
export 'widgets/widgets.dart';
|
||
|
||
/// 升级回调
|
||
typedef UpgradeCallback = void Function(UpgradeInfo info);
|
||
typedef DownloadCallback = void Function(DownloadTask task);
|
||
typedef ErrorCallback = void Function(String error);
|
||
|
||
/// 增强版App升级插件
|
||
class AppUpgradePluginEnhanced {
|
||
static AppUpgradePluginEnhanced? _instance;
|
||
|
||
/// 获取单例实例
|
||
static AppUpgradePluginEnhanced get instance {
|
||
_instance ??= AppUpgradePluginEnhanced._();
|
||
return _instance!;
|
||
}
|
||
|
||
AppUpgradePluginEnhanced._() {
|
||
_init();
|
||
}
|
||
// 核心组件
|
||
|
||
final UpgradeConfig _config = UpgradeConfig.instance;
|
||
final DownloadManager _downloadManager = DownloadManager.instance;
|
||
final NetworkMonitor _networkMonitor = NetworkMonitor.instance;
|
||
final CacheManager _cacheManager = CacheManager.instance;
|
||
final NotificationHelper _notificationHelper = NotificationHelper.instance;
|
||
late VersionComparator _versionComparator;
|
||
final Dio _dio = Dio();
|
||
|
||
// 回调
|
||
final List<UpgradeCallback> _upgradeCallbacks = [];
|
||
final List<DownloadCallback> _downloadCallbacks = [];
|
||
final List<ErrorCallback> _errorCallbacks = [];
|
||
|
||
// 状态
|
||
UpgradeInfo? _currentUpgradeInfo;
|
||
String? _currentDownloadTaskId;
|
||
Timer? _autoCheckTimer;
|
||
StreamSubscription<NetworkStatus>? _networkSubscription;
|
||
|
||
// 内存管理
|
||
final List<StreamSubscription> _subscriptions = [];
|
||
final Map<String, WeakReference<Object>> _weakRefs = {};
|
||
|
||
/// 初始化
|
||
void _init() {
|
||
_versionComparator = VersionComparator(
|
||
strategy: _config.debugMode ? VersionCompareStrategy.buildNumber : VersionCompareStrategy.semantic,
|
||
);
|
||
|
||
// 延迟初始化Flutter相关服务,直到绑定完全初始化
|
||
_scheduleFlutterServicesInit();
|
||
|
||
// 监听网络状态 - 这个可以立即初始化,因为不依赖Flutter绑定
|
||
_networkSubscription = _networkMonitor.statusStream.listen(_onNetworkStatusChanged);
|
||
_subscriptions.add(_networkSubscription!);
|
||
|
||
// 启动自动检查
|
||
if (_config.autoCheck) {
|
||
_startAutoCheck();
|
||
}
|
||
}
|
||
|
||
/// 延迟初始化Flutter相关服务
|
||
void _scheduleFlutterServicesInit() {
|
||
// 如果绑定已经初始化,直接执行
|
||
_initFlutterServices();
|
||
}
|
||
|
||
/// 初始化Flutter相关服务
|
||
void _initFlutterServices() {
|
||
try {
|
||
// 初始化通知,并设置点击回调
|
||
_notificationHelper.initialize(_onNotificationClick);
|
||
|
||
// 监听原生渠道的安装请求
|
||
const MethodChannel('app_upgrade_plugin_channel').setMethodCallHandler((call) async {
|
||
if (call.method == 'installApkFromNotification') {
|
||
final filePath = call.arguments as String?;
|
||
if (filePath != null) {
|
||
await installApkSmart(filePath);
|
||
}
|
||
}
|
||
});
|
||
} catch (e) {
|
||
debugPrint('Failed to initialize Flutter services: $e');
|
||
}
|
||
}
|
||
|
||
/// 配置插件
|
||
void configure({
|
||
bool? debugMode,
|
||
int? checkIntervalHours,
|
||
bool? autoCheck,
|
||
bool? wifiOnly,
|
||
int? downloadTimeout,
|
||
int? connectTimeout,
|
||
int? maxRetryCount,
|
||
bool? supportBreakpoint,
|
||
bool? verifyIntegrity,
|
||
VersionCompareStrategy? versionStrategy,
|
||
Map<String, String>? customHeaders,
|
||
}) {
|
||
_config.updateConfig(
|
||
debugMode: debugMode,
|
||
checkIntervalHours: checkIntervalHours,
|
||
autoCheck: autoCheck,
|
||
wifiOnly: wifiOnly,
|
||
downloadTimeout: downloadTimeout,
|
||
connectTimeout: connectTimeout,
|
||
maxRetryCount: maxRetryCount,
|
||
supportBreakpoint: supportBreakpoint,
|
||
verifyIntegrity: verifyIntegrity,
|
||
customHeaders: customHeaders,
|
||
);
|
||
|
||
if (versionStrategy != null) {
|
||
_versionComparator = VersionComparator(strategy: versionStrategy);
|
||
}
|
||
|
||
// 重启自动检查
|
||
if (autoCheck != null) {
|
||
if (autoCheck) {
|
||
_startAutoCheck();
|
||
} else {
|
||
_stopAutoCheck();
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 获取平台版本(无需平台接口)
|
||
Future<String?> getPlatformVersion() async {
|
||
try {
|
||
final info = await PackageInfo.fromPlatform();
|
||
return info.version;
|
||
} catch (e) {
|
||
debugPrint('getPlatformVersion failed: $e');
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// 获取当前App信息
|
||
Future<Map<String, String>> getAppInfo() async {
|
||
return await _cacheManager.get<Map<String, String>>(
|
||
'app_info',
|
||
strategy: CacheStrategy.cacheFirst,
|
||
networkFetcher: _getPackageInfoMap,
|
||
) ??
|
||
{};
|
||
}
|
||
|
||
/// 检查更新(智能版)
|
||
Future<UpgradeInfo?> checkUpdateSmart(
|
||
String url, {
|
||
Map<String, dynamic>? params,
|
||
bool forceRefresh = false,
|
||
Duration? cacheDuration,
|
||
}) async {
|
||
try {
|
||
// 检查网络状态
|
||
if (!_networkMonitor.isConnected) {
|
||
_notifyError('No network connection');
|
||
return null;
|
||
}
|
||
|
||
// 检查是否可以下载
|
||
if (_config.wifiOnly && !_networkMonitor.isWifi) {
|
||
_notifyError('WiFi required for update check');
|
||
return null;
|
||
}
|
||
|
||
// 使用缓存策略
|
||
final strategy = forceRefresh ? CacheStrategy.networkFirst : CacheStrategy.cacheFirst;
|
||
|
||
final upgradeInfo = await _cacheManager.get<UpgradeInfo>(
|
||
'upgrade_info_$url',
|
||
strategy: strategy,
|
||
networkFetcher: () async {
|
||
final info = await _fetchUpgradeInfo(url, params: params);
|
||
if (info != null) {
|
||
// 智能版本比较
|
||
final appInfo = await getAppInfo();
|
||
final currentVersion = appInfo['version'] ?? '0.0.0';
|
||
|
||
if (_versionComparator.isUpdateAvailable(currentVersion, info.versionName)) {
|
||
// 判断更新类型
|
||
final isMajor = _versionComparator.isMajorUpdate(currentVersion, info.versionName);
|
||
|
||
// 根据更新类型调整策略
|
||
if (isMajor && !info.isForceUpdate) {
|
||
// 主要版本更新建议强制
|
||
debugPrint('Major update detected, suggesting force update');
|
||
}
|
||
|
||
return info;
|
||
}
|
||
}
|
||
return null;
|
||
},
|
||
);
|
||
|
||
if (upgradeInfo != null) {
|
||
_currentUpgradeInfo = upgradeInfo;
|
||
_notifyUpgrade(upgradeInfo);
|
||
|
||
// 预下载(如果配置了静默下载)
|
||
if (_config.silentDownload && upgradeInfo.downloadUrl != null && _networkMonitor.isWifi) {
|
||
_startSilentDownload(upgradeInfo.downloadUrl!);
|
||
}
|
||
}
|
||
|
||
return upgradeInfo;
|
||
} catch (e) {
|
||
_notifyError('Check update failed: $e');
|
||
return null;
|
||
}
|
||
}
|
||
|
||
Future<Map<String, String>> _getPackageInfoMap() async {
|
||
final packageInfo = await PackageInfo.fromPlatform();
|
||
return {
|
||
'appName': packageInfo.appName,
|
||
'packageName': packageInfo.packageName,
|
||
'version': packageInfo.version,
|
||
'buildNumber': packageInfo.buildNumber,
|
||
};
|
||
}
|
||
|
||
/// 直接通过网络请求拉取升级信息,避免依赖平台接口实现
|
||
Future<UpgradeInfo?> _fetchUpgradeInfo(
|
||
String url, {
|
||
Map<String, dynamic>? params,
|
||
}) async {
|
||
try {
|
||
final appInfo = await getAppInfo();
|
||
final requestParams = <String, dynamic>{
|
||
'version': appInfo['version'],
|
||
'buildNumber': appInfo['buildNumber'],
|
||
'platform': Platform.isAndroid ? 'android' : 'ios',
|
||
...?params,
|
||
};
|
||
|
||
final response = await _dio.get(url, queryParameters: requestParams);
|
||
if (response.statusCode == 200 && response.data != null) {
|
||
return UpgradeInfo.fromJson(response.data);
|
||
}
|
||
return null;
|
||
} catch (e) {
|
||
debugPrint('fetch upgrade info failed: $e');
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// 智能下载APK
|
||
Future<String?> downloadApkSmart(
|
||
String url, {
|
||
String? versionName, // 新增版本名,用于标准化文件名
|
||
Function(DownloadProgress)? onProgress,
|
||
String? savePath,
|
||
String? md5,
|
||
String? sha256,
|
||
bool resumeIfExists = true,
|
||
}) async {
|
||
if (!Platform.isAndroid) {
|
||
throw UnsupportedError('downloadApk only supports Android platform');
|
||
}
|
||
|
||
try {
|
||
// 检查网络状态
|
||
final networkStatus = _networkMonitor.currentStatus;
|
||
if (networkStatus == null || !networkStatus.isConnected) {
|
||
_notifyError('No network connection for download');
|
||
return null;
|
||
}
|
||
|
||
// 创建下载任务
|
||
_currentDownloadTaskId = await _downloadManager.createTask(
|
||
url: url,
|
||
savePath: savePath,
|
||
md5: md5,
|
||
sha256: sha256,
|
||
versionName: versionName ?? _currentUpgradeInfo?.versionName,
|
||
);
|
||
|
||
// 监听下载进度
|
||
final progressSubscription = _downloadManager.getProgressStream(_currentDownloadTaskId!)?.listen((task) {
|
||
_notifyDownload(task);
|
||
|
||
// 更新通知栏进度
|
||
_updateNotificationForTask(task);
|
||
|
||
if (onProgress != null && task.totalSize != null) {
|
||
onProgress(DownloadProgress(received: task.downloadedSize, total: task.totalSize!));
|
||
}
|
||
});
|
||
|
||
if (progressSubscription != null) {
|
||
_subscriptions.add(progressSubscription);
|
||
}
|
||
|
||
// 开始下载
|
||
final success = await _downloadManager.startDownload(_currentDownloadTaskId!);
|
||
|
||
if (success) {
|
||
final task = _downloadManager.getTask(_currentDownloadTaskId!);
|
||
return task?.savePath;
|
||
}
|
||
|
||
return null;
|
||
} catch (e) {
|
||
_notifyError('Download failed: $e');
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// 安装APK(带权限检查)
|
||
Future<bool> installApkSmart(String filePath) async {
|
||
if (!Platform.isAndroid) {
|
||
return false;
|
||
}
|
||
|
||
try {
|
||
// 检查文件是否存在
|
||
final file = File(filePath);
|
||
if (!await file.exists()) {
|
||
_notifyError('APK file not found');
|
||
return false;
|
||
}
|
||
|
||
// 检查文件完整性(如果有MD5)
|
||
if (_currentUpgradeInfo?.apkMd5 != null) {
|
||
final isValid = await DownloadManager.verifyFileInIsolate({
|
||
'filePath': filePath,
|
||
'md5': _currentUpgradeInfo!.apkMd5,
|
||
});
|
||
|
||
if (!isValid) {
|
||
_notifyError('APK file integrity check failed');
|
||
return false;
|
||
}
|
||
}
|
||
|
||
return await AppUpgradePluginPlatform.instance.installApk(filePath);
|
||
} catch (e) {
|
||
_notifyError('Install failed: $e');
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// 跳转到应用商店(直接使用 url_launcher,避免依赖平台通道)
|
||
Future<bool> goToAppStore(String url) async {
|
||
try {
|
||
final uri = Uri.parse(url);
|
||
if (await canLaunchUrl(uri)) {
|
||
return await launchUrl(uri, mode: LaunchMode.externalApplication);
|
||
}
|
||
return false;
|
||
} catch (e) {
|
||
_notifyError('Failed to open app store: $e');
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// 暂停下载
|
||
void pauseDownload() {
|
||
if (_currentDownloadTaskId != null) {
|
||
_downloadManager.pauseDownload(_currentDownloadTaskId!);
|
||
}
|
||
}
|
||
|
||
/// 恢复下载
|
||
Future<bool> resumeDownload() async {
|
||
if (_currentDownloadTaskId != null) {
|
||
return await _downloadManager.resumeDownload(_currentDownloadTaskId!);
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/// 取消下载
|
||
void cancelDownload() {
|
||
if (_currentDownloadTaskId != null) {
|
||
_downloadManager.cancelDownload(_currentDownloadTaskId!);
|
||
_notificationHelper.cancelNotification(); // 取消时移除通知
|
||
_currentDownloadTaskId = null;
|
||
}
|
||
}
|
||
|
||
/// 重试下载
|
||
Future<bool> retryDownload() async {
|
||
if (_currentDownloadTaskId != null) {
|
||
return await _downloadManager.retryDownload(_currentDownloadTaskId!);
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/// 获取下载任务
|
||
DownloadTask? getCurrentDownloadTask() {
|
||
if (_currentDownloadTaskId != null) {
|
||
return _downloadManager.getTask(_currentDownloadTaskId!);
|
||
}
|
||
return null;
|
||
}
|
||
|
||
/// 添加升级回调
|
||
void addUpgradeCallback(UpgradeCallback callback) {
|
||
_upgradeCallbacks.add(callback);
|
||
}
|
||
|
||
/// 移除升级回调
|
||
void removeUpgradeCallback(UpgradeCallback callback) {
|
||
_upgradeCallbacks.remove(callback);
|
||
}
|
||
|
||
/// 添加下载回调
|
||
void addDownloadCallback(DownloadCallback callback) {
|
||
_downloadCallbacks.add(callback);
|
||
}
|
||
|
||
/// 移除下载回调
|
||
void removeDownloadCallback(DownloadCallback callback) {
|
||
_downloadCallbacks.remove(callback);
|
||
}
|
||
|
||
/// 添加错误回调
|
||
void addErrorCallback(ErrorCallback callback) {
|
||
_errorCallbacks.add(callback);
|
||
}
|
||
|
||
/// 移除错误回调
|
||
void removeErrorCallback(ErrorCallback callback) {
|
||
_errorCallbacks.remove(callback);
|
||
}
|
||
|
||
/// 开始自动检查
|
||
void _startAutoCheck() {
|
||
_stopAutoCheck();
|
||
|
||
_autoCheckTimer = Timer.periodic(Duration(hours: _config.checkIntervalHours), (_) async {
|
||
// 自动检查需要在WiFi环境下
|
||
if (_config.wifiOnly && !_networkMonitor.isWifi) {
|
||
return;
|
||
}
|
||
|
||
// 执行检查(使用缓存的URL)
|
||
final lastCheckUrl = await _cacheManager.get<String>('last_check_url');
|
||
if (lastCheckUrl != null) {
|
||
await checkUpdateSmart(lastCheckUrl);
|
||
}
|
||
});
|
||
}
|
||
|
||
/// 停止自动检查
|
||
void _stopAutoCheck() {
|
||
_autoCheckTimer?.cancel();
|
||
_autoCheckTimer = null;
|
||
}
|
||
|
||
/// 静默下载
|
||
void _startSilentDownload(String url) async {
|
||
if (!_config.silentDownload) return;
|
||
if (!_networkMonitor.isWifi) return;
|
||
|
||
try {
|
||
final taskId = await _downloadManager.createTask(url: url);
|
||
await _downloadManager.startDownload(taskId);
|
||
} catch (e) {
|
||
debugPrint('Silent download failed: $e');
|
||
}
|
||
}
|
||
|
||
/// 网络状态变化处理
|
||
void _onNetworkStatusChanged(NetworkStatus status) {
|
||
if (_config.debugMode) {
|
||
debugPrint('Network status changed: ${status.toJson()}');
|
||
}
|
||
|
||
// 网络恢复时恢复下载
|
||
if (status.isConnected && _currentDownloadTaskId != null) {
|
||
final task = _downloadManager.getTask(_currentDownloadTaskId!);
|
||
if (task != null && task.status == DownloadStatus.paused) {
|
||
resumeDownload();
|
||
}
|
||
}
|
||
|
||
// WiFi环境下启动静默下载
|
||
if (status.type == NetworkType.wifi && _config.silentDownload && _currentUpgradeInfo?.downloadUrl != null) {
|
||
_startSilentDownload(_currentUpgradeInfo!.downloadUrl!);
|
||
}
|
||
}
|
||
|
||
/// 通知升级信息
|
||
void _notifyUpgrade(UpgradeInfo info) {
|
||
for (final callback in _upgradeCallbacks) {
|
||
callback(info);
|
||
}
|
||
}
|
||
|
||
/// 通知下载进度
|
||
void _notifyDownload(DownloadTask task) {
|
||
for (final callback in _downloadCallbacks) {
|
||
callback(task);
|
||
}
|
||
}
|
||
|
||
/// 通知错误
|
||
void _notifyError(String error) {
|
||
if (_config.debugMode) {
|
||
debugPrint('AppUpgrade Error: $error');
|
||
}
|
||
|
||
for (final callback in _errorCallbacks) {
|
||
callback(error);
|
||
}
|
||
}
|
||
|
||
/// 通知点击回调
|
||
Future<void> _onNotificationClick(NotificationResponse response) async {
|
||
if (response.payload != null && response.payload!.startsWith('download_complete:')) {
|
||
final filePath = response.payload!.split(':').last;
|
||
await installApkSmart(filePath);
|
||
}
|
||
}
|
||
|
||
/// 根据下载任务状态更新通知
|
||
void _updateNotificationForTask(DownloadTask task) async {
|
||
final progress = (task.progress * 100).toInt();
|
||
final appInfo = await _cacheManager.get<Map<String, String>>('app_info', strategy: CacheStrategy.cacheFirst);
|
||
final appName = appInfo?['appName'] ?? '应用';
|
||
|
||
switch (task.status) {
|
||
case DownloadStatus.downloading:
|
||
_notificationHelper.showDownloadProgressNotification(
|
||
progress: progress,
|
||
title: '$appName 下载中...',
|
||
body: '${(task.progress * 100).toStringAsFixed(1)}%',
|
||
);
|
||
break;
|
||
case DownloadStatus.completed:
|
||
_notificationHelper.showDownloadCompleteNotification(
|
||
title: '$appName 下载完成',
|
||
body: '点击安装新版本',
|
||
filePath: task.savePath,
|
||
);
|
||
break;
|
||
case DownloadStatus.failed:
|
||
_notificationHelper.showDownloadFailedNotification(
|
||
title: '$appName 下载失败',
|
||
body: task.errorMessage ?? '请检查网络后重试',
|
||
);
|
||
break;
|
||
case DownloadStatus.cancelled:
|
||
_notificationHelper.cancelNotification();
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
/// 获取缓存统计
|
||
Future<Map<String, dynamic>> getCacheStats() {
|
||
return _cacheManager.getCacheStats();
|
||
}
|
||
|
||
/// 清空缓存
|
||
Future<void> clearCache() {
|
||
return _cacheManager.clear();
|
||
}
|
||
|
||
/// 获取网络状态
|
||
NetworkStatus? get networkStatus => _networkMonitor.currentStatus;
|
||
|
||
/// 刷新网络状态
|
||
Future<void> refreshNetworkStatus() async {
|
||
await _networkMonitor.refreshNetworkStatus();
|
||
}
|
||
|
||
/// 获取当前配置
|
||
UpgradeConfig get config => _config;
|
||
|
||
/// 获取版本比较器
|
||
VersionComparator get versionComparator => _versionComparator;
|
||
|
||
/// 释放资源(优化内存)
|
||
void dispose() {
|
||
// 停止自动检查
|
||
_stopAutoCheck();
|
||
|
||
// 取消所有订阅
|
||
for (final subscription in _subscriptions) {
|
||
subscription.cancel();
|
||
}
|
||
_subscriptions.clear();
|
||
|
||
// 清理回调
|
||
_upgradeCallbacks.clear();
|
||
_downloadCallbacks.clear();
|
||
_errorCallbacks.clear();
|
||
|
||
// 释放组件资源
|
||
_downloadManager.dispose();
|
||
_networkMonitor.dispose();
|
||
_cacheManager.dispose();
|
||
|
||
// 清理弱引用
|
||
_weakRefs.clear();
|
||
|
||
// 清空当前状态
|
||
_currentUpgradeInfo = null;
|
||
_currentDownloadTaskId = null;
|
||
}
|
||
}
|