@@ -90,8 +90,6 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
|
||||
// 是否显示延迟
|
||||
final kr_isLatency = false.obs;
|
||||
|
||||
final kr_isPositionAnimating = false.obs;
|
||||
|
||||
/// 默认
|
||||
var kr_cutTag = ''.obs;
|
||||
var kr_cutSeletedTag = ''.obs;
|
||||
@@ -219,17 +217,14 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
|
||||
|
||||
await _kr_prepareCountrySelectionBeforeStart();
|
||||
final selectedAfter =
|
||||
await KRSecureStorage().kr_readData(key: 'SELECTED_NODE_TAG');
|
||||
KRLogUtil.kr_i(
|
||||
'准备后 SELECTED_NODE_TAG_autoConnect: ${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}',
|
||||
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}',
|
||||
KRLogUtil.kr_i('准备后 kr_cutSeletedTag_autoConnect: ${kr_cutSeletedTag.value}',
|
||||
tag: 'HomeController');
|
||||
await kr_performNodeSwitch(selectedAfter!);
|
||||
await KRSingBoxImp.instance.kr_start();
|
||||
@@ -320,42 +315,11 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
|
||||
});
|
||||
|
||||
// 🔧 Android 15 新增:5秒后再次强制更新高度,兜底保护
|
||||
// Future.delayed(const Duration(seconds: 5), () {
|
||||
// if (kr_bottomPanelHeight.value < 100) {
|
||||
// KRLogUtil.kr_w('检测到底部面板高度异常(${kr_bottomPanelHeight.value}),强制修正',
|
||||
// tag: 'HomeController');
|
||||
// kr_updateBottomPanelHeight();
|
||||
// }
|
||||
// });
|
||||
|
||||
_initShowAnimationWatcher();
|
||||
}
|
||||
|
||||
bool _lastIsShow = false;
|
||||
|
||||
void _initShowAnimationWatcher() {
|
||||
_lastIsShow = kr_currentNodeLatency.value == -1 || kr_isConnected.value;
|
||||
everAll([kr_currentNodeLatency, kr_isConnected], (_) {
|
||||
final v = kr_currentNodeLatency.value == -1 || kr_isConnected.value;
|
||||
if (v != _lastIsShow) {
|
||||
_triggerPositionAnim();
|
||||
_lastIsShow = v;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void kr_onPositionAnimationEnd() {
|
||||
kr_isPositionAnimating.value = false;
|
||||
}
|
||||
|
||||
int _positionAnimToken = 0;
|
||||
void _triggerPositionAnim() {
|
||||
_positionAnimToken++;
|
||||
final token = _positionAnimToken;
|
||||
kr_isPositionAnimating.value = true;
|
||||
Future.delayed(const Duration(milliseconds: 450), () {
|
||||
if (_positionAnimToken == token) {
|
||||
kr_isPositionAnimating.value = false;
|
||||
Future.delayed(const Duration(seconds: 5), () {
|
||||
if (kr_bottomPanelHeight.value < 100) {
|
||||
KRLogUtil.kr_w('检测到底部面板高度异常(${kr_bottomPanelHeight.value}),强制修正',
|
||||
tag: 'HomeController');
|
||||
kr_updateBottomPanelHeight();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -974,26 +938,6 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
|
||||
if (kDebugMode) {}
|
||||
}
|
||||
|
||||
DateTime? _lastConnectClickAt;
|
||||
static const Duration _connectClickCooldown = Duration(milliseconds: 600);
|
||||
|
||||
bool kr_canProcessConnectClick() {
|
||||
final now = DateTime.now();
|
||||
if (kr_isPositionAnimating.value) {
|
||||
return false;
|
||||
}
|
||||
final st = KRSingBoxImp.instance.kr_status.value;
|
||||
if (st is SingboxStarting || st is SingboxStopping) {
|
||||
return false;
|
||||
}
|
||||
if (_lastConnectClickAt != null &&
|
||||
now.difference(_lastConnectClickAt!) < _connectClickCooldown) {
|
||||
return false;
|
||||
}
|
||||
_lastConnectClickAt = now;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// 🔧 等待状态达到预期值
|
||||
Future<void> _waitForStatus(Type expectedType, {int maxSeconds = 3}) async {
|
||||
if (kDebugMode) {}
|
||||
@@ -1204,9 +1148,7 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
|
||||
/// 处理手动模式
|
||||
void _kr_handleManualMode(dynamic element) {
|
||||
try {
|
||||
KRLogUtil.kr_d(
|
||||
'处理手动模式 - 内核选择: ${element.selected}, 用户选择: ${kr_cutTag.value}',
|
||||
tag: 'HomeController');
|
||||
KRLogUtil.kr_d('处理手动模式 - 内核选择: ${element.selected}, 用户选择: ${kr_cutTag.value}', tag: 'HomeController');
|
||||
|
||||
// 🔧 关键修复:不要用内核返回的 selected 覆盖用户手动选择
|
||||
// 只有当内核返回的节点与用户选择一致时,才更新相关状态
|
||||
@@ -1218,15 +1160,12 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
|
||||
// 只有当内核确认切换成功时,才更新 UI 状态
|
||||
if (element.selected == kr_cutTag.value) {
|
||||
kr_cutSeletedTag.value = element.selected;
|
||||
kr_currentNodeName.value =
|
||||
kr_truncateText(element.selected, maxLength: 25);
|
||||
kr_currentNodeName.value = kr_truncateText(element.selected, maxLength: 25);
|
||||
// kr_moveToSelectedNode();
|
||||
KRLogUtil.kr_d('✅ 内核确认节点切换成功: ${element.selected}',
|
||||
tag: 'HomeController');
|
||||
KRLogUtil.kr_d('✅ 内核确认节点切换成功: ${element.selected}', tag: 'HomeController');
|
||||
} else {
|
||||
// 内核返回的节点与用户选择不一致,保持用户选择的显示
|
||||
KRLogUtil.kr_d('⏳ 等待内核切换到用户选择的节点: ${kr_cutTag.value}',
|
||||
tag: 'HomeController');
|
||||
KRLogUtil.kr_d('⏳ 等待内核切换到用户选择的节点: ${kr_cutTag.value}', tag: 'HomeController');
|
||||
}
|
||||
} catch (e) {
|
||||
KRLogUtil.kr_e('处理手动模式出错: $e', tag: 'HomeController');
|
||||
@@ -1425,12 +1364,9 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
|
||||
|
||||
// 🔒 节流控制: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');
|
||||
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;
|
||||
}
|
||||
@@ -1457,9 +1393,7 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
|
||||
|
||||
// 🔧 修复:保存节点选择以便VPN启动时应用
|
||||
KRLogUtil.kr_i('💾 保存节点选择以便稍后应用: $tag', tag: 'HomeController');
|
||||
KRSecureStorage()
|
||||
.kr_saveData(key: 'SELECTED_NODE_TAG', value: tag)
|
||||
.then((_) {
|
||||
KRSecureStorage().kr_saveData(key: 'SELECTED_NODE_TAG', value: tag).then((_) {
|
||||
KRLogUtil.kr_i('✅ 节点选择已保存: $tag', tag: 'HomeController');
|
||||
}).catchError((e) {
|
||||
KRLogUtil.kr_e('❌ 保存节点选择失败: $e', tag: 'HomeController');
|
||||
@@ -1478,13 +1412,11 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
|
||||
|
||||
// 🔧 保存新节点选择
|
||||
KRLogUtil.kr_i('💾 保存新节点选择: $tag', tag: 'HomeController');
|
||||
await KRSecureStorage()
|
||||
.kr_saveData(key: 'SELECTED_NODE_TAG', value: tag);
|
||||
await KRSecureStorage().kr_saveData(key: 'SELECTED_NODE_TAG', value: tag);
|
||||
|
||||
// 🚀 核心改进:使用 selectOutbound 进行热切换(参考 hiddify-app)
|
||||
// 优势:不重启VPN,保持连接状态,切换瞬间完成,VPN开关不闪烁
|
||||
KRLogUtil.kr_i('🔄 [热切换] 调用 selectOutbound 切换节点...',
|
||||
tag: 'HomeController');
|
||||
KRLogUtil.kr_i('🔄 [热切换] 调用 selectOutbound 切换节点...', tag: 'HomeController');
|
||||
|
||||
// 🔧 关键修复:确定正确的 selector 组 tag
|
||||
// selectOutbound(groupTag, outboundTag) - 第一个参数是组的tag,不是节点的tag
|
||||
@@ -1495,14 +1427,12 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
|
||||
for (var group in activeGroups) {
|
||||
if (group.type == ProxyType.selector) {
|
||||
selectorGroupTag = group.tag;
|
||||
KRLogUtil.kr_i('🔍 找到 selector 组: $selectorGroupTag',
|
||||
tag: 'HomeController');
|
||||
KRLogUtil.kr_i('🔍 找到 selector 组: $selectorGroupTag', tag: 'HomeController');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
KRLogUtil.kr_i('📡 调用 selectOutbound("$selectorGroupTag", "$tag")',
|
||||
tag: 'HomeController');
|
||||
KRLogUtil.kr_i('📡 调用 selectOutbound("$selectorGroupTag", "$tag")', tag: 'HomeController');
|
||||
|
||||
// 调用 sing-box 的 selectOutbound API
|
||||
final result = await KRSingBoxImp.instance.kr_singBox
|
||||
@@ -1511,13 +1441,12 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
|
||||
|
||||
// 处理切换结果
|
||||
result.fold(
|
||||
(error) {
|
||||
(error) {
|
||||
// 切换失败
|
||||
KRLogUtil.kr_e('❌ selectOutbound 调用失败: $error',
|
||||
tag: 'HomeController');
|
||||
KRLogUtil.kr_e('❌ selectOutbound 调用失败: $error', tag: 'HomeController');
|
||||
throw Exception('节点切换失败: $error');
|
||||
},
|
||||
(_) {
|
||||
(_) {
|
||||
// 切换成功
|
||||
KRLogUtil.kr_i('✅ selectOutbound 调用成功', tag: 'HomeController');
|
||||
},
|
||||
@@ -1537,18 +1466,15 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
|
||||
try {
|
||||
final updatedGroups = KRSingBoxImp.instance.kr_activeGroups;
|
||||
final selectGroup = updatedGroups.firstWhere(
|
||||
(group) => group.type == ProxyType.selector,
|
||||
(group) => group.type == ProxyType.selector,
|
||||
orElse: () => throw Exception('未找到 selector 组'),
|
||||
);
|
||||
|
||||
KRLogUtil.kr_i(
|
||||
'📊 [验证] ${selectGroup.tag}组当前选中: ${selectGroup.selected}',
|
||||
tag: 'HomeController');
|
||||
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');
|
||||
KRLogUtil.kr_w('⚠️ [验证] 节点选择验证失败,实际选中: ${selectGroup.selected}', tag: 'HomeController');
|
||||
// 不抛出异常,但记录警告
|
||||
} else {
|
||||
KRLogUtil.kr_i('✅ [验证] 节点选择验证成功!', tag: 'HomeController');
|
||||
@@ -1562,6 +1488,7 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
|
||||
|
||||
KRLogUtil.kr_i('✅ 节点热切换成功,VPN保持连接: $tag', tag: 'HomeController');
|
||||
return true;
|
||||
|
||||
} catch (switchError) {
|
||||
// 后台切换失败,恢复到原节点
|
||||
KRLogUtil.kr_e('❌ 后台节点切换失败: $switchError', tag: 'HomeController');
|
||||
@@ -1573,8 +1500,7 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
|
||||
|
||||
// 恢复原节点选择
|
||||
try {
|
||||
await KRSecureStorage()
|
||||
.kr_saveData(key: 'SELECTED_NODE_TAG', value: originalTag);
|
||||
await KRSecureStorage().kr_saveData(key: 'SELECTED_NODE_TAG', value: originalTag);
|
||||
} catch (e) {
|
||||
KRLogUtil.kr_e('❌ 恢复节点选择失败: $e', tag: 'HomeController');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user