hi-client/lib/app/modules/kr_home/controllers/kr_home_controller.dart
2026-01-09 06:52:55 -08:00

2699 lines
102 KiB
Dart
Executable File
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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:async';
import 'dart:io';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'package:get/get.dart';
import 'package:kaer_with_panels/app/common/app_run_data.dart';
import 'package:kaer_with_panels/app/services/kr_subscribe_service.dart';
import '../../../../singbox/model/singbox_proxy_type.dart';
import '../../../../singbox/model/singbox_status.dart';
import '../../../common/app_config.dart';
import '../../../localization/app_translations.dart';
import '../../../localization/kr_language_utils.dart';
import '../../../model/business/kr_group_outbound_list.dart';
import '../../../model/business/kr_outbound_item.dart';
import '../../../utils/kr_event_bus.dart';
import '../../../utils/kr_update_util.dart';
import '../../../widgets/dialogs/kr_dialog.dart';
import 'package:kaer_with_panels/app/widgets/dialogs/hi_dialog.dart';
import 'package:kaer_with_panels/app/widgets/kr_app_text_style.dart';
import 'package:kaer_with_panels/app/routes/app_pages.dart';
import '../models/kr_home_views_status.dart';
import 'package:kaer_with_panels/app/model/response/kr_user_available_subscribe.dart';
import 'package:kaer_with_panels/app/utils/kr_log_util.dart';
import 'package:kaer_with_panels/app/utils/kr_common_util.dart';
import 'package:kaer_with_panels/app/utils/kr_secure_storage.dart';
import 'package:kaer_with_panels/app/services/singbox_imp/kr_sing_box_imp.dart';
import 'package:flutter/foundation.dart';
import 'package:kaer_with_panels/app/utils/kr_init_log_collector.dart'; // 🔧 新增:导入日志收集器
import 'package:kaer_with_panels/app/utils/kr_latency_tester.dart'; // 🔧 新增:导入真实延迟测试工具
import 'package:kaer_with_panels/app/utils/account_guard.dart';
import 'package:kaer_with_panels/app/services/iap/iap_service.dart';
class KRHomeController extends GetxController with WidgetsBindingObserver {
// 🔧 新增:日志收集器实例
final _initLog = KRInitLogCollector();
/// 订阅服务
final KRSubscribeService kr_subscribeService = KRSubscribeService();
/// 当前视图状态,登录状态
final Rx<KRHomeViewsStatus> kr_currentViewStatus =
KRHomeViewsStatus.kr_notLoggedIn.obs;
/// 当前列表视图状态
final kr_currentListStatus = KRHomeViewsListStatus.kr_loading.obs;
/// 底部面板高度常量
static const double kr_baseHeight = 120.0; // 基础高度(连接选项)
static const double kr_subscriptionCardHeight = 200.0; // 订阅卡片高度
static const double kr_connectionInfoHeight = 126.0; // 连接信息卡片高度(修复后)
static const double kr_trialCardHeight = 120.0; // 试用卡片高度
static const double kr_lastDayCardHeight = 120.0; // 最后一天卡片高度
static const double kr_nodeListHeight = 400.0; // 节点列表高度
static const double kr_errorHeight = 100.0; // 错误状态高度
static const double kr_loadingHeight = 100.0; // 加载状态高度
/// 间距常量
static const double kr_marginTop = 12.0; // 顶部间距
static const double kr_marginBottom = 12.0; // 底部间距
static const double kr_marginHorizontal = 16.0; // 水平间距
static const double kr_marginVertical = 12.0; // 垂直间距
/// 底部面板高度
final kr_bottomPanelHeight = 200.0.obs;
/// 连接字符串
final kr_connectText = AppTranslations.kr_home.disconnected.obs;
// 当前节点名称
final kr_currentNodeName = 'auto'.obs;
/// 当前连接速率
final RxString kr_currentSpeed = "--".obs;
// 当前节点延迟
final kr_currentNodeLatency = (-2).obs;
// 是否已连接
final kr_isConnected = false.obs;
// 是否显示延迟
final kr_isLatency = false.obs;
/// 默认
var kr_cutTag = ''.obs;
var kr_cutSeletedTag = ''.obs;
var kr_coutryText = ''.obs;
var kr_selectedCountryTag = 'auto'.obs;
void kr_setSelectedCountryTag(String country) {
kr_selectedCountryTag.value = country;
kr_coutryText.value = country;
KRSecureStorage().kr_saveData(key: 'SELECTED_COUNTRY_TAG', value: country);
}
/// 当前连接信息
final RxString kr_currentIp = AppTranslations.kr_home.disconnected.obs;
final RxString kr_currentProtocol = AppTranslations.kr_home.disconnected.obs;
final RxString kr_connectionTime = '00:00:00'.obs;
// 连接计时器
Timer? _kr_connectionTimer;
// 🔧 新增EventBus 监听器 Worker用于清理
Worker? _eventBusWorker;
int _kr_connectionSeconds = 0;
// 🔧 保守修复:添加状态变化时间追踪,用于检测状态卡住
DateTime? _lastStatusChangeTime;
Timer? _statusWatchdogTimer;
// 当前选中的组
final Rx<KRGroupOutboundList?> kr_currentGroup =
Rx<KRGroupOutboundList?>(null);
// 添加是否用户正在移动地图的标志
final kr_isUserMoving = false.obs;
// 添加最后的地图中心点
final kr_lastMapCenter = 0.obs;
// 为"闪连"Checkbox添加一个响应式变量默认为 false
final isQuickConnectEnabled = false.obs;
// 存储实例
final KRSecureStorage _storage = KRSecureStorage();
// 闪连状态存储键
static const String _quickConnectKey = 'kr_quick_connect_enabled';
// 添加一个方法来切换状态
void toggleQuickConnect(bool? value) async {
// 只有当传入的值不为 null 时才更新状态
if (value != null) {
isQuickConnectEnabled.value = value;
// 保存闪连状态到本地存储
await _saveQuickConnectStatus(value);
KRLogUtil.kr_i('闪连状态已更新: $value', tag: 'QuickConnect');
}
}
// 保存闪连状态到本地存储
Future<void> _saveQuickConnectStatus(bool enabled) async {
try {
await _storage.kr_saveBool(key: _quickConnectKey, value: enabled);
KRLogUtil.kr_i('闪连状态已保存到本地存储: $enabled', tag: 'QuickConnect');
} catch (e) {
KRLogUtil.kr_e('保存闪连状态失败: $e', tag: 'QuickConnect');
}
}
// 从本地存储加载闪连状态
Future<void> _loadQuickConnectStatus() async {
try {
final enabled = await _storage.kr_getBool(key: _quickConnectKey);
if (enabled != null) {
isQuickConnectEnabled.value = enabled;
KRLogUtil.kr_i('从本地存储加载闪连状态: $enabled', tag: 'QuickConnect');
} else {
// 如果没有保存过状态使用默认值false
isQuickConnectEnabled.value = false;
KRLogUtil.kr_i('未找到保存的闪连状态,使用默认值: false', tag: 'QuickConnect');
}
} catch (e) {
KRLogUtil.kr_e('加载闪连状态失败: $e', tag: 'QuickConnect');
// 出错时使用默认值
isQuickConnectEnabled.value = false;
}
}
// 获取当前闪连状态
bool get isQuickConnectActive => isQuickConnectEnabled.value;
/// 检查闪连自动连接启动条件
Future<void> _checkQuickConnectAutoStart() async {
try {
KRLogUtil.kr_i('检查闪连自动连接启动条件', tag: 'QuickConnect');
// 检查闪连是否开启
final enabled = await _storage.kr_getBool(key: _quickConnectKey);
if (enabled == null || enabled == false) {
KRLogUtil.kr_i('闪连功能未开启,跳过自动连接', tag: 'QuickConnect');
return;
}
// 检查用户是否已登录
if (!KRAppRunData.getInstance().kr_isLogin.value) {
KRLogUtil.kr_i('用户未登录,跳过自动连接', tag: 'QuickConnect');
return;
}
// 检查当前是否已连接
if (kr_isConnected.value) {
KRLogUtil.kr_i('当前已连接,同步线路信息', tag: 'QuickConnect');
await _restoreSelectedNode();
kr_updateConnectionInfo();
return;
}
KRLogUtil.kr_i('满足闪连自动连接条件,开始自动连接', tag: 'QuickConnect');
await _autoConnect();
} catch (e) {
KRLogUtil.kr_e('检查闪连自动连接启动条件时发生错误: $e', tag: 'QuickConnect');
}
}
/// 执行自动连接
Future<void> _autoConnect() async {
try {
KRLogUtil.kr_i('开始执行闪连自动连接', tag: 'QuickConnect');
// 防止重复操作
if (KRSingBoxImp.instance.kr_status.value == SingboxStarted()) {
KRLogUtil.kr_w('连接操作正在进行中,跳过自动连接', tag: 'QuickConnect');
return;
}
await _kr_prepareCountrySelectionBeforeStart();
final selectedAfter =
await KRSecureStorage().kr_readData(key: 'SELECTED_NODE_TAG');
KRLogUtil.kr_i(
'准备后 SELECTED_NODE_TAG_autoConnect: ${selectedAfter ?? ''}',
tag: 'HomeController');
KRLogUtil.kr_i(
'准备后 kr_currentNodeName_autoConnect: ${kr_currentNodeName.value}',
tag: 'HomeController');
KRLogUtil.kr_i('准备后 kr_cutTag_autoConnect: ${kr_cutTag.value}',
tag: 'HomeController');
KRLogUtil.kr_i(
'准备后 kr_cutSeletedTag_autoConnect: ${kr_cutSeletedTag.value}',
tag: 'HomeController');
await kr_performNodeSwitch(selectedAfter!);
await KRSingBoxImp.instance.kr_start();
KRLogUtil.kr_i('闪连自动连接执行完成', tag: 'QuickConnect');
} catch (e) {
KRLogUtil.kr_e('闪连自动连接失败: $e', tag: 'QuickConnect');
}
}
/// 手动触发闪连状态变更(公共方法)
Future<void> triggerQuickConnect() async {
try {
// 切换闪连状态
bool newStatus = !isQuickConnectEnabled.value;
await _storage.kr_saveBool(key: _quickConnectKey, value: newStatus);
isQuickConnectEnabled.value = newStatus;
KRLogUtil.kr_i('手动切换闪连状态: ${newStatus ? "开启" : "关闭"}',
tag: 'QuickConnect');
} catch (e) {
KRLogUtil.kr_e('切换闪连状态失败: $e', tag: 'QuickConnect');
}
}
/// 检查是否满足自动连接条件(不执行连接)
bool canAutoConnect() {
return isQuickConnectEnabled.value &&
KRAppRunData.getInstance().kr_isLogin.value &&
!kr_isConnected.value;
}
@override
void onInit() async {
super.onInit();
// 🔧 紧急诊断:直接写文件验证 onInit 是否被调用
try {
final dir = await getApplicationDocumentsDirectory();
final debugFile = File('${dir.path}/HOME_CONTROLLER_DEBUG.txt');
await debugFile.writeAsString(
'=' * 60 +
'\n'
'HomeController.onInit 被调用!\n'
'时间: ${DateTime.now()}\n'
'实例 HashCode: ${hashCode}\n'
'线程: ${Platform.isAndroid ? "Android" : "Unknown"}\n'
'=' *
60 +
'\n',
mode: FileMode.append,
);
} catch (e) {
// 忽略错误,确保不影响主流程
}
// 🔧 新增:记录 HomeController 初始化开始
_initLog.logPhaseStart('HomeController 初始化');
_initLog.log('KRHomeController.onInit 被调用', tag: 'Home');
// 🔧 Android 15 紧急修复:立即设置默认高度,确保底部面板可见
// kr_updateBottomPanelHeight();
/// 底部面板高度处理
// _kr_initBottomPanelHeight();
// 加载闪连状态
_loadQuickConnectStatus();
// 加载已选择的国家
await _loadSelectedCountryTag();
// 绑定订阅状态
_bindSubscribeStatus();
/// 登录处理
_kr_initLoginStatus();
// 绑定连接状态
_bindConnectionStatus();
// 注册应用生命周期监听
WidgetsBinding.instance.addObserver(this);
// 🔧 新增:恢复上次选择的节点显示
_restoreSelectedNode();
// 延迟同步连接状态,确保状态正确
Future.delayed(const Duration(milliseconds: 500), () {
kr_forceSyncConnectionStatus(true);
});
// 🔧 Android 15 新增5秒后再次强制更新高度兜底保护
// 🔧 保守修复: 启动状态监控定时器,每 30 秒检查一次状态
_startStatusWatchdog();
}
/// 🔧 新增:恢复上次选择的节点显示
Future<void> _restoreSelectedNode() async {
try {
// 从 Hive 读取保存的节点
final savedNode =
await KRSecureStorage().kr_readData(key: 'SELECTED_NODE_TAG');
if (savedNode != null && savedNode.isNotEmpty) {
KRLogUtil.kr_i('📖 恢复上次选择的节点显示: $savedNode', tag: 'HomeController');
// 更新 UI 显示
kr_currentNodeName.value = savedNode;
kr_cutTag.value = savedNode;
kr_cutSeletedTag.value = savedNode;
// 如果是国家节点,更新国家文本
if (savedNode != 'auto') {
kr_coutryText.value = savedNode;
}
KRLogUtil.kr_i('✅ 节点显示已恢复: $savedNode', tag: 'HomeController');
} else {
KRLogUtil.kr_i(' 没有保存的节点,使用默认 auto', tag: 'HomeController');
}
} catch (e) {
KRLogUtil.kr_e('❌ 恢复节点显示失败: $e', tag: 'HomeController');
// 失败时保持默认值 'auto'
}
}
Future<void> _loadSelectedCountryTag() async {
try {
final v =
await KRSecureStorage().kr_readData(key: 'SELECTED_COUNTRY_TAG');
if (v != null && v.isNotEmpty) {
kr_selectedCountryTag.value = v;
kr_coutryText.value = v;
} else {
kr_selectedCountryTag.value = 'auto';
kr_coutryText.value = 'auto';
}
} catch (_) {}
}
/// 底部面板高度处理
void _kr_initBottomPanelHeight() {
ever(kr_currentListStatus, (status) {
kr_updateBottomPanelHeight();
KRLogUtil.kr_i(status.toString(), tag: "_kr_initBottomPanelHeight");
});
}
void _kr_initLoginStatus() {
_initLog.log('开始初始化登录状态处理', tag: 'Home');
KRLogUtil.kr_i('初始化登录状态', tag: 'HomeController');
// 🔧 Android 15 紧急修复8秒超时更快响应移动网络慢的情况
Timer(const Duration(seconds: 8), () {
if (kr_currentListStatus.value == KRHomeViewsListStatus.kr_loading) {
_initLog.logWarning('⏱️ 订阅加载超时(8秒保护),强制设置为无数据状态', tag: 'Subscribe');
_initLog.log('当前列表状态仍为: loading触发超时保护', tag: 'Subscribe');
KRLogUtil.kr_w('⏱️ 订阅服务初始化超时(8秒),强制设置为无数据状态', tag: 'HomeController');
// 🔧 Android 15 优化:超时设置为 none 而非 error避免底部面板显示错误界面
kr_currentListStatus.value = KRHomeViewsListStatus.kr_none;
// 强制更新底部面板高度,确保显示正常
kr_updateBottomPanelHeight();
_initLog.log('已强制切换到默认视图, 底部面板高度: ${kr_bottomPanelHeight.value}',
tag: 'Subscribe');
KRLogUtil.kr_i('✅ 已强制切换到默认视图', tag: 'HomeController');
}
});
// 🔧 Android 15 新增3秒警告帮助诊断
Timer(const Duration(seconds: 3), () {
if (kr_currentListStatus.value == KRHomeViewsListStatus.kr_loading) {
_initLog.logWarning('⚠️ 订阅服务已加载3秒网络可能较慢', tag: 'Subscribe');
KRLogUtil.kr_w('⚠️ 订阅服务初始化已耗时3秒可能网络较慢', tag: 'HomeController');
}
});
// 延迟初始化,确保所有异步操作完成
Future.delayed(const Duration(milliseconds: 100), () {
_kr_validateAndSetLoginStatus();
});
// 注册登录状态监听器
ever(KRAppRunData().kr_isLogin, (isLoggedIn) {
KRLogUtil.kr_i('登录状态变化: $isLoggedIn', tag: 'HomeController');
_kr_handleLoginStatusChange(isLoggedIn);
});
// 添加状态同步检查
_kr_addStatusSyncCheck();
if (AppConfig().kr_is_daytime == true) {
Future.delayed(const Duration(seconds: 5), () {
KRUpdateUtil().kr_checkUpdate();
Future.delayed(const Duration(seconds: 5), () {
// 不做语言切换
// KRLanguageSwitchDialog.kr_show();
});
});
}
}
/// 验证并设置登录状态
void _kr_validateAndSetLoginStatus() {
try {
_initLog.log('🔍 开始验证登录状态', tag: 'Home');
// 多重验证登录状态
final hasToken = KRAppRunData().kr_token != null &&
KRAppRunData().kr_token!.isNotEmpty;
final isLoginFlag = KRAppRunData().kr_isLogin.value;
final isValidLogin = hasToken && isLoginFlag;
_initLog.log(
'登录验证结果: hasToken=$hasToken, isLogin=$isLoginFlag, isValid=$isValidLogin',
tag: 'Home');
KRLogUtil.kr_i(
'登录状态验证: hasToken=$hasToken, isLogin=$isLoginFlag, isValid=$isValidLogin',
tag: 'HomeController');
KRLogUtil.kr_i('Token内容: ${KRAppRunData().kr_token?.substring(0, 10)}...',
tag: 'HomeController');
if (isValidLogin) {
kr_currentViewStatus.value = KRHomeViewsStatus.kr_loggedIn;
_initLog.logSuccess('用户已登录,准备加载订阅数据', tag: 'Home');
KRLogUtil.kr_i('设置为已登录状态', tag: 'HomeController');
// 订阅服务已在 splash 页面初始化,此处无需重复初始化
KRLogUtil.kr_i('订阅服务已在启动页初始化,跳过重复初始化', tag: 'HomeController');
} else {
kr_currentViewStatus.value = KRHomeViewsStatus.kr_notLoggedIn;
_initLog.logWarning('用户未登录,跳过订阅加载', tag: 'Home');
KRLogUtil.kr_i('设置为未登录状态', tag: 'HomeController');
}
} catch (e) {
_initLog.logError('登录状态验证失败', tag: 'Home', error: e);
KRLogUtil.kr_e('登录状态验证失败: $e', tag: 'HomeController');
kr_currentViewStatus.value = KRHomeViewsStatus.kr_notLoggedIn;
}
}
/// 确保订阅服务初始化
void _kr_ensureSubscribeServiceInitialized() {
try {
_initLog.logSeparator();
_initLog.log('📦 开始订阅服务初始化', tag: 'Subscribe');
// 🔧 修复1: 添加登录状态检查 - 只有已登录用户才能初始化订阅
final isLoggedIn = KRAppRunData().kr_isLogin.value;
if (!isLoggedIn) {
_initLog.logWarning('未登录用户,跳过订阅服务初始化', tag: 'Subscribe');
KRLogUtil.kr_w('⚠️ 未登录用户,不初始化订阅服务', tag: 'HomeController');
kr_currentListStatus.value = KRHomeViewsListStatus.kr_none;
return;
}
// 检查订阅服务状态
final currentStatus = kr_subscribeService.kr_currentStatus.value;
_initLog.log('当前订阅服务状态: $currentStatus', tag: 'Subscribe');
KRLogUtil.kr_i('订阅服务当前状态: $currentStatus', tag: 'HomeController');
if (currentStatus == KRSubscribeServiceStatus.kr_none ||
currentStatus == KRSubscribeServiceStatus.kr_error) {
_initLog.log('订阅服务需要初始化,开始加载...', tag: 'Subscribe');
KRLogUtil.kr_i('订阅服务未初始化或错误,开始初始化', tag: 'HomeController');
// 设置加载状态
kr_currentListStatus.value = KRHomeViewsListStatus.kr_loading;
_initLog.log('设置列表状态为: loading', tag: 'Subscribe');
final startTime = DateTime.now();
_initLog.log('开始调用 kr_refreshAll() 加载订阅数据', tag: 'Subscribe');
// 🔧 Android 15 增强:添加 10 秒超时保护
kr_subscribeService.kr_refreshAll().timeout(
const Duration(seconds: 10),
onTimeout: () {
final elapsed = DateTime.now().difference(startTime).inMilliseconds;
_initLog.logWarning('订阅服务初始化超时(10秒), 实际耗时: ${elapsed}ms',
tag: 'Subscribe');
KRLogUtil.kr_w('⏱️ 订阅服务初始化超时(10秒)', tag: 'HomeController');
kr_currentListStatus.value = KRHomeViewsListStatus.kr_none;
throw TimeoutException('订阅服务初始化超时');
},
).then((_) async {
final elapsed = DateTime.now().difference(startTime).inMilliseconds;
_initLog.logSuccess('订阅服务初始化完成, 耗时: ${elapsed}ms', tag: 'Subscribe');
_initLog.log('最终列表状态: ${kr_currentListStatus.value}',
tag: 'Subscribe');
// 🔧 新增:记录订阅数据详情
final subscribeList = kr_subscribeService.kr_availableSubscribes;
_initLog.log('订阅列表数量: ${subscribeList.length}', tag: 'Subscribe');
if (subscribeList.isEmpty) {
_initLog.logWarning('⚠️ 订阅列表为空!用户可能没有有效订阅', tag: 'Subscribe');
} else {
for (int i = 0; i < subscribeList.length; i++) {
final sub = subscribeList[i];
_initLog.log(
'订阅[$i]: ID=${sub.id}, 名称=${sub.name}, 试用=${sub.isTryOut}',
tag: 'Subscribe');
_initLog.log(' 过期时间: ${sub.expireTime}, 流量: ${sub.traffic}',
tag: 'Subscribe');
}
}
// 记录当前选中的订阅
final currentSub = kr_subscribeService.kr_currentSubscribe.value;
if (currentSub != null) {
_initLog.log('当前选中订阅: ${currentSub.name} (ID: ${currentSub.id})',
tag: 'Subscribe');
} else {
_initLog.logWarning('⚠️ 没有选中任何订阅', tag: 'Subscribe');
}
// 记录订阅服务的最终状态
final finalServiceStatus = kr_subscribeService.kr_currentStatus.value;
_initLog.log('订阅服务最终状态: $finalServiceStatus', tag: 'Subscribe');
KRLogUtil.kr_i('✅ 订阅服务初始化完成', tag: 'HomeController');
// 🔧 关键修复:订阅服务初始化完成后,关闭日志文件
await _initLog.finalize();
// 成功后状态由服务自己控制,不在这里设置
}).catchError((error) async {
final elapsed = DateTime.now().difference(startTime).inMilliseconds;
_initLog.logError('订阅服务初始化失败, 耗时: ${elapsed}ms',
tag: 'Subscribe', error: error);
_initLog.log('设置列表状态为: none (失败)', tag: 'Subscribe');
KRLogUtil.kr_e('❌ 订阅服务初始化失败: $error', tag: 'HomeController');
// 🔧 关键修复:即使失败也要关闭日志文件
await _initLog.finalize();
// 🔧 Android 15 优化:失败时设置为 none 而非 error允许用户手动重试
kr_currentListStatus.value = KRHomeViewsListStatus.kr_none;
});
} else if (currentStatus == KRSubscribeServiceStatus.kr_loading) {
KRLogUtil.kr_i('订阅服务正在初始化中', tag: 'HomeController');
kr_currentListStatus.value = KRHomeViewsListStatus.kr_loading;
} else if (currentStatus == KRSubscribeServiceStatus.kr_success) {
KRLogUtil.kr_i('订阅服务已成功初始化', tag: 'HomeController');
kr_currentListStatus.value = KRHomeViewsListStatus.kr_none;
}
} catch (e) {
KRLogUtil.kr_e('❌ 确保订阅服务初始化异常: $e', tag: 'HomeController');
// 🔧 Android 15 优化:异常时设置为 none 而非 error
kr_currentListStatus.value = KRHomeViewsListStatus.kr_none;
}
}
/// 处理登录状态变化
void _kr_handleLoginStatusChange(bool isLoggedIn) {
try {
if (isLoggedIn) {
// 再次验证登录状态的有效性
final isValidLogin = KRAppRunData().kr_token != null &&
KRAppRunData().kr_token!.isNotEmpty;
if (isValidLogin) {
kr_currentViewStatus.value = KRHomeViewsStatus.kr_loggedIn;
KRLogUtil.kr_i('登录状态变化:设置为已登录', tag: 'HomeController');
// 订阅服务已在 splash 页面初始化,此处无需重复初始化
KRLogUtil.kr_i('订阅服务已在启动页初始化,跳过重复初始化', tag: 'HomeController');
} else {
KRLogUtil.kr_w('登录状态为true但token为空重置为未登录', tag: 'HomeController');
kr_currentViewStatus.value = KRHomeViewsStatus.kr_notLoggedIn;
}
} else {
kr_currentViewStatus.value = KRHomeViewsStatus.kr_notLoggedIn;
KRLogUtil.kr_i('登录状态变化:设置为未登录', tag: 'HomeController');
// 重置列表状态,防止出现无限高度
kr_currentListStatus.value = KRHomeViewsListStatus.kr_none;
// 退出登录清理订阅服务
kr_subscribeService.kr_logout();
}
} catch (e) {
KRLogUtil.kr_e('处理登录状态变化失败: $e', tag: 'HomeController');
kr_currentViewStatus.value = KRHomeViewsStatus.kr_notLoggedIn;
}
}
/// 添加状态同步检查
void _kr_addStatusSyncCheck() {
// 在下一帧检查状态同步
WidgetsBinding.instance.addPostFrameCallback((_) {
_kr_syncLoginStatus();
});
}
/// 同步登录状态
void _kr_syncLoginStatus() {
try {
final currentLoginStatus = KRAppRunData().kr_isLogin.value;
final currentViewStatus = kr_currentViewStatus.value;
KRLogUtil.kr_i(
'状态同步检查: login=$currentLoginStatus, view=$currentViewStatus',
tag: 'HomeController');
// 检查状态是否一致
if (currentViewStatus == KRHomeViewsStatus.kr_loggedIn &&
!currentLoginStatus) {
KRLogUtil.kr_w('状态不一致:视图显示已登录但实际未登录,修正状态', tag: 'HomeController');
kr_currentViewStatus.value = KRHomeViewsStatus.kr_notLoggedIn;
} else if (currentViewStatus == KRHomeViewsStatus.kr_notLoggedIn &&
currentLoginStatus) {
KRLogUtil.kr_w('状态不一致:视图显示未登录但实际已登录,修正状态', tag: 'HomeController');
_kr_validateAndSetLoginStatus();
}
} catch (e) {
KRLogUtil.kr_e('状态同步检查失败: $e', tag: 'HomeController');
}
}
/// 属性数据
void kr_refreshAll() {
kr_subscribeService.kr_refreshAll();
}
/// 绑定订阅状态
void _bindSubscribeStatus() {
ever(kr_subscribeService.kr_currentStatus, (data) {
KRLogUtil.kr_i('订阅服务状态变化: $data', tag: 'HomeController');
if (KRAppRunData.getInstance().kr_isLogin.value) {
switch (data) {
case KRSubscribeServiceStatus.kr_loading:
KRLogUtil.kr_i('订阅服务加载中', tag: 'HomeController');
kr_currentListStatus.value = KRHomeViewsListStatus.kr_loading;
break;
case KRSubscribeServiceStatus.kr_error:
KRLogUtil.kr_w('订阅服务错误', tag: 'HomeController');
kr_currentListStatus.value = KRHomeViewsListStatus.kr_error;
// 不再自动重试,让用户手动刷新
break;
case KRSubscribeServiceStatus.kr_success:
KRLogUtil.kr_i('订阅服务成功', tag: 'HomeController');
kr_cutTag.value = '';
kr_cutSeletedTag.value = '';
kr_currentNodeName.value = "";
if (kr_currentListStatus.value != KRHomeViewsListStatus.kr_none) {
kr_currentListStatus.value = KRHomeViewsListStatus.kr_none;
} else {
// kr_updateBottomPanelHeight();
}
_checkQuickConnectAutoStart();
_kr_testLatencyWithoutVpn();
break;
case KRSubscribeServiceStatus.kr_none:
KRLogUtil.kr_i('订阅服务未初始化', tag: 'HomeController');
// 订阅服务已在 splash 页面初始化如果状态为none可能是网络问题
if (kr_currentViewStatus.value == KRHomeViewsStatus.kr_loggedIn &&
kr_subscribeService.kr_availableSubscribes.isEmpty) {
KRLogUtil.kr_i('订阅服务状态为none可能是网络问题已在启动页初始化',
tag: 'HomeController');
}
break;
}
} else {
KRLogUtil.kr_i('用户未登录,忽略订阅状态变化', tag: 'HomeController');
}
});
// 🔧 修复:保存 EventBus 监听器 Worker以便在控制器销毁时清理
_eventBusWorker = KREventBus().kr_listenMessages(
[KRMessageType.kr_payment, KRMessageType.kr_subscribe_update],
_kr_handleMessage,
);
if (kDebugMode) {
print('✅ [HomeController] EventBus 监听器已注册');
}
}
/// 处理消息
void _kr_handleMessage(KRMessageData message) {
print('✅ [HomeController] EventBus message.kr_type${message.kr_type}');
switch (message.kr_type) {
case KRMessageType.kr_payment:
kr_refreshAll();
break;
case KRMessageType.kr_subscribe_update:
// 处理订阅更新消息
// 显示提示框
KRDialog.show(
title: AppTranslations.kr_home.subscriptionUpdated,
message: AppTranslations.kr_home.subscriptionUpdatedMessage,
confirmText: AppTranslations.kr_dialog.kr_confirm,
cancelText: AppTranslations.kr_dialog.kr_cancel,
onConfirm: () {
kr_refreshAll();
},
onCancel: () => Get.back(),
);
break;
// TODO: Handle this case.
}
}
/// 绑定连接状态
void _bindConnectionStatus() {
// 添加更详细的状态监听
ever(KRSingBoxImp.instance.kr_status, (status) {
KRLogUtil.kr_i('🔄 连接状态变化: $status', tag: 'HomeController');
KRLogUtil.kr_i('📊 当前状态类型: ${status.runtimeType}', tag: 'HomeController');
// 🔧 保守修复: 记录状态变化时间
_lastStatusChangeTime = DateTime.now();
switch (status) {
case SingboxStopped():
KRLogUtil.kr_i('🔴 状态: 已停止', tag: 'HomeController');
kr_connectText.value = AppTranslations.kr_home.disconnected;
kr_stopConnectionTimer();
kr_resetConnectionInfo();
// 取消连接超时处理
_cancelConnectionTimeout();
kr_currentSpeed.value = "--";
kr_isLatency.value = false;
kr_isConnected.value = false;
kr_currentNodeLatency.value = -2;
// 强制刷新 isConnected 状态
kr_isConnected.refresh();
break;
case SingboxStarting():
KRLogUtil.kr_i('🟡 状态: 正在启动', tag: 'HomeController');
kr_connectText.value = AppTranslations.kr_home.connecting;
kr_currentSpeed.value = AppTranslations.kr_home.connecting;
kr_currentNodeLatency.value = -1;
kr_isConnected.value = false; // 修复启动中应该为false
// 启动连接超时处理
_startConnectionTimeout();
break;
case SingboxStarted():
KRLogUtil.kr_i('🟢 状态: 已启动', tag: 'HomeController');
if (kDebugMode) {}
// 取消连接超时处理
_cancelConnectionTimeout();
kr_connectText.value = AppTranslations.kr_home.connected;
kr_startConnectionTimer();
kr_updateConnectionInfo();
kr_isLatency.value = false;
kr_isConnected.value = true;
// 🔧 关键修复:如果延迟还是-1(连接中状态),立即设置为0(已连接但延迟未知)
if (kr_currentNodeLatency.value == -1) {
if (kDebugMode) {}
kr_currentNodeLatency.value = 0;
kr_currentNodeLatency.refresh(); // 强制刷新延迟值
if (kDebugMode) {}
}
// 🔧 修复:立即尝试更新延迟值
_kr_updateLatencyOnConnected();
// 强制刷新 isConnected 状态
kr_isConnected.refresh();
// 强制更新UI
update();
if (kDebugMode) {}
break;
case SingboxStopping():
KRLogUtil.kr_i('🟠 状态: 正在停止', tag: 'HomeController');
kr_connectText.value = AppTranslations.kr_home.disconnecting;
kr_isConnected.value = false;
kr_currentSpeed.value = "--";
break;
}
// 强制更新UI
update();
});
// 添加活动组监听,确保状态同步
ever(KRSingBoxImp.instance.kr_activeGroups, (value) {
KRLogUtil.kr_i('📡 活动组更新,数量: ${value.length}', tag: 'HomeController');
if (value.isEmpty) {
KRLogUtil.kr_w('⚠️ 活动组为空', tag: 'HomeController');
// 🔧 修复如果已连接但活动组为空设置延迟为0而不是-1
if (kr_isConnected.value && kr_currentNodeLatency.value == -1) {
KRLogUtil.kr_w('⚠️ 已连接但活动组为空设置延迟为0', tag: 'HomeController');
kr_currentNodeLatency.value = 0;
}
return;
}
try {
bool hasSelector = false;
for (var element in value) {
KRLogUtil.kr_i(
'📋 处理组: ${element.tag}, 类型: ${element.type}, 选中: ${element.selected}',
tag: 'HomeController');
if (element.type == ProxyType.selector) {
hasSelector = true;
_kr_handleSelectorProxy(element, value);
} else if (element.type == ProxyType.urltest) {
KRLogUtil.kr_d('URL测试代理选中: ${element.selected}',
tag: 'HomeController');
}
}
// 🔧 修复如果已连接但没有selector组设置延迟为0
if (!hasSelector &&
kr_isConnected.value &&
kr_currentNodeLatency.value == -1) {
KRLogUtil.kr_w('⚠️ 已连接但无selector组设置延迟为0', tag: 'HomeController');
kr_currentNodeLatency.value = 0;
}
// 强制更新UI
update();
} catch (e) {
KRLogUtil.kr_e('处理活动组时发生错误: $e', tag: 'HomeController');
// 🔧 修复发生错误时如果已连接设置延迟为0
if (kr_isConnected.value && kr_currentNodeLatency.value == -1) {
KRLogUtil.kr_w('⚠️ 处理活动组错误设置延迟为0', tag: 'HomeController');
kr_currentNodeLatency.value = 0;
}
}
});
ever(KRSingBoxImp.instance.kr_allGroups, (value) {
List<String> updateTags = []; // 收集需要更新的标记ID
for (var element in value) {
for (var subElement in element.items) {
var node = kr_subscribeService.keyList[subElement.tag];
if (node != null) {
if (subElement.urlTestDelay != 0) {
node.urlTestDelay.value = subElement.urlTestDelay;
updateTags.add(subElement.tag); // 添加需要更新的标记ID
}
}
}
// 批量更新所有变化的标记
}
if (updateTags.isNotEmpty) {
kr_updateMarkers(updateTags);
}
});
}
/// 🔧 重构: 参考 hiddify-app 的 toggleConnection 实现
Future<void> kr_toggleSwitch(bool value) async {
final currentStatus = KRSingBoxImp.instance.kr_status.value;
KRLogUtil.kr_i(
'🔵 toggleSwitch 被调用: value=$value, currentStatus=$currentStatus',
tag: 'HomeController');
if (kDebugMode) {}
// 🔧 保守修复: 检测状态是否卡住超过5秒
if (currentStatus is SingboxStarting || currentStatus is SingboxStopping) {
final now = DateTime.now();
if (_lastStatusChangeTime != null &&
now.difference(_lastStatusChangeTime!) > const Duration(seconds: 5)) {
// 状态卡住,强制重置
KRLogUtil.kr_w('⚠️ 检测到状态卡住超过10秒 (当前: $currentStatus),执行强制重置',
tag: 'HomeController');
await _forceResetState();
// 重置后重新尝试操作
KRLogUtil.kr_i('🔄 状态已重置,继续执行切换操作', tag: 'HomeController');
} else {
KRLogUtil.kr_i('🔄 正在切换中,忽略本次操作 (当前状态: $currentStatus)',
tag: 'HomeController');
if (kDebugMode) {}
return;
}
}
try {
// 🔧 保守修复: 记录状态变化时间
_lastStatusChangeTime = DateTime.now();
if (value) {
// 开启连接
KRLogUtil.kr_i('🔄 开始连接...', tag: 'HomeController');
if (kDebugMode) {}
await _kr_prepareCountrySelectionBeforeStart();
final selectedAfter =
await KRSecureStorage().kr_readData(key: 'SELECTED_NODE_TAG');
// KRLogUtil.kr_i('准备后 SELECTED_NODE_TAG: ${selectedAfter ?? ''}',
// tag: 'HomeController');
// KRLogUtil.kr_i('准备后 kr_currentNodeName: ${kr_currentNodeName.value}',
// tag: 'HomeController');
// KRLogUtil.kr_i('准备后 kr_cutTag: ${kr_cutTag.value}',
// tag: 'HomeController');
// KRLogUtil.kr_i('准备后 kr_cutSeletedTag: ${kr_cutSeletedTag.value}',
// tag: 'HomeController');
await kr_performNodeSwitch(selectedAfter!);
await KRSingBoxImp.instance.kr_start();
KRLogUtil.kr_i('✅ 连接命令已发送', tag: 'HomeController');
if (kDebugMode) {}
// 🔧 修复: 等待状态更新最多3秒
await _waitForStatus(SingboxStatus.started().runtimeType,
maxSeconds: 3);
} else {
// 关闭连接
KRLogUtil.kr_i('🛑 开始断开连接...', tag: 'HomeController');
if (kDebugMode) {}
await KRSingBoxImp.instance.kr_stop().timeout(
const Duration(seconds: 10),
onTimeout: () {
KRLogUtil.kr_e('⚠️ 停止操作超时', tag: 'HomeController');
throw TimeoutException('Stop operation timeout');
},
);
KRLogUtil.kr_i('✅ 断开命令已发送', tag: 'HomeController');
if (kDebugMode) {}
// 🔧 保守修复: 等待状态更新,增加超时处理
final success = await _waitForStatus(
SingboxStatus.stopped().runtimeType,
maxSeconds: 3,
);
if (!success) {
// 停止超时,强制同步状态
KRLogUtil.kr_w('⚠️ VPN 停止超时3秒强制同步状态', tag: 'HomeController');
kr_forceSyncConnectionStatus();
}
}
} catch (e) {
KRLogUtil.kr_e('❌ 切换失败: $e', tag: 'HomeController');
if (kDebugMode) {}
// 发生错误时强制同步状态
kr_forceSyncConnectionStatus();
}
if (kDebugMode) {}
}
/// 🔧 等待状态达到预期值
/// 🔧 保守修复: 返回 bool 表示是否成功达到预期状态
Future<bool> _waitForStatus(Type expectedType, {int maxSeconds = 3}) async {
if (kDebugMode) {}
final startTime = DateTime.now();
while (DateTime.now().difference(startTime).inSeconds < maxSeconds) {
final currentStatus = KRSingBoxImp.instance.kr_status.value;
if (kDebugMode) {}
if (currentStatus.runtimeType == expectedType) {
if (kDebugMode) {}
// 强制同步确保 kr_isConnected 正确
kr_forceSyncConnectionStatus();
// 🔧 更新状态变化时间
_lastStatusChangeTime = DateTime.now();
return true; // 成功
}
await Future.delayed(const Duration(milliseconds: 100));
}
// 🔧 保守修复: 超时后记录详细日志
final finalStatus = KRSingBoxImp.instance.kr_status.value;
KRLogUtil.kr_w(
'⏱️ 等待状态超时: 期望=${expectedType.toString()}, 实际=${finalStatus.runtimeType}, 耗时=${maxSeconds}',
tag: 'HomeController',
);
if (kDebugMode) {}
kr_forceSyncConnectionStatus();
return false; // 超时失败
}
Future<void> _kr_prepareCountrySelectionBeforeStart() async {
try {
// KRLogUtil.kr_i('开始准备国家选择', tag: 'CountrySelect');
final storedCountry = kr_selectedCountryTag.value;
// KRLogUtil.kr_i('使用响应式 SELECTED_COUNTRY_TAG: $storedCountry',
// tag: 'CountrySelect');
if (storedCountry == 'auto') {
KRLogUtil.kr_w('当前为 auto按全局最优节点进行选择', tag: 'CountrySelect');
final best = _kr_selectBestNodeTagGlobal();
if (best != null && best.isNotEmpty) {
// KRLogUtil.kr_i('选中全局最优节点: $best', tag: 'CountrySelect');
await KRSecureStorage()
.kr_saveData(key: 'SELECTED_NODE_TAG', value: best);
kr_currentNodeName.value = best;
kr_cutTag.value = best;
kr_cutSeletedTag.value = best;
kr_updateConnectionInfo();
return;
}
// KRLogUtil.kr_w('未找到可用的全局最优节点', tag: 'CountrySelect');
// KRLogUtil.kr_i('触发直接延迟测试以获取有效延迟', tag: 'CountrySelect');
await _kr_testLatencyWithoutVpn();
final bestAfterTest = _kr_selectBestNodeTagGlobal();
if (bestAfterTest != null && bestAfterTest.isNotEmpty) {
// KRLogUtil.kr_i('延迟测试后选中全局最优节点: $bestAfterTest', tag: 'CountrySelect');
await KRSecureStorage()
.kr_saveData(key: 'SELECTED_NODE_TAG', value: bestAfterTest);
kr_currentNodeName.value = bestAfterTest;
kr_cutTag.value = bestAfterTest;
kr_cutSeletedTag.value = bestAfterTest;
kr_updateConnectionInfo();
return;
}
String? fallback = kr_subscribeService.keyList.values
.where((n) => n.tag != 'auto')
.map((n) => n.tag)
.cast<String?>()
.firstWhere((t) => t != null && t.isNotEmpty, orElse: () => null);
if (fallback != null && fallback.isNotEmpty) {
KRLogUtil.kr_w('使用后备节点: $fallback', tag: 'CountrySelect');
await KRSecureStorage()
.kr_saveData(key: 'SELECTED_NODE_TAG', value: fallback);
final verifyB =
await KRSecureStorage().kr_readData(key: 'SELECTED_NODE_TAG');
KRLogUtil.kr_i('写入后校验 SELECTED_NODE_TAG: $verifyB',
tag: 'CountrySelect');
kr_currentNodeName.value = fallback;
kr_cutTag.value = fallback;
kr_cutSeletedTag.value = fallback;
kr_updateConnectionInfo();
} else {
KRLogUtil.kr_w('没有可用的后备节点', tag: 'CountrySelect');
}
return;
}
final currentSelected =
await KRSecureStorage().kr_readData(key: 'SELECTED_NODE_TAG');
KRLogUtil.kr_i('当前 SELECTED_NODE_TAG: ${currentSelected ?? ''}',
tag: 'CountrySelect');
bool valid = false;
if (currentSelected != null && currentSelected.isNotEmpty) {
final node = kr_subscribeService.keyList[currentSelected];
if (node != null) {
final d = node.urlTestDelay.value;
if (node.country == storedCountry && d > 0 && d < 65535) {
valid = true;
}
KRLogUtil.kr_i(
'校验节点: tag=${node.tag}, country=${node.country}, delay=$d, valid=$valid',
tag: 'CountrySelect');
}
}
if (!valid) {
KRLogUtil.kr_w('当前节点无效或不匹配国家,将按国家选择最优节点: $storedCountry',
tag: 'CountrySelect');
final bestInCountry = _kr_selectBestNodeTagInCountry(storedCountry);
if (bestInCountry != null && bestInCountry.isNotEmpty) {
KRLogUtil.kr_i('选中国家最优节点: $bestInCountry', tag: 'CountrySelect');
await KRSecureStorage()
.kr_saveData(key: 'SELECTED_NODE_TAG', value: bestInCountry);
final verify2 =
await KRSecureStorage().kr_readData(key: 'SELECTED_NODE_TAG');
KRLogUtil.kr_i('写入后校验 SELECTED_NODE_TAG: $verify2',
tag: 'CountrySelect');
kr_currentNodeName.value = bestInCountry;
kr_cutTag.value = bestInCountry;
kr_cutSeletedTag.value = bestInCountry;
kr_updateConnectionInfo();
} else {
KRLogUtil.kr_w('未找到国家范围内的可用节点: $storedCountry', tag: 'CountrySelect');
}
}
KRLogUtil.kr_i('国家选择准备完成', tag: 'CountrySelect');
} catch (_) {}
}
String? _kr_selectBestNodeTagGlobal() {
try {
String? bestTag;
int bestDelay = 65535;
for (final node in kr_subscribeService.keyList.values) {
final tag = node.tag;
final delay = node.urlTestDelay.value;
if (tag == 'auto') continue;
if (delay > 0 && delay < 65535) {
if (delay < bestDelay) {
bestDelay = delay;
bestTag = tag;
}
}
}
return bestTag;
} catch (_) {
return null;
}
}
String? _kr_selectBestNodeTagInCountry(String countryCode) {
try {
String? bestTag;
int bestDelay = 65535;
for (final node in kr_subscribeService.keyList.values) {
final tag = node.tag;
final delay = node.urlTestDelay.value;
if (tag == 'auto') continue;
if (node.country != countryCode) continue;
if (delay > 0 && delay < 65535) {
if (delay < bestDelay) {
bestDelay = delay;
bestTag = tag;
}
}
}
return bestTag;
} catch (_) {
return null;
}
}
/// 处理选择器代理
void _kr_handleSelectorProxy(dynamic element, List<dynamic> allGroups) {
try {
KRLogUtil.kr_d(
'处理选择器代理 - 内核选择: ${element.selected}, 用户选择: ${kr_cutTag.value}, 切换中: $_isSwitchingNode',
tag: 'HomeController');
// 🔧 关键修复:如果正在切换节点中,不要用内核返回值覆盖 UI
// 这是导致"跳两次"问题的根本原因
if (_isSwitchingNode) {
KRLogUtil.kr_d('⏳ 节点切换进行中,跳过内核状态同步', tag: 'HomeController');
// 只更新延迟值,不更新选中状态
_kr_updateNodeLatency(element);
return;
}
// 如果用户手动选择了节点不是auto
if (kr_cutTag.value != "auto") {
// 🔧 修复:用户手动选择时,不要用内核返回的 selected 覆盖用户选择
// 只有当内核返回的节点与用户选择一致时,才更新 kr_cutSeletedTag
if (element.selected == kr_cutTag.value) {
kr_cutSeletedTag.value = element.selected;
}
_kr_handleManualMode(element);
return;
}
// 默认auto模式处理 - 此时可以用内核返回值更新 UI
if (element.selected.isNotEmpty) {
kr_cutSeletedTag.value = element.selected;
}
_kr_handleAutoMode(element, allGroups);
} catch (e) {
KRLogUtil.kr_e('处理选择器代理出错: $e', tag: 'HomeController');
}
}
/// 处理手动模式
void _kr_handleManualMode(dynamic element) {
try {
KRLogUtil.kr_d(
'处理手动模式 - 内核选择: ${element.selected}, 用户选择: ${kr_cutTag.value}',
tag: 'HomeController');
// 🔧 关键修复:不要用内核返回的 selected 覆盖用户手动选择
// 只有当内核返回的节点与用户选择一致时,才更新相关状态
// 这可以防止 UI 跳动
// 更新延迟值(这个始终需要更新)
_kr_updateNodeLatency(element);
// 只有当内核确认切换成功时,才更新 UI 状态
if (element.selected == kr_cutTag.value) {
kr_cutSeletedTag.value = element.selected;
kr_currentNodeName.value =
kr_truncateText(element.selected, maxLength: 25);
// kr_moveToSelectedNode();
KRLogUtil.kr_d('✅ 内核确认节点切换成功: ${element.selected}',
tag: 'HomeController');
} else {
// 内核返回的节点与用户选择不一致,保持用户选择的显示
KRLogUtil.kr_d('⏳ 等待内核切换到用户选择的节点: ${kr_cutTag.value}',
tag: 'HomeController');
}
} catch (e) {
KRLogUtil.kr_e('处理手动模式出错: $e', tag: 'HomeController');
}
}
/// 更新节点延迟
void _kr_updateNodeLatency(dynamic element) {
try {
bool delayUpdated = false;
for (var subElement in element.items) {
if (subElement.tag == element.selected) {
// 检查延迟是否有效
if (subElement.urlTestDelay != 0) {
kr_currentNodeLatency.value = subElement.urlTestDelay;
delayUpdated = true;
}
// 更新速度显示
// kr_updateSpeed(subElement.urlTestDelay);
// // 停止动画
// _kr_speedAnimationController.reverse();
KRLogUtil.kr_d('更新节点延迟: ${subElement.urlTestDelay}',
tag: 'HomeController');
break;
}
}
// 🔧 修复如果已连接但延迟未更新设置为0
if (!delayUpdated &&
kr_isConnected.value &&
kr_currentNodeLatency.value == -1) {
KRLogUtil.kr_w('⚠️ 已连接但延迟未更新设置为0', tag: 'HomeController');
kr_currentNodeLatency.value = 0;
}
} catch (e) {
KRLogUtil.kr_e('更新节点延迟出错: $e', tag: 'HomeController');
// 🔧 修复:发生错误时,根据连接状态设置合适的值
if (kr_isConnected.value) {
kr_currentNodeLatency.value = 0; // 已连接但延迟未知
} else {
kr_currentNodeLatency.value = -2; // 未连接
}
kr_currentSpeed.value = "--";
// 停止动画
// _kr_speedAnimationController.reverse();
}
}
/// 处理自动模式
void _kr_handleAutoMode(dynamic element, List<dynamic> allGroups) {
KRLogUtil.kr_d('处理自动模式 - 活动组: ${allGroups.toString()}',
tag: 'HomeController');
// 更新auto模式的延迟
_kr_updateAutoLatency(element);
// 查找并处理urltest类型的组
for (var item in allGroups) {
if (item.type == ProxyType.urltest) {
// 检查延迟是否有效小于65535
if (item.selected != null && _kr_isValidLatency(item.selected)) {
kr_cutSeletedTag.value = item.selected;
kr_currentNodeName.value =
kr_truncateText("${item.selected}(auto)", maxLength: 25);
// kr_moveToSelectedNode();
kr_updateConnectionInfo();
break;
} else {
// 如果延迟无效,尝试选择延迟最小的节点
_kr_selectBestLatencyNode(item.items);
break;
}
}
}
}
/// 选择延迟最小的节点
void _kr_selectBestLatencyNode(List<dynamic> items) {
int minDelay = 65535;
String? bestNode = null;
for (var item in items) {
// 只考虑有效的延迟值小于65535且大于0
if (item.urlTestDelay < minDelay &&
item.urlTestDelay < 65535 &&
item.urlTestDelay > 0) {
minDelay = item.urlTestDelay;
bestNode = item.tag;
}
}
if (bestNode != null) {
kr_cutSeletedTag.value = bestNode;
kr_currentNodeName.value =
kr_truncateText("${bestNode}(auto)", maxLength: 25);
// kr_moveToSelectedNode();
kr_updateConnectionInfo();
}
}
/// 更新连接信息
void kr_updateConnectionInfo() {
try {
final selectedNode = kr_subscribeService.keyList[kr_cutSeletedTag.value];
if (selectedNode != null) {
KRLogUtil.kr_d(
'更新节点信息 - 协议: ${selectedNode.protocol}, IP: ${selectedNode.serverAddr}',
tag: 'HomeController');
kr_currentProtocol.value =
kr_truncateText(selectedNode.protocol, maxLength: 15);
kr_currentIp.value =
kr_truncateText(selectedNode.serverAddr, maxLength: 20);
} else {
KRLogUtil.kr_d('未找到选中的节点: ${kr_cutSeletedTag.value}',
tag: 'HomeController');
kr_currentProtocol.value = "--";
kr_currentIp.value = "--";
}
} catch (e) {
KRLogUtil.kr_e('更新连接信息失败: $e', tag: 'HomeController');
kr_currentProtocol.value = "--";
kr_currentIp.value = "--";
}
}
/// 处理文本截断
String kr_truncateText(String text, {int maxLength = 20}) {
if (text.length <= maxLength) return text;
return '${text.substring(0, maxLength)}...';
}
/// 检查延迟是否有效
bool _kr_isValidLatency(String? nodeTag) {
if (nodeTag == null) return false;
// 从keyList中获取节点信息
final node = kr_subscribeService.keyList[nodeTag];
if (node == null) return false;
// 检查延迟是否有效小于65535且大于0
return node.urlTestDelay.value < 65535 && node.urlTestDelay.value > 0;
}
/// 更新自动模式延迟
void _kr_updateAutoLatency(dynamic element) {
for (var subElement in element.items) {
if (subElement.tag == "auto") {
if (subElement.urlTestDelay != 0) {
kr_currentNodeLatency.value = subElement.urlTestDelay;
} else {
kr_currentNodeLatency.value = -2; // 当延迟为 0 时,设置为未连接状态
}
break;
}
}
}
/// 切换列表状态
void kr_switchListStatus(KRHomeViewsListStatus status) {
kr_currentListStatus.value = status;
}
// 切换订阅
Future<void> kr_switchSubscribe(
KRUserAvailableSubscribeItem subscribe) async {
try {
KRLogUtil.kr_i("kr_switchSubscribe", tag: "kr_switchSubscribe");
// 通知订阅服务切换订阅
await kr_subscribeService.kr_switchSubscribe(subscribe);
} catch (e) {
KRLogUtil.kr_e('切换订阅失败: $e', tag: 'HomeController');
rethrow;
}
}
// 🔒 节点切换状态锁和节流控制
bool _isSwitchingNode = false;
DateTime? _lastSwitchTime;
static const Duration _switchThrottleDuration =
Duration(milliseconds: 2000); // 2秒节流
/// 🔧 修复统一的节点切换方法包含UI同步和后台操作等待
/// 执行节点切换包括UI更新和后台操作的完整同步
/// 返回 true 表示切换成功false 表示失败
Future<bool> kr_performNodeSwitch(String tag) async {
try {
// 🔒 状态锁:防止并发切换
if (_isSwitchingNode) {
KRLogUtil.kr_w('⚠️ 节点切换正在进行中,忽略重复请求', tag: 'HomeController');
KRCommonUtil.kr_showToast('请等待当前节点切换完成');
return false;
}
if (kr_cutTag.value == tag) {
return false;
}
// 🔒 节流控制2秒内的重复切换请求直接忽略
final now = DateTime.now();
if (_lastSwitchTime != null &&
now.difference(_lastSwitchTime!) < _switchThrottleDuration) {
final remainingTime = _switchThrottleDuration.inMilliseconds -
now.difference(_lastSwitchTime!).inMilliseconds;
KRLogUtil.kr_w('⚠️ 切换过于频繁,请等待 ${remainingTime}ms',
tag: 'HomeController');
KRCommonUtil.kr_showToast('切换过于频繁,请稍后再试');
return false;
}
// 🔒 设置状态锁和记录时间
_isSwitchingNode = true;
_lastSwitchTime = now;
KRLogUtil.kr_i('🔄 开始切换节点: $tag', tag: 'HomeController');
// 1. 保存原节点,以备失败恢复
final originalTag = kr_cutTag.value;
// 2. 设置切换中状态
kr_cutTag.value = tag;
kr_currentNodeName.value = tag; // 🔧 修复:更新节点名称
// 3. 如果VPN未连接只更新UI变量即可
if (!kr_isConnected.value) {
KRLogUtil.kr_i('📴 VPN未连接只更新UI变量: $tag', tag: 'HomeController');
kr_cutSeletedTag.value = tag;
kr_updateConnectionInfo();
// kr_moveToSelectedNode();
// 🔧 修复保存节点选择以便VPN启动时应用
KRLogUtil.kr_i('💾 保存节点选择以便稍后应用: $tag', tag: 'HomeController');
KRSecureStorage()
.kr_saveData(key: 'SELECTED_NODE_TAG', value: tag)
.then((_) {
KRLogUtil.kr_i('✅ 节点选择已保存: $tag', tag: 'HomeController');
}).catchError((e) {
KRLogUtil.kr_e('❌ 保存节点选择失败: $e', tag: 'HomeController');
});
return true;
}
// 4. VPN已连接使用热切换方式selectOutbound切换节点
try {
KRLogUtil.kr_i('🔌 VPN已连接使用热切换方式切换节点: $tag', tag: 'HomeController');
// 🔧 设置切换中状态,显示"正在连接"
kr_currentNodeLatency.value = -1;
kr_isLatency.value = true; // 显示加载动画
// 🔧 保存新节点选择
KRLogUtil.kr_i('💾 保存新节点选择: $tag', tag: 'HomeController');
await KRSecureStorage()
.kr_saveData(key: 'SELECTED_NODE_TAG', value: tag);
// 🚀 核心改进:使用 selectOutbound 进行热切换(参考 hiddify-app
// 优势不重启VPN保持连接状态切换瞬间完成VPN开关不闪烁
KRLogUtil.kr_i('🔄 [热切换] 调用 selectOutbound 切换节点...',
tag: 'HomeController');
// 🔧 关键修复:确定正确的 selector 组 tag
// selectOutbound(groupTag, outboundTag) - 第一个参数是组的tag不是节点的tag
final activeGroups = KRSingBoxImp.instance.kr_activeGroups;
String selectorGroupTag = 'select'; // 默认值
// 查找 selector 类型的组
for (var group in activeGroups) {
if (group.type == ProxyType.selector) {
selectorGroupTag = group.tag;
KRLogUtil.kr_i('🔍 找到 selector 组: $selectorGroupTag',
tag: 'HomeController');
break;
}
}
KRLogUtil.kr_i('📡 调用 selectOutbound("$selectorGroupTag", "$tag")',
tag: 'HomeController');
// 调用 sing-box 的 selectOutbound API
final result = await KRSingBoxImp.instance.kr_singBox
.selectOutbound(selectorGroupTag, tag)
.run();
// 处理切换结果
result.fold(
(error) {
// 切换失败
KRLogUtil.kr_e('❌ selectOutbound 调用失败: $error',
tag: 'HomeController');
throw Exception('节点切换失败: $error');
},
(_) {
// 切换成功
KRSingBoxImp.instance.kr_startNodeSelectionMonitor(tag);
KRLogUtil.kr_i('✅ selectOutbound 调用成功', tag: 'HomeController');
},
);
// 后台切换成功立即更新UI乐观更新
kr_cutSeletedTag.value = tag;
kr_updateConnectionInfo();
// kr_moveToSelectedNode();
// 🔧 短暂等待以确保内核状态同步(相比重启,等待时间大幅缩短)
KRLogUtil.kr_i('⏳ [热切换] 等待内核状态同步200ms...', tag: 'HomeController');
await Future.delayed(const Duration(milliseconds: 200));
// 🔍 验证节点是否真正切换成功
KRLogUtil.kr_i('🔍 [验证] 检查节点切换结果...', tag: 'HomeController');
try {
final updatedGroups = KRSingBoxImp.instance.kr_activeGroups;
final selectGroup = updatedGroups.firstWhere(
(group) => group.type == ProxyType.selector,
orElse: () => throw Exception('未找到 selector 组'),
);
KRLogUtil.kr_i(
'📊 [验证] ${selectGroup.tag}组当前选中: ${selectGroup.selected}',
tag: 'HomeController');
KRLogUtil.kr_i('📊 [验证] 目标节点: $tag', tag: 'HomeController');
if (selectGroup.selected != tag) {
KRLogUtil.kr_w('⚠️ [验证] 节点选择验证失败,实际选中: ${selectGroup.selected}',
tag: 'HomeController');
// 不抛出异常,但记录警告
} else {
KRLogUtil.kr_i('✅ [验证] 节点选择验证成功!', tag: 'HomeController');
}
} catch (e) {
KRLogUtil.kr_w('⚠️ [验证] 节点验证过程出错: $e', tag: 'HomeController');
}
// 更新延迟信息
_kr_updateLatencyOnConnected();
KRLogUtil.kr_i('✅ 节点热切换成功VPN保持连接: $tag', tag: 'HomeController');
return true;
} catch (switchError) {
// 后台切换失败,恢复到原节点
KRLogUtil.kr_e('❌ 后台节点切换失败: $switchError', tag: 'HomeController');
// 恢复原状态
kr_cutTag.value = originalTag;
kr_currentNodeName.value = originalTag; // 🔧 修复:同时恢复节点名称显示
kr_currentNodeLatency.value = -2; // 恢复为未连接状态
// 恢复原节点选择
try {
await KRSecureStorage()
.kr_saveData(key: 'SELECTED_NODE_TAG', value: originalTag);
} catch (e) {
KRLogUtil.kr_e('❌ 恢复节点选择失败: $e', tag: 'HomeController');
}
// 显示错误提示给用户
KRCommonUtil.kr_showToast('节点切换失败,已恢复为: $originalTag');
return false;
}
} catch (e) {
KRLogUtil.kr_e('❌ 节点切换异常: $e', tag: 'HomeController');
kr_isLatency.value = false;
KRCommonUtil.kr_showToast('节点切换异常,请重试');
return false;
} finally {
// 🔒 释放状态锁
_isSwitchingNode = false;
// 关闭加载状态
kr_isLatency.value = false;
KRLogUtil.kr_i('🔄 节点切换流程完成', tag: 'HomeController');
}
}
/// 🔧 修复:简化的 kr_selectNode 方法
/// 现在只是委托给新的 kr_performNodeSwitch 方法
/// 为了保持向后兼容,保留此方法但改为调用新方法
Future<bool> kr_selectNode(String tag) async {
return await kr_performNodeSwitch(tag);
}
/// 获取当前节点国家
/// 🔧 修复:优先使用 kr_cutSeletedTag避免依赖可能为空的 kr_activeGroups
/// 策略:
/// 1. 如果 kr_cutTag 不是 auto直接使用用户手动选择的节点
/// 2. 如果是 auto优先使用 kr_cutSeletedTag保存了实际选中的节点
/// 3. 如果 kr_cutSeletedTag 也是 auto 或空,再尝试从 kr_activeGroups 获取
String kr_getCurrentNodeCountry() {
// KRLogUtil.kr_i('========== 开始获取国家代码 ==========',
// tag: 'getCurrentNodeCountry');
// KRLogUtil.kr_i('kr_cutTag: ${kr_cutTag.value}',
// tag: 'getCurrentNodeCountry');
// KRLogUtil.kr_i('kr_cutSeletedTag: ${kr_cutSeletedTag.value}',
// tag: 'getCurrentNodeCountry');
// KRLogUtil.kr_i('keyList 节点总数: ${kr_subscribeService.keyList.length}',
// tag: 'getCurrentNodeCountry');
String actualTag;
if (kr_selectedCountryTag.value == 'auto' &&
kr_cutSeletedTag.value.isNotEmpty) {
actualTag = kr_cutSeletedTag.value;
}
// 🔧 优先策略:
// 1. 如果 kr_cutTag 不是 auto直接使用用户手动选择的节点
else if (kr_cutTag.value != 'auto' &&
kr_cutTag.value != 'select' &&
kr_cutTag.value.isNotEmpty) {
// 用户手动选择了具体节点
actualTag = kr_cutTag.value;
// KRLogUtil.kr_i('✅ 使用手动选择的节点: $actualTag', tag: 'getCurrentNodeCountry');
}
// 2. 如果是 auto优先使用 kr_cutSeletedTag保存了实际选中的节点
else if (kr_cutSeletedTag.value.isNotEmpty &&
kr_cutSeletedTag.value != 'auto' &&
kr_cutSeletedTag.value != 'select') {
// auto 模式下,使用保存的实际节点
actualTag = kr_cutSeletedTag.value;
// KRLogUtil.kr_i('✅ 使用 auto 模式下的实际节点 (kr_cutSeletedTag): $actualTag',
// tag: 'getCurrentNodeCountry');
}
// 3. 降级:尝试从活动组获取
else {
try {
// KRLogUtil.kr_i('⚠️ 尝试从活动组获取实际节点', tag: 'getCurrentNodeCountry');
// KRLogUtil.kr_i('活动组数量: ${KRSingBoxImp.instance.kr_activeGroups.length}',
// tag: 'getCurrentNodeCountry');
// 🔧 修复:活动组为空时,尝试使用 allGroups
if (KRSingBoxImp.instance.kr_activeGroups.isEmpty) {
print('[getCurrentNodeCountry] ⚠️ 活动组为空,尝试使用 allGroups');
KRLogUtil.kr_w('⚠️ 活动组为空,尝试使用 allGroups',
tag: 'getCurrentNodeCountry');
final allGroups = KRSingBoxImp.instance.kr_allGroups;
print('[getCurrentNodeCountry] allGroups 数量: ${allGroups.length}');
if (allGroups.isEmpty) {
print('[getCurrentNodeCountry] ❌ allGroups 也为空,返回空字符串');
KRLogUtil.kr_w('❌ allGroups 也为空,返回空字符串',
tag: 'getCurrentNodeCountry');
return '';
}
// 从 allGroups 中查找 select 组
final selectGroup = allGroups.firstWhere(
(group) => group.tag == 'select',
orElse: () => throw Exception('未找到 select 组'),
);
print(
'[getCurrentNodeCountry] selectGroup.selected: ${selectGroup.selected}');
if (selectGroup.selected.isEmpty ||
selectGroup.selected == 'auto' ||
selectGroup.selected == 'select') {
print('[getCurrentNodeCountry] select 组选中的是 auto查找 urltest 组');
// 如果 select 组选中的是 auto从 urltest 组获取
final urlTestGroup = allGroups.firstWhere(
(group) => group.type == ProxyType.urltest,
orElse: () => throw Exception('未找到 urltest 组'),
);
if (urlTestGroup.selected.isNotEmpty) {
actualTag = urlTestGroup.selected;
print(
'[getCurrentNodeCountry] ✅ 从 allGroups 的 urltest 组获取节点: $actualTag');
KRLogUtil.kr_i('✅ 从 allGroups 的 urltest 组获取节点: $actualTag',
tag: 'getCurrentNodeCountry');
} else {
print('[getCurrentNodeCountry] ❌ urltest 组的 selected 也为空');
KRLogUtil.kr_w('❌ urltest 组的 selected 也为空',
tag: 'getCurrentNodeCountry');
return '';
}
} else {
actualTag = selectGroup.selected;
print(
'[getCurrentNodeCountry] ✅ 从 allGroups 的 select 组获取节点: $actualTag');
KRLogUtil.kr_i('✅ 从 allGroups 的 select 组获取节点: $actualTag',
tag: 'getCurrentNodeCountry');
}
} else {
// 活动组不为空,从活动组获取
// 从 SingBox 活动组中找到 "select" 选择器组
final selectGroup = KRSingBoxImp.instance.kr_activeGroups.firstWhere(
(group) => group.tag == 'select',
orElse: () => throw Exception('未找到 select 组'),
);
if (selectGroup.selected.isEmpty) {
KRLogUtil.kr_w('❌ select 组的 selected 为空',
tag: 'getCurrentNodeCountry');
return '';
}
actualTag = selectGroup.selected;
KRLogUtil.kr_i('✅ 从活动组获取节点: $actualTag',
tag: 'getCurrentNodeCountry');
}
} catch (e) {
KRLogUtil.kr_e('❌ 从活动组获取节点失败: $e', tag: 'getCurrentNodeCountry');
return '';
}
}
// 验证 actualTag 是否有效
if (actualTag.isEmpty) {
KRLogUtil.kr_w('❌ 节点标签为空', tag: 'getCurrentNodeCountry');
return '';
}
// 使用实际节点标签查找国家代码
KRLogUtil.kr_i('查找节点: $actualTag', tag: 'getCurrentNodeCountry');
final node = kr_subscribeService.keyList[actualTag];
if (node == null) {
KRLogUtil.kr_e('❌ 节点未找到: $actualTag', tag: 'getCurrentNodeCountry');
KRLogUtil.kr_e('keyList 中的所有节点标签:', tag: 'getCurrentNodeCountry');
int count = 0;
for (var key in kr_subscribeService.keyList.keys) {
count++;
KRLogUtil.kr_e(
' [$count] $key -> country: ${kr_subscribeService.keyList[key]?.country}',
tag: 'getCurrentNodeCountry');
if (count >= 10) {
KRLogUtil.kr_e(
' ... 还有 ${kr_subscribeService.keyList.length - 10} 个节点',
tag: 'getCurrentNodeCountry');
break;
}
}
return '';
}
// KRLogUtil.kr_i('✅ 找到节点: $actualTag', tag: 'getCurrentNodeCountry');
// KRLogUtil.kr_i(' - city: ${node.city}', tag: 'getCurrentNodeCountry');
// KRLogUtil.kr_i(' - country: "${node.country}"',
// tag: 'getCurrentNodeCountry');
// KRLogUtil.kr_i(' - country.isEmpty: ${node.country.isEmpty}',
// tag: 'getCurrentNodeCountry');
// KRLogUtil.kr_i(' - country.length: ${node.country.length}',
// tag: 'getCurrentNodeCountry');
// KRLogUtil.kr_i('========== 国家代码获取结束 ==========',
// tag: 'getCurrentNodeCountry');
return node.country;
}
/// 获取真实连接的节点信息(统一为手动选择场景)
Map<String, dynamic> kr_getRealConnectedNodeInfo() {
String actualTag =
kr_cutSeletedTag == 'auto' ? kr_cutSeletedTag.value : kr_cutTag.value;
final node = kr_subscribeService.keyList[actualTag];
return {
'nodeName': kr_cutTag.value,
'delay': node?.urlTestDelay.value ?? -2,
'country': node?.country ?? '',
};
}
/// 获取真实连接的节点国家
String kr_getRealConnectedNodeCountry() {
final country = kr_getCurrentNodeCountry();
if (country.isEmpty) return '';
return kr_getCountryFullName(country);
// controller.kr_cutSeletedTag.value
// final info = kr_getRealConnectedNodeInfo();
// final delay = info['delay'] as int; // 使用真实连接的节点延迟,而不是 kr_currentNodeLatency.value
// final country1 = kr_getCurrentNodeCountry();
// print('country----$country1');
// print('kr_getRealConnectedNodeCountry - delay from info: $delay, country from info: ${info['country']}');
// final country = kr_getCountryFullName(country1);
// if (delay == -2) {
// return '--';
// } else if (delay == -1) {
// return '${country} ${AppTranslations.kr_home.connecting}';
// } else if (delay == 0) {
// return '${country} ${AppTranslations.kr_home.connected}';
// } else if (delay >= 3000) {
// return '${country} ${AppTranslations.kr_home.timeout}';
// } else {
// return '${country} ${delay}ms';
// }
}
// 格式化字节数
String kr_formatBytes(int bytes) {
if (bytes < 1024) {
return '$bytes B';
} else if (bytes < 1024 * 1024) {
return '${(bytes / 1024).toStringAsFixed(1)} KB';
} else if (bytes < 1024 * 1024 * 1024) {
return '${(bytes / (1024 * 1024)).toStringAsFixed(1)} MB';
} else {
return '${(bytes / (1024 * 1024 * 1024)).toStringAsFixed(1)} GB';
}
}
// 设置当前选中的组
void kr_setCurrentGroup(dynamic group) {
try {
KRLogUtil.kr_i('设置当前组: ${group.tag}', tag: 'HomeController');
kr_currentGroup.value = group;
update(); // 通知 GetBuilder 更新
} catch (e) {
KRLogUtil.kr_e('设置当前组失败: $e', tag: 'HomeController');
}
}
/// 获取国家全称
/// [countryCode] 国家代码(大小写不敏感)
String kr_getCountryFullName(String countryCode) {
final Map<String, String> countryNames = {
'CN': '中国',
'HK': '香港',
'TW': '台湾',
'MO': '澳门',
'US': '美国',
'JP': '日本',
'KR': '韩国',
'SG': '新加坡',
'MY': '马来西亚',
'TH': '泰国',
'VN': '越南',
'ID': '印度尼西亚',
'PH': '菲律宾',
'IN': '印度',
'RU': '俄罗斯',
'GB': '英国',
'DE': '德国',
'FR': '法国',
'IT': '意大利',
'ES': '西班牙',
'NL': '荷兰',
'CH': '瑞士',
'SE': '瑞典',
'NO': '挪威',
'FI': '芬兰',
'DK': '丹麦',
'IE': '爱尔兰',
'AT': '奥地利',
'PT': '葡萄牙',
'PL': '波兰',
'UA': '乌克兰',
'CA': '加拿大',
'MX': '墨西哥',
'BR': '巴西',
'AR': '阿根廷',
'AU': '澳大利亚',
'NZ': '新西兰',
'ZA': '南非',
'AE': '阿拉伯联合酋长国',
'IL': '以色列',
'TR': '土耳其',
};
final String code = countryCode.toUpperCase();
return countryNames[code] ?? 'Unknown Country';
}
@override
void onReady() {
super.onReady();
Future.delayed(const Duration(milliseconds: 200), () async {
ensureAccountExists().then((ok) {
print('ok');
});
});
Future.delayed(const Duration(milliseconds: 500), () async {
if (Get.isRegistered<KRIAPService>()) {
KRIAPService.instance.setup();
}
});
}
@override
void onClose() {
if (kDebugMode) {
print('🧹 [HomeController] 开始清理资源...');
}
// 清理 EventBus 监听器
_eventBusWorker?.dispose();
if (kDebugMode) {
print('✅ [HomeController] EventBus 监听器已清理');
}
// 清理连接计时器
_kr_connectionTimer?.cancel();
if (kDebugMode) {
print('✅ [HomeController] 连接计时器已清理');
}
// 🔧 保守修复: 清理状态监控定时器
_statusWatchdogTimer?.cancel();
_connectionTimeoutTimer?.cancel();
if (kDebugMode) {
print('✅ [HomeController] 状态监控定时器已清理');
}
if (kDebugMode) {
print('✅ [HomeController] 资源清理完成');
}
super.onClose();
}
/// 应用生命周期状态变化处理
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
KRLogUtil.kr_i('应用生命周期状态变化: $state', tag: 'QuickConnect');
switch (state) {
case AppLifecycleState.resumed:
// 应用从后台恢复到前台,同步连接状态
KRLogUtil.kr_i('应用恢复到前台,同步连接状态', tag: 'QuickConnect');
_onAppResumed();
break;
case AppLifecycleState.paused:
// 应用进入后台
KRLogUtil.kr_i('应用进入后台', tag: 'QuickConnect');
break;
case AppLifecycleState.detached:
// 应用被销毁
KRLogUtil.kr_i('应用被销毁', tag: 'QuickConnect');
break;
case AppLifecycleState.inactive:
// 应用处于非活动状态(如来电话时)
KRLogUtil.kr_i('应用处于非活动状态', tag: 'QuickConnect');
break;
case AppLifecycleState.hidden:
// 应用被隐藏
KRLogUtil.kr_i('应用被隐藏', tag: 'QuickConnect');
break;
}
}
/// 应用恢复到前台时的处理
void _onAppResumed() {
// 同步连接状态确保UI显示正确
Future.delayed(const Duration(milliseconds: 500), () {
kr_forceSyncConnectionStatus();
});
}
// 更新底部面板高度
void kr_updateBottomPanelHeight() {
// 🔧 Android 15 优化:加载状态时也允许更新高度,显示加载指示器
if (kr_subscribeService.kr_currentStatus ==
KRHomeViewsListStatus.kr_loading &&
kr_currentListStatus.value != KRHomeViewsListStatus.kr_loading) {
return;
}
KRLogUtil.kr_i('更新底部面板高度', tag: 'HomeController');
KRLogUtil.kr_i('当前视图状态: ${kr_currentViewStatus.value}',
tag: 'HomeController');
KRLogUtil.kr_i('当前列表状态: ${kr_currentListStatus.value}',
tag: 'HomeController');
KRLogUtil.kr_i('是否试用: ${kr_subscribeService.kr_isTrial.value}',
tag: 'HomeController');
KRLogUtil.kr_i(
'是否最后一天: ${kr_subscribeService.kr_isLastDayOfSubscription.value}',
tag: 'HomeController');
double targetHeight = 0.0;
if (kr_currentViewStatus.value == KRHomeViewsStatus.kr_notLoggedIn) {
// 未登录状态下,高度由内容撑开
targetHeight = kr_subscriptionCardHeight +
kr_baseHeight +
kr_marginTop +
kr_marginBottom +
kr_marginVertical * 2;
KRLogUtil.kr_i('未登录状态,目标高度: $targetHeight', tag: 'HomeController');
} else if (kr_currentListStatus.value ==
KRHomeViewsListStatus.kr_serverList ||
kr_currentListStatus.value ==
KRHomeViewsListStatus.kr_countrySubscribeList ||
kr_currentListStatus.value ==
KRHomeViewsListStatus.kr_serverSubscribeList ||
kr_currentListStatus.value == KRHomeViewsListStatus.kr_subscribeList) {
targetHeight = kr_nodeListHeight + kr_marginVertical * 2;
KRLogUtil.kr_i('节点列表状态,目标高度: $targetHeight', tag: 'HomeController');
}
// 🔧 Android 15 新增:处理加载和错误状态
else if (kr_currentListStatus.value == KRHomeViewsListStatus.kr_loading ||
kr_currentListStatus.value == KRHomeViewsListStatus.kr_error) {
// 加载或错误状态下显示最小高度
targetHeight = kr_loadingHeight + kr_marginTop + kr_marginBottom;
KRLogUtil.kr_i('加载/错误状态,目标高度: $targetHeight', tag: 'HomeController');
} else {
// 已登录状态下的默认高度计算
targetHeight = kr_baseHeight + kr_marginTop + kr_marginBottom;
KRLogUtil.kr_i('基础高度: $targetHeight', tag: 'HomeController');
// 🔧 关键修复:增加防御性检查,确保订阅服务访问异常时高度计算仍正常
try {
if (kr_subscribeService.kr_currentSubscribe.value != null) {
targetHeight += kr_connectionInfoHeight + kr_marginTop;
KRLogUtil.kr_i('添加连接信息卡片高度: $targetHeight', tag: 'HomeController');
} else {
targetHeight += kr_subscriptionCardHeight + kr_marginTop;
KRLogUtil.kr_i('添加订阅卡片高度: $targetHeight', tag: 'HomeController');
}
// 如果有试用状态,添加试用卡片高度
if (kr_subscribeService.kr_isTrial.value) {
targetHeight += kr_trialCardHeight + kr_marginTop;
KRLogUtil.kr_i('添加试用卡片高度: $targetHeight', tag: 'HomeController');
}
// 如果是最后一天,添加最后一天卡片高度
else if (kr_subscribeService.kr_isLastDayOfSubscription.value) {
targetHeight += kr_lastDayCardHeight + kr_marginTop;
KRLogUtil.kr_i('添加最后一天卡片高度: $targetHeight', tag: 'HomeController');
}
} catch (e) {
// 🔧 修复:订阅服务访问异常时,使用默认订阅卡片高度
KRLogUtil.kr_e('访问订阅服务数据异常,使用默认高度: $e', tag: 'HomeController');
targetHeight += kr_subscriptionCardHeight + kr_marginTop;
KRLogUtil.kr_i('使用默认订阅卡片高度: $targetHeight', tag: 'HomeController');
}
}
// 🔧 Android 15 优化:确保最小高度,避免出现 0 高度
if (targetHeight < 100) {
KRLogUtil.kr_w('计算的高度过小($targetHeight),设置为最小高度', tag: 'HomeController');
targetHeight = kr_loadingHeight;
}
KRLogUtil.kr_i('最终目标高度: $targetHeight', tag: 'HomeController');
kr_bottomPanelHeight.value = targetHeight;
}
// 移动到选中节点
void kr_moveToSelectedNode() {
}
// 简化移动地图方法
void kr_moveToLocation() {
try {
// 🔧 关键修复:约束坐标到有效范围,防止超出地图边界
kr_isUserMoving.value = false;
} catch (e) {
KRLogUtil.kr_e('移动地图失败: $e', tag: 'HomeController');
}
}
// 添加一个方法来批量更新标记
void kr_updateMarkers(List<String> tags) {
// 使用Set来去重
final Set<String> updateIds = tags.toSet();
// 一次性更新所有需要更新的标记
update(updateIds.toList());
// 延迟2秒后关闭加载状态
Future.delayed(const Duration(seconds: 1), () {
kr_isLatency.value = false;
});
}
/// 刷新地图标记
void showMarkersMap() {
KRLogUtil.kr_i('========== 刷新地图标记 ==========', tag: 'HomeController');
KRLogUtil.kr_i('当前选中节点: ${kr_cutSeletedTag.value}', tag: 'HomeController');
KRLogUtil.kr_i('可用节点数: ${kr_subscribeService.allList.length}',
tag: 'HomeController');
KRLogUtil.kr_i('国家分组数: ${kr_subscribeService.countryOutboundList.length}',
tag: 'HomeController');
// 手动触发地图标记更新
update(['map_markers']);
KRLogUtil.kr_i('✅ 地图标记更新完成', tag: 'HomeController');
}
/// 选择地图标记
void selectMarkerMap(int index) {
try {
if (index >= 0 && index < kr_subscribeService.allList.length) {
// 重置所有节点的选中状态
for (var item in kr_subscribeService.allList) {
item.selected = 0;
}
// 设置当前节点为选中状态
kr_subscribeService.allList[index].selected = 1;
// 手动触发更新
update(['map_markers']);
}
} catch (e) {
KRLogUtil.kr_e('选择地图标记失败: $e', tag: 'HomeController');
}
}
/// 手动触发 SingBox URL 测试(调试用)
Future<void> kr_manualUrlTest() async {
try {
KRLogUtil.kr_i('🔧 手动触发 SingBox URL 测试...', tag: 'HomeController');
// 直接调用 SingBox 的 URL 测试
await KRSingBoxImp.instance.kr_urlTest("auto");
// 等待测试完成
await Future.delayed(const Duration(seconds: 5));
// 检查结果
KRLogUtil.kr_i('📊 检查手动测试结果...', tag: 'HomeController');
final activeGroups = KRSingBoxImp.instance.kr_activeGroups;
for (int i = 0; i < activeGroups.length; i++) {
final group = activeGroups[i];
KRLogUtil.kr_i(
'📋 活动组[$i]: tag=${group.tag}, type=${group.type}, selected=${group.selected}',
tag: 'HomeController');
for (int j = 0; j < group.items.length; j++) {
final item = group.items[j];
KRLogUtil.kr_i(
' └─ 节点[$j]: tag=${item.tag}, type=${item.type}, delay=${item.urlTestDelay}',
tag: 'HomeController');
}
}
} catch (e) {
KRLogUtil.kr_e('❌ 手动 URL 测试失败: $e', tag: 'HomeController');
}
}
/// 强制使用直接连接测试(绕过 SingBox URL 测试)
Future<void> kr_forceDirectTest() async {
try {
KRLogUtil.kr_i('🔧 强制使用直接连接测试...', tag: 'HomeController');
// 使用直接连接测试所有节点
await _kr_testLatencyWithoutVpn();
KRLogUtil.kr_i('✅ 直接连接测试完成', tag: 'HomeController');
} catch (e) {
KRLogUtil.kr_e('❌ 直接连接测试失败: $e', tag: 'HomeController');
}
}
/// 测试延迟
Future<void> kr_urlTest() async {
kr_isLatency.value = true;
try {
KRLogUtil.kr_i('🧪 开始延迟测试...', tag: 'HomeController');
KRLogUtil.kr_i('📊 当前连接状态: ${kr_isConnected.value}',
tag: 'HomeController');
if (kr_isConnected.value) {
// 已连接状态:使用 SingBox 通过代理测试
KRLogUtil.kr_i('🔗 已连接状态 - 使用 SingBox 通过代理测试延迟', tag: 'HomeController');
await KRSingBoxImp.instance.kr_urlTest("select");
// 等待一段时间让 SingBox 完成测试
await Future.delayed(const Duration(seconds: 3));
// 再次检查活动组状态
KRLogUtil.kr_i('🔄 检查代理测试后的活动组状态...', tag: 'HomeController');
final activeGroups = KRSingBoxImp.instance.kr_activeGroups;
for (int i = 0; i < activeGroups.length; i++) {
final group = activeGroups[i];
KRLogUtil.kr_i(
'📋 活动组[$i]: tag=${group.tag}, type=${group.type}, selected=${group.selected}',
tag: 'HomeController');
for (int j = 0; j < group.items.length; j++) {
final item = group.items[j];
KRLogUtil.kr_i(
' └─ 节点[$j]: tag=${item.tag}, type=${item.type}, delay=${item.urlTestDelay}',
tag: 'HomeController');
}
}
} else {
// 未连接状态使用本机网络直接ping节点IP
KRLogUtil.kr_i('🔌 未连接状态 - 使用本机网络直接ping节点IP测试延迟',
tag: 'HomeController');
KRLogUtil.kr_i('🌐 这将绕过代理,直接使用本机网络连接节点', tag: 'HomeController');
await _kr_testLatencyWithoutVpn();
}
} catch (e) {
KRLogUtil.kr_e('❌ 延迟测试失败: $e', tag: 'HomeController');
} finally {
// 延迟1秒后关闭加载状态
Future.delayed(const Duration(seconds: 1), () {
kr_isLatency.value = false;
});
}
}
/// 未连接状态下的真实延迟测试TCP连接测试
Future<void> _kr_testLatencyWithoutVpn() async {
kr_isLatency.value = true;
try {
KRLogUtil.kr_i('🚀 开始真实延迟测试(按国家分两阶段)', tag: 'HomeController');
KRLogUtil.kr_i('📊 当前连接状态: ${kr_isConnected.value}',
tag: 'HomeController');
final allNodes = kr_subscribeService.allList
.where((item) => item.tag != 'auto')
.toList();
KRLogUtil.kr_i('📋 可测试节点总数: ${allNodes.length}', tag: 'HomeController');
if (allNodes.isEmpty) {
KRLogUtil.kr_w('⚠️ 没有可测试的节点', tag: 'HomeController');
return;
}
final Map<String, List<dynamic>> byCountry = {};
for (final node in allNodes) {
final code = node.country;
byCountry.putIfAbsent(code, () => <dynamic>[]).add(node);
}
KRLogUtil.kr_i('🌎 国家数: ${byCountry.length}', tag: 'HomeController');
final Map<String, dynamic> tagToNode = {
for (final n in allNodes) n.tag: n,
};
final List<MapEntry<String, SocketAddress>> stage1 = [];
final List<String> stage1Tags = [];
for (final entry in byCountry.entries) {
final list = entry.value;
if (list.isEmpty) continue;
final node = list.first;
try {
String host = node.serverAddr;
int port = 0;
if (node.config.containsKey('server_port')) {
port = node.config['server_port'] as int;
} else if (node.config.containsKey('port')) {
port = node.config['port'] as int;
}
if (host.isNotEmpty && port > 0) {
stage1.add(MapEntry(node.tag, SocketAddress(host, port)));
stage1Tags.add(node.tag);
KRLogUtil.kr_i('阶段1加入: ${entry.key}/${node.tag} -> $host:$port',
tag: 'HomeController');
} else {
final n = tagToNode[node.tag];
if (n != null) n.urlTestDelay.value = 65535;
KRLogUtil.kr_w('阶段1节点缺少地址或端口: ${node.tag}', tag: 'HomeController');
}
} catch (e) {
final n = tagToNode[node.tag];
if (n != null) n.urlTestDelay.value = 65535;
KRLogUtil.kr_w('阶段1解析失败: ${node.tag}, $e', tag: 'HomeController');
}
}
if (stage1.isNotEmpty) {
KRLogUtil.kr_i('🔌 阶段1测试国家首个节点: ${stage1.length}',
tag: 'HomeController');
final results1 = await KRLatencyTester.testMultipleNodes(
nodes: stage1,
concurrency: 5,
timeout: const Duration(seconds: 5),
);
int s1ok = 0;
for (final tag in stage1Tags) {
final delay = results1[tag];
if (delay != null) {
final node = tagToNode[tag];
if (node != null) {
node.urlTestDelay.value = delay;
s1ok++;
}
}
}
KRLogUtil.kr_i('✅ 阶段1完成: 成功 $s1ok/${stage1Tags.length}',
tag: 'HomeController');
}
final List<MapEntry<String, SocketAddress>> stage2 = [];
final List<String> stage2Tags = [];
for (final entry in byCountry.entries) {
final list = entry.value;
if (list.length <= 1) continue;
for (final node in list.skip(1)) {
try {
String host = node.serverAddr;
int port = 0;
if (node.config.containsKey('server_port')) {
port = node.config['server_port'] as int;
} else if (node.config.containsKey('port')) {
port = node.config['port'] as int;
}
if (host.isNotEmpty && port > 0) {
stage2.add(MapEntry(node.tag, SocketAddress(host, port)));
stage2Tags.add(node.tag);
} else {
final n = tagToNode[node.tag];
if (n != null) n.urlTestDelay.value = 65535;
}
} catch (e) {
final n = tagToNode[node.tag];
if (n != null) n.urlTestDelay.value = 65535;
}
}
}
if (stage2.isNotEmpty) {
KRLogUtil.kr_i('🔌 阶段2测试剩余节点: ${stage2.length}', tag: 'HomeController');
final results2 = await KRLatencyTester.testMultipleNodes(
nodes: stage2,
concurrency: 10,
timeout: const Duration(seconds: 5),
);
int s2ok = 0;
for (final tag in stage2Tags) {
final delay = results2[tag];
if (delay != null) {
final node = tagToNode[tag];
if (node != null) {
node.urlTestDelay.value = delay;
s2ok++;
}
}
}
KRLogUtil.kr_i('✅ 阶段2完成: 成功 $s2ok/${stage2Tags.length}',
tag: 'HomeController');
}
final tested =
allNodes.where((n) => n.urlTestDelay.value < 65535).toList();
final notTested = allNodes.length - tested.length;
KRLogUtil.kr_i('📊 总结: 可用 ${tested.length}, 不可用/失败 ${notTested}',
tag: 'HomeController');
} catch (e) {
KRLogUtil.kr_e('❌ 真实延迟测试过程出错: $e', tag: 'HomeController');
KRLogUtil.kr_e('❌ 错误堆栈: ${StackTrace.current}', tag: 'HomeController');
} finally {
kr_isLatency.value = false;
}
}
/// 开始连接计时
void kr_startConnectionTimer() {
kr_stopConnectionTimer();
// _kr_connectionSeconds = 0;
// _kr_connectionTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
// _kr_connectionSeconds++;
// kr_connectionTime.value = kr_formatDuration(_kr_connectionSeconds);
// KRLogUtil.kr_i(kr_connectText.value, tag: 'kr_startConnectionTimer');
// });
}
/// 停止连接计时
void kr_stopConnectionTimer() {
_kr_connectionTimer?.cancel();
_kr_connectionTimer = null;
}
/// 格式化时长
String kr_formatDuration(int seconds) {
final hours = seconds ~/ 3600;
final minutes = (seconds % 3600) ~/ 60;
final remainingSeconds = seconds % 60;
return '${hours.toString().padLeft(2, '0')}:'
'${minutes.toString().padLeft(2, '0')}:'
'${remainingSeconds.toString().padLeft(2, '0')}';
}
/// 重置连接信息
void kr_resetConnectionInfo() {
kr_currentIp.value = AppTranslations.kr_home.disconnected;
kr_currentProtocol.value = AppTranslations.kr_home.disconnected;
kr_currentSpeed.value = "--";
kr_connectionTime.value = '00:00:00';
_kr_connectionSeconds = 0;
kr_currentNodeLatency.value = -2; // 设置为未连接状态
}
/// 调试:打印所有节点的坐标信息
void kr_debugPrintNodeCoordinates() {
KRLogUtil.kr_i('========== 节点坐标调试信息 ==========', tag: 'HomeController');
KRLogUtil.kr_i('节点总数: ${kr_subscribeService.allList.length}',
tag: 'HomeController');
if (kr_subscribeService.allList.isEmpty) {
KRLogUtil.kr_w('节点列表为空!请检查:', tag: 'HomeController');
KRLogUtil.kr_w('1. 是否已登录', tag: 'HomeController');
KRLogUtil.kr_w('2. 是否有订阅', tag: 'HomeController');
KRLogUtil.kr_w('3. 订阅是否已加载完成', tag: 'HomeController');
return;
}
int validNodes = 0;
int invalidNodes = 0;
for (int i = 0; i < kr_subscribeService.allList.length; i++) {
final node = kr_subscribeService.allList[i];
if (node.latitude != 0.0 || node.longitude != 0.0) {
validNodes++;
if (i < 5) {
// 只打印前5个有效节点
KRLogUtil.kr_i(
'节点[$i] ${node.tag}: (${node.latitude}, ${node.longitude})',
tag: 'HomeController');
}
} else {
invalidNodes++;
if (i < 3) {
// 只打印前3个无效节点
KRLogUtil.kr_w('节点[$i] ${node.tag}: 坐标为(0, 0) - 无效!',
tag: 'HomeController');
}
}
}
KRLogUtil.kr_i('有效节点: $validNodes', tag: 'HomeController');
KRLogUtil.kr_w('无效节点(坐标为0): $invalidNodes', tag: 'HomeController');
if (invalidNodes > 0) {
KRLogUtil.kr_w('⚠️ 发现 $invalidNodes 个节点坐标为0这些节点不会显示在地图上',
tag: 'HomeController');
KRLogUtil.kr_w('可能原因:', tag: 'HomeController');
KRLogUtil.kr_w('1. 后端API未返回 latitude/longitude 字段',
tag: 'HomeController');
KRLogUtil.kr_w('2. 后端数据库中节点坐标未配置', tag: 'HomeController');
}
}
/// 强制同步连接状态
void kr_forceSyncConnectionStatus([bool? isQuickConnect]) {
try {
KRLogUtil.kr_i('🔄 强制同步连接状态...', tag: 'HomeController');
final currentStatus = KRSingBoxImp.instance.kr_status.value;
KRLogUtil.kr_i('📊 当前 SingBox 状态: $currentStatus', tag: 'HomeController');
// 根据当前状态强制更新UI
switch (currentStatus) {
case SingboxStopped():
kr_connectText.value = AppTranslations.kr_home.disconnected;
kr_isConnected.value = false;
kr_currentSpeed.value = "--";
kr_currentNodeLatency.value = -2;
break;
case SingboxStarting():
kr_connectText.value = AppTranslations.kr_home.connecting;
kr_isConnected.value = false;
kr_currentSpeed.value = AppTranslations.kr_home.connecting;
kr_currentNodeLatency.value = -1;
break;
case SingboxStarted():
kr_connectText.value = AppTranslations.kr_home.connected;
kr_isConnected.value = true;
kr_startConnectionTimer();
kr_updateConnectionInfo();
// 🔧 修复:同步已启动状态时,尝试更新延迟值
if (!_kr_tryUpdateDelayFromActiveGroups()) {
// 如果获取不到延迟值设置为0已连接但延迟未知
kr_currentNodeLatency.value = 0;
KRLogUtil.kr_w('⚠️ 强制同步时无法获取延迟值设置为0', tag: 'HomeController');
}
break;
case SingboxStopping():
kr_connectText.value = AppTranslations.kr_home.disconnecting;
kr_isConnected.value = false;
kr_currentSpeed.value = "--";
break;
}
// 强制更新UI
update();
print('isQuickConnect$isQuickConnect');
if (isQuickConnect == true) {
_checkQuickConnectAutoStart();
}
KRLogUtil.kr_i('✅ 连接状态同步完成', tag: 'HomeController');
} catch (e) {
KRLogUtil.kr_e('❌ 强制同步连接状态失败: $e', tag: 'HomeController');
}
}
/// 🔧 保守修复: 强制重置状态(用于状态卡住时)
Future<void> _forceResetState() async {
try {
KRLogUtil.kr_w('🔄 开始强制重置状态...', tag: 'HomeController');
// 1. 尝试停止 SingBox
try {
await KRSingBoxImp.instance.kr_stop().timeout(
const Duration(seconds: 5),
onTimeout: () {
KRLogUtil.kr_w('⚠️ 强制停止超时,继续重置', tag: 'HomeController');
},
);
} catch (e) {
KRLogUtil.kr_w('⚠️ 强制停止失败: $e,继续重置', tag: 'HomeController');
}
// 2. 等待状态稳定
await Future.delayed(const Duration(milliseconds: 500));
// 3. 强制重置所有 UI 状态为断开
kr_connectText.value = AppTranslations.kr_home.disconnected;
KRSingBoxImp.instance.kr_status.value = SingboxStatus.stopped();
kr_isConnected.value = false;
kr_currentSpeed.value = "--";
kr_currentNodeLatency.value = -2;
kr_currentIp.value = "--";
kr_currentProtocol.value = "--";
kr_stopConnectionTimer();
kr_resetConnectionInfo();
// 4. 重置状态变化时间
_lastStatusChangeTime = null;
// 5. 强制更新 UI
update();
KRLogUtil.kr_i('✅ 强制重置状态完成', tag: 'HomeController');
} catch (e) {
KRLogUtil.kr_e('❌ 强制重置状态失败: $e', tag: 'HomeController');
}
}
/// 🔧 保守修复: 启动状态监控定时器,定期检测状态是否卡住
void _startStatusWatchdog() {
_statusWatchdogTimer?.cancel();
_statusWatchdogTimer = Timer.periodic(const Duration(seconds: 30), (timer) {
final currentStatus = KRSingBoxImp.instance.kr_status.value;
// 只检测中间状态Starting/Stopping
if (currentStatus is SingboxStarting ||
currentStatus is SingboxStopping) {
final now = DateTime.now();
if (_lastStatusChangeTime != null) {
final duration = now.difference(_lastStatusChangeTime!);
if (duration > const Duration(seconds: 5)) {
// 状态卡住超过 5 秒
KRLogUtil.kr_w(
'⚠️ [Watchdog] 检测到状态卡住: ${currentStatus.runtimeType}, 持续时间: ${duration.inSeconds}',
tag: 'HomeController',
);
// 自动触发强制重置
_forceResetState().then((_) {});
}
}
}
});
KRLogUtil.kr_i('✅ 状态监控定时器已启动', tag: 'HomeController');
}
/// 连接超时处理
Timer? _connectionTimeoutTimer;
void _startConnectionTimeout() {
_connectionTimeoutTimer?.cancel();
_connectionTimeoutTimer = Timer(const Duration(seconds: 30), () {
KRLogUtil.kr_w('⏰ 连接超时,强制重置状态', tag: 'HomeController');
// 检查是否仍在连接中
if (KRSingBoxImp.instance.kr_status.value is SingboxStarting) {
KRLogUtil.kr_w('🔄 连接超时,强制停止并重置', tag: 'HomeController');
// 强制停止连接
KRSingBoxImp.instance.kr_stop().then((_) {
// 重置状态
kr_connectText.value = AppTranslations.kr_home.disconnected;
kr_isConnected.value = false;
kr_currentSpeed.value = "--";
kr_currentNodeLatency.value = -2;
kr_resetConnectionInfo();
update();
KRLogUtil.kr_i('✅ 连接超时处理完成', tag: 'HomeController');
}).catchError((e) {
KRLogUtil.kr_e('❌ 连接超时处理失败: $e', tag: 'HomeController');
});
}
});
}
void _cancelConnectionTimeout() {
_connectionTimeoutTimer?.cancel();
_connectionTimeoutTimer = null;
}
/// 连接成功后更新延迟值
void _kr_updateLatencyOnConnected() {
KRLogUtil.kr_i('🔧 尝试获取连接延迟值...', tag: 'HomeController');
// 立即尝试从活动组获取延迟
bool delayUpdated = _kr_tryUpdateDelayFromActiveGroups();
if (delayUpdated) {
KRLogUtil.kr_i('✅ 延迟值已从活动组更新', tag: 'HomeController');
return;
}
// 如果立即获取失败,设置临时值并启动延迟重试
KRLogUtil.kr_w('⚠️ 活动组暂无延迟数据,设置临时值并启动重试', tag: 'HomeController');
kr_currentNodeLatency.value = 0; // 设置为0表示已连接但延迟未知
// 延迟500ms后重试等待活动组数据到达
Future.delayed(const Duration(milliseconds: 500), () {
if (_kr_tryUpdateDelayFromActiveGroups()) {
KRLogUtil.kr_i('✅ 延迟重试成功,延迟值已更新', tag: 'HomeController');
return;
}
// 再次延迟1秒重试
Future.delayed(const Duration(seconds: 1), () {
if (_kr_tryUpdateDelayFromActiveGroups()) {
KRLogUtil.kr_i('✅ 第二次延迟重试成功', tag: 'HomeController');
return;
}
// 如果还是获取不到保持为0表示已连接但延迟未知
KRLogUtil.kr_w('⚠️ 多次重试后仍无法获取延迟值,保持为已连接状态', tag: 'HomeController');
kr_currentNodeLatency.value = 0;
});
});
}
/// 获取指定国家的所有真实节点延迟列表
List<Map<String, dynamic>> kr_getCountryRealNodeDelays(String countryCode) {
final delays = <Map<String, dynamic>>[];
try {
// 从订阅服务中获取该国家的节点列表
final countryNodes = kr_subscribeService.keyList.values
.where((item) => item.country == countryCode)
.toList();
for (final node in countryNodes) {
delays.add({
'tag': node.tag,
'delay': node.urlTestDelay.value,
'city': node.city,
});
}
// 按延迟排序
delays.sort((a, b) => a['delay'].compareTo(b['delay']));
} catch (e) {
KRLogUtil.kr_e('获取国家真实节点延迟列表失败: $e', tag: 'HomeController');
}
return delays;
}
/// 尝试从活动组更新延迟值
bool _kr_tryUpdateDelayFromActiveGroups() {
try {
final activeGroups = KRSingBoxImp.instance.kr_activeGroups;
if (activeGroups.isEmpty) {
KRLogUtil.kr_d('活动组为空', tag: 'HomeController');
return false;
}
// 查找 selector 类型的组
for (var group in activeGroups) {
if (group.type == ProxyType.selector) {
KRLogUtil.kr_d('找到 selector 组: ${group.tag}, 选中: ${group.selected}',
tag: 'HomeController');
for (var item in group.items) {
if (item.tag == kr_cutTag.value && item.urlTestDelay != 0) {
kr_currentNodeLatency.value = item.urlTestDelay;
KRLogUtil.kr_i('✅ 延迟值: ${item.urlTestDelay}ms',
tag: 'HomeController');
return true;
}
}
}
}
KRLogUtil.kr_d('未找到匹配的延迟数据', tag: 'HomeController');
return false;
} catch (e) {
KRLogUtil.kr_e('获取延迟值失败: $e', tag: 'HomeController');
return false;
}
}
}