Files
proxypin/lib/ui/app_update/app_update_repository.dart
2025-06-18 01:51:16 +08:00

108 lines
3.6 KiB
Dart

import 'dart:convert';
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:proxypin/l10n/app_localizations.dart';
import 'package:http/http.dart' as http;
import 'package:proxypin/network/util/logger.dart';
import 'package:proxypin/ui/app_update/remote_version_entity.dart';
import 'package:proxypin/ui/component/app_dialog.dart';
import 'package:proxypin/ui/configuration.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'constants.dart';
import 'new_version_dialog.dart';
class AppUpdateRepository {
static final HttpClient httpClient = HttpClient();
static Future<void> checkUpdate(BuildContext context, {bool canIgnore = true, bool showToast = false}) async {
try {
var lastVersion = await getLatestVersion();
if (lastVersion == null) {
logger.w("[AppUpdate] failed to fetch latest version info");
return;
}
if (!context.mounted) return;
var availableUpdates = compareVersions(AppConfiguration.version, lastVersion.version);
if (availableUpdates) {
if (canIgnore) {
var ignoreVersion = await SharedPreferencesAsync().getString(Constants.ignoreReleaseVersionKey);
if (ignoreVersion == lastVersion.version) {
logger.d("ignored release [${lastVersion.version}]");
return;
}
}
logger.d("new version available: $lastVersion");
if (!context.mounted) return;
NewVersionDialog(
AppConfiguration.version,
lastVersion,
canIgnore: true,
).show(context);
return;
}
logger.i("already using latest version[${AppConfiguration.version}], last: [${lastVersion.version}]");
if (showToast) {
AppLocalizations localizations = AppLocalizations.of(context)!;
CustomToast.success(localizations.appUpdateNotAvailableMsg).show(context);
}
} catch (e) {
logger.e("Error checking for updates: $e");
if (showToast) {
CustomToast.error(e.toString()).show(context);
}
}
}
/// Fetches the latest version information from the GitHub releases API.
static Future<RemoteVersionEntity?> getLatestVersion({bool includePreReleases = false}) async {
final response = await http.get(Uri.parse(Constants.githubReleasesApiUrl));
if (response.statusCode != 200 || response.body.isEmpty) {
logger.w("[AppUpdate] failed to fetch latest version info");
return null;
}
var body = jsonDecode(response.body) as List;
final releases = body.map((e) => GithubReleaseParser.parse(e as Map<String, dynamic>));
late RemoteVersionEntity latest;
if (includePreReleases) {
latest = releases.first;
} else {
latest = releases.firstWhere((e) => e.preRelease == false);
}
logger.d("[AppUpdate] latest version: $latest");
return latest;
}
static bool compareVersions(String currentVersion, String latestVersion) {
String normalizeVersion(String version) {
return version.startsWith('v') ? version.substring(1) : version;
}
List<int> parseVersion(String version) {
return normalizeVersion(version).split('.').map(int.parse).toList();
}
List<int> current = parseVersion(currentVersion);
List<int> latest = parseVersion(latestVersion);
for (int i = 0; i < current.length; i++) {
if (i >= latest.length || current[i] > latest[i]) {
return false; // 当前版本高于最新版本
} else if (current[i] < latest[i]) {
return true; // 需要更新
}
}
return latest.length > current.length; // 最新版本有更多的子版本号
}
}