Optimize automatic updates
This commit is contained in:
@@ -9,10 +9,12 @@ import 'package:path_provider/path_provider.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:xml/xml.dart';
|
||||
|
||||
import '../core/constants/storage_keys.dart';
|
||||
import '../core/utils/app_logger.dart';
|
||||
import '../data/models/app_update_model.dart';
|
||||
import 'storage_service.dart';
|
||||
|
||||
class UpdateService {
|
||||
class UpdateService extends ChangeNotifier {
|
||||
UpdateService._();
|
||||
|
||||
static final UpdateService instance = UpdateService._();
|
||||
@@ -23,6 +25,9 @@ class UpdateService {
|
||||
static final Uri releasesPageUrl = Uri.parse(
|
||||
'https://git.saont.net/gongyun/app/releases',
|
||||
);
|
||||
static final Uri downloadsPageUrl = Uri.parse(
|
||||
'https://www.gongyun.org/downloads.html',
|
||||
);
|
||||
|
||||
final Dio _dio = Dio(
|
||||
BaseOptions(
|
||||
@@ -34,17 +39,24 @@ class UpdateService {
|
||||
);
|
||||
|
||||
bool _checking = false;
|
||||
AppUpdateInfo? _availableUpdate;
|
||||
|
||||
AppUpdateInfo? get availableUpdate => _availableUpdate;
|
||||
bool get hasUpdate => _availableUpdate != null;
|
||||
|
||||
Future<AppUpdateInfo?> checkForUpdate() async {
|
||||
if (_checking) return null;
|
||||
if (!_supportsAutoDownload) return null;
|
||||
|
||||
_checking = true;
|
||||
try {
|
||||
final current = await PackageInfo.fromPlatform();
|
||||
final latest = await _fetchLatestRelease(currentVersion: current.version);
|
||||
if (latest == null) return null;
|
||||
if (_compareVersions(latest.version, current.version) <= 0) return null;
|
||||
if (latest == null ||
|
||||
_compareVersions(latest.version, current.version) <= 0) {
|
||||
_setAvailableUpdate(null);
|
||||
return null;
|
||||
}
|
||||
_setAvailableUpdate(latest);
|
||||
return latest;
|
||||
} catch (e, stackTrace) {
|
||||
AppLogger.e('检查更新失败: $e\n$stackTrace');
|
||||
@@ -54,6 +66,34 @@ class UpdateService {
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> shouldShowPrompt(AppUpdateInfo update) async {
|
||||
final storage = StorageService.instance;
|
||||
final skipVersion = await storage.getString(
|
||||
StorageKeys.updatePromptSkipVersion,
|
||||
);
|
||||
final skipUntil = await storage.getInt(StorageKeys.updatePromptSkipUntil);
|
||||
if (skipVersion != update.version || skipUntil == null) return true;
|
||||
|
||||
final now = DateTime.now().millisecondsSinceEpoch;
|
||||
if (skipUntil > now) return false;
|
||||
|
||||
await storage.remove(StorageKeys.updatePromptSkipUntil);
|
||||
await storage.remove(StorageKeys.updatePromptSkipVersion);
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<void> skipPromptForFiveDays(AppUpdateInfo update) async {
|
||||
final skipUntil = DateTime.now()
|
||||
.add(const Duration(days: 5))
|
||||
.millisecondsSinceEpoch;
|
||||
final storage = StorageService.instance;
|
||||
await storage.setString(
|
||||
StorageKeys.updatePromptSkipVersion,
|
||||
update.version,
|
||||
);
|
||||
await storage.setInt(StorageKeys.updatePromptSkipUntil, skipUntil);
|
||||
}
|
||||
|
||||
Future<File?> downloadUpdate(
|
||||
AppUpdateInfo update, {
|
||||
void Function(int received, int total)? onProgress,
|
||||
@@ -96,6 +136,15 @@ class UpdateService {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> openDownloadsPage() async {
|
||||
if (!await launchUrl(
|
||||
downloadsPageUrl,
|
||||
mode: LaunchMode.externalApplication,
|
||||
)) {
|
||||
throw Exception('无法打开下载页面: $downloadsPageUrl');
|
||||
}
|
||||
}
|
||||
|
||||
Future<AppUpdateInfo?> _fetchLatestRelease({String? currentVersion}) async {
|
||||
final response = await _dio.getUri<String>(releasesRssUrl);
|
||||
final body = response.data;
|
||||
@@ -119,7 +168,6 @@ class UpdateService {
|
||||
final update = await _buildUpdateInfo(release);
|
||||
if (update == null) continue;
|
||||
|
||||
if (update.downloadUrl == null) continue;
|
||||
return update;
|
||||
}
|
||||
|
||||
@@ -206,8 +254,6 @@ class UpdateService {
|
||||
return null;
|
||||
}
|
||||
|
||||
bool get _supportsAutoDownload => Platform.isAndroid || Platform.isWindows;
|
||||
|
||||
bool _isReleaseAttachment(Uri uri) {
|
||||
return uri.path.contains('/releases/download/');
|
||||
}
|
||||
@@ -345,6 +391,16 @@ class UpdateService {
|
||||
return Uri.decodeComponent(segment);
|
||||
}
|
||||
|
||||
void _setAvailableUpdate(AppUpdateInfo? update) {
|
||||
final previous = _availableUpdate;
|
||||
if (previous?.version == update?.version &&
|
||||
previous?.pageUrl == update?.pageUrl) {
|
||||
return;
|
||||
}
|
||||
_availableUpdate = update;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@visibleForTesting
|
||||
Future<AppUpdateInfo?> debugFetchLatestRelease() => _fetchLatestRelease();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user