Files
astral/lib/utils/up.dart
T
会做饭的二哈 567b3518e8 ​切换官方服务器
更换为官方服务器,提升稳定性。

​移除模拟延迟
改为直接检测连接,优化体验。

​修复延迟计算
从 peer 获取最小延迟(μs→ms),无效时用路由延迟。

​修复管理员权限
解决管理员操作无权限问题。

​配置存储位置调整
将配置信息改为运行目录,方便管理。

​查看 NAT 类型
添加功能检测并显示 NAT 类型。

​修复页面状态丢失
解决缩放导致导航栏移动时页面状态丢失问题。

​优化延迟检测
始终检测延迟,移除暂停/开始功能,简化逻辑。

​最小化通知
应用最小化时提供通知,提醒用户状态。

​增加客户端搜索功能
添加客户端搜索功能,支持快速查找内容或设备。
2025-03-16 21:15:38 +08:00

292 lines
8.9 KiB
Dart
Raw 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 'dart:convert';
import 'dart:math';
import 'package:astral/utils/app_info.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:package_info_plus/package_info_plus.dart';
import 'package:url_launcher/url_launcher.dart';
class UpdateChecker {
/// GitHub 仓库所有者
final String owner;
/// GitHub 仓库名称
final String repo;
/// 可选:指定检查的分支名称,默认为 'main'
final String branch;
UpdateChecker({
required this.owner,
required this.repo,
this.branch = 'main',
});
/// 检查更新
Future<void> scheckForUpdates(BuildContext context) async {
try {
final releaseInfo = await _fetchLatestRelease();
if (releaseInfo == null) {
_showUpdateDialog(
// 添加空值处理
context,
'检查更新失败',
'无法获取最新版本信息',
'https://github.com/$owner/$repo/releases',
);
return;
}
// 获取当前应用版本
final currentVersion = await _getCurrentVersion();
// 比较版本号,如果有新版本则显示更新弹窗
if (_shouldUpdate(currentVersion, releaseInfo['tag_name'])) {
_showUpdateDialog(
context,
releaseInfo['tag_name'],
releaseInfo['body'] ?? '新版本已发布',
releaseInfo['html_url'],
);
}
} catch (e) {
_showUpdateDialog(
context,
'更新检查失败',
'检查更新时发生错误: $e',
'https://github.com/$owner/$repo/releases',
);
}
}
Future<void> checkForUpdates(BuildContext context) async {
try {
final releaseInfo = await _fetchLatestRelease();
if (releaseInfo == null) {
_showUpdateDialog(
// 添加空值处理
context,
'检查更新失败',
'无法获取最新版本信息',
'https://github.com/$owner/$repo/releases',
);
return;
}
// 获取当前应用版本
final currentVersion = await _getCurrentVersion();
// 比较版本号,如果有新版本则显示更新弹窗
if (_shouldUpdate(currentVersion, releaseInfo['tag_name'])) {
_showUpdateDialog(
context,
releaseInfo['tag_name'],
releaseInfo['body'] ?? '新版本已发布',
releaseInfo['html_url'],
);
} else {
_showUpdateDialog(
context,
'当前已是最新版本',
'当前版本为: $currentVersion',
'https://github.com/$owner/$repo/releases',
);
}
} catch (e) {
_showUpdateDialog(
context,
'更新检查失败',
'检查更新时发生错误: $e',
'https://github.com/$owner/$repo/releases',
);
}
}
/// 获取最新发布版本信息
Future<Map<String, dynamic>?> _fetchLatestRelease() async {
try {
// 添加异常捕获
final response = await http.get(
Uri.parse('https://api.github.com/repos/$owner/$repo/releases'),
headers: {
'Accept': 'application/vnd.github.v3+json',
'User-Agent': 'astral',
},
);
if (response.statusCode == 200) {
final List<dynamic> releases = json.decode(response.body);
if (releases.isEmpty) return null;
// 获取第一个发布版本(最新版本)
return releases[0];
} else {
debugPrint('获取最新版本失败: ${response.statusCode}');
return {
// 返回错误信息
'tag_name': '错误 ${response.statusCode}',
'body': '请求GitHub API失败',
'html_url': 'https://github.com/$owner/$repo/releases'
};
}
} catch (e) {
debugPrint('网络请求异常: $e');
return null;
}
}
/// 获取当前应用版本
Future<String> _getCurrentVersion() async {
try {
return AppInfoUtil.getVersion();
} catch (e) {
debugPrint('获取版本信息失败: $e');
return "0.0.0"; // 返回默认版本号避免后续比较崩溃
}
}
/// 比较版本号,判断是否需要更新
bool _shouldUpdate(String currentVersion, String latestVersion) {
// 确保版本号格式正确(添加v前缀如果没有)
final current = currentVersion.startsWith('v')
? currentVersion.substring(1)
: currentVersion;
final latest = latestVersion.startsWith('v')
? latestVersion.substring(1)
: latestVersion;
// 处理预发布版本标签(如 -alpha, -beta 等)
String currentClean = current;
String latestClean = latest;
if (current.contains('-')) {
currentClean = current.split('-')[0];
}
if (latest.contains('-')) {
latestClean = latest.split('-')[0];
}
// 分割版本号为数组
final currentParts = currentClean.split('.');
final latestParts = latestClean.split('.');
// 比较主版本号、次版本号和修订号
for (int i = 0; i < 3; i++) {
final currentPart =
i < currentParts.length ? int.parse(currentParts[i]) : 0;
final latestPart = i < latestParts.length ? int.parse(latestParts[i]) : 0;
if (latestPart > currentPart) {
return true;
} else if (latestPart < currentPart) {
return false;
}
}
// 版本号相同,检查预发布标签
if (current.contains('-') && !latest.contains('-')) {
// 当前是预发布版本,而最新是正式版本
return true;
} else if (!current.contains('-') && latest.contains('-')) {
// 当前是正式版本,而最新是预发布版本
return false;
} else if (current.contains('-') && latest.contains('-')) {
// 两者都是预发布版本,比较预发布标签
final currentPreRelease = current.split('-')[1];
final latestPreRelease = latest.split('-')[1];
// 简单比较预发布标签(alpha < beta < rc
if (currentPreRelease.startsWith('alpha') &&
(latestPreRelease.startsWith('beta') ||
latestPreRelease.startsWith('rc'))) {
return true;
} else if (currentPreRelease.startsWith('beta') &&
latestPreRelease.startsWith('rc')) {
return true;
} else if (currentPreRelease == latestPreRelease) {
return false;
}
// 如果预发布标签包含数字(如 beta.1, beta.2),则比较数字部分
if (currentPreRelease.contains('.') &&
latestPreRelease.contains('.') &&
currentPreRelease.split('.')[0] == latestPreRelease.split('.')[0]) {
try {
final currentNum = int.parse(currentPreRelease.split('.')[1]);
final latestNum = int.parse(latestPreRelease.split('.')[1]);
return latestNum > currentNum;
} catch (e) {
// 解析失败,返回简单比较结果
return latestPreRelease.compareTo(currentPreRelease) > 0;
}
}
// 默认比较预发布标签的字符串
return latestPreRelease.compareTo(currentPreRelease) > 0;
}
return false; // 版本相同,不需要更新
}
/// 显示更新弹窗
void _showUpdateDialog(
BuildContext context,
String version,
String releaseNotes,
String downloadUrl,
) {
final isLatestVersion = version.contains("当前已是最新版本");
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(isLatestVersion ? version : '发现新版本: $version'),
content: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
if (!isLatestVersion) Text('更新内容:'),
if (!isLatestVersion) const SizedBox(height: 8),
Text(
releaseNotes,
style: const TextStyle(fontSize: 14),
),
],
),
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('稍后再说'),
),
if (!isLatestVersion) // 仅在新版本弹窗显示更新按钮
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
_launchUrl(downloadUrl);
},
child: const Text('立即更新'),
),
if (isLatestVersion) // 最新版本显示确认按钮
ElevatedButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('确定'),
),
],
),
);
}
/// 打开浏览器跳转到下载链接
Future<void> _launchUrl(String url) async {
final uri = Uri.parse(url);
if (await canLaunchUrl(uri)) {
await launchUrl(uri, mode: LaunchMode.externalApplication);
} else {
debugPrint('无法打开链接: $url');
}
}
}