Signed-off-by: lazycat75246 <153997642+lazycat75246@users.noreply.github.com>
This commit is contained in:
lazycat75246 2023-12-21 23:17:20 +08:00
parent 6f864701e0
commit 48f4ecc472
14 changed files with 512 additions and 49 deletions

View file

@ -1,4 +1,4 @@
using v2rayN.Base; using System.Runtime.Intrinsics.X86;
using v2rayN.Mode; using v2rayN.Mode;
namespace v2rayN.Handler namespace v2rayN.Handler
@ -176,6 +176,15 @@ namespace v2rayN.Handler
return SqliteHelper.Instance.Table<ProfileItem>().FirstOrDefault(it => it.indexId == indexId); return SqliteHelper.Instance.Table<ProfileItem>().FirstOrDefault(it => it.indexId == indexId);
} }
public ProfileItem? GetProfileItemRemarks(string Remarks)
{
if (Utils.IsNullOrEmpty(Remarks))
{
return null;
}
return SqliteHelper.Instance.Table<ProfileItem>().FirstOrDefault(it => it.remarks == Remarks);
}
public ProfileItem? GetProfileItemViaRemarks(string remarks) public ProfileItem? GetProfileItemViaRemarks(string remarks)
{ {
if (Utils.IsNullOrEmpty(remarks)) if (Utils.IsNullOrEmpty(remarks))

View file

@ -1,16 +1,21 @@
using DynamicData; using DynamicData;
using Splat;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows;
using System.Windows.Interop; using System.Windows.Interop;
using v2rayN.Mode; using v2rayN.Mode;
using v2rayN.Resx;
using v2rayN.ViewModels;
namespace v2rayN.Handler namespace v2rayN.Handler
{ {
public delegate void SetDefaultServerDelegate(string s); public delegate void SetDefaultServerDelegate(string s);
public delegate void SetTestResultDelegate(string i, string d, string s);
public class TestResultItem public class TestResultItem
{ {
public string indexId { get; set; } public string indexId { get; set; }
@ -24,6 +29,8 @@ namespace v2rayN.Handler
private NoticeHandler? _noticeHandler; private NoticeHandler? _noticeHandler;
private Task taskmain = null; private Task taskmain = null;
private SetDefaultServerDelegate setDefaultServerDelegates; private SetDefaultServerDelegate setDefaultServerDelegates;
private SetTestResultDelegate setTestResultDelegates;
private SetAutoSwitchTogDelegate _setAutoSwitchTog;
public ServerAutoSwitch() public ServerAutoSwitch()
{ {
@ -36,12 +43,20 @@ namespace v2rayN.Handler
{ {
bStop = true; bStop = true;
if(taskmain!=null) if(taskmain!=null)
taskmain.Wait(); taskmain.Wait();
taskmain = null; taskmain = null;
} }
public void SetDelegate(SetDefaultServerDelegate s = null) { public void SetDelegate(SetDefaultServerDelegate s = null) {
this.setDefaultServerDelegates = s; this.setDefaultServerDelegates = s;
} }
public void SetDelegate(SetTestResultDelegate s = null)
{
this.setTestResultDelegates = s;
}
public void SetDelegate(SetAutoSwitchTogDelegate s = null)
{
this._setAutoSwitchTog = s;
}
private void UpdateHandler(bool notify, string msg) private void UpdateHandler(bool notify, string msg)
{ {
} }
@ -62,6 +77,7 @@ namespace v2rayN.Handler
indexId = id, indexId = id,
latency = i latency = i
}); });
setTestResultDelegates(id, dl, speed);
} }
} }
@ -77,56 +93,114 @@ namespace v2rayN.Handler
taskmain = Task.Run(() => taskmain = Task.Run(() =>
{ {
int iFailTimeMax = 30; _noticeHandler = Locator.Current.GetService<NoticeHandler>();
int iTestInterval = iFailTimeMax / 3;
DownloadHandle downloadHandle = new(); DownloadHandle downloadHandle = new();
long failtimestart = 0; long failtimestart = 0;
int LastServerSelectMode = -1;
List<ProfileItem> listprofile = new List<ProfileItem>(); List<ProfileItem> listprofile = new List<ProfileItem>();
while (!bStop) while (!bStop)
{ {
var _config = LazyConfig.Instance.GetConfig();
int iFailTimeMax = LazyConfig.Instance.GetConfig().autoSwitchItem.FailTimeMax;
int iTestInterval = iFailTimeMax / 3;
int ServerSelectMode = LazyConfig.Instance.GetConfig().autoSwitchItem.ServerSelectMode;
int AutoSwitchMode = LazyConfig.Instance.GetConfig().autoSwitchItem.mode;
Thread.Sleep(iTestInterval * 1000); Thread.Sleep(iTestInterval * 1000);
int res = downloadHandle.RunAvailabilityCheck(null).Result; int res = downloadHandle.RunAvailabilityCheck(null).Result;
if (!bStop)
Task.Run(() => setTestResultDelegates(_config.indexId, res.ToString(), ""));
else
break;
if (res <= 0) if (res <= 0)
{ {
_noticeHandler?.SendMessage("Current server test failed!", true); _noticeHandler?.SendMessage("Current server test failed!",true);
if (failtimestart == 0) if (failtimestart == 0)
failtimestart = GetTimestamp(DateTime.Now); failtimestart = GetTimestamp(DateTime.Now);
if (GetTimestamp(DateTime.Now) - failtimestart >= iFailTimeMax) if (GetTimestamp(DateTime.Now) - failtimestart >= iFailTimeMax)
{ {
if (testResultItems.Count == 0 || listprofile.Count == 0) if (testResultItems.Count == 0 || listprofile.Count == 0 || ServerSelectMode!= LastServerSelectMode)
listprofile = LazyConfig.Instance.ProfileItemsAutoSwitch();
if (listprofile.Count > 0)
{ {
var _config = LazyConfig.Instance.GetConfig(); testResultItems.Clear();
listprofile = LazyConfig.Instance.ProfileItemsAutoSwitch();
}
if (listprofile.Count >= 2)
{
if (testResultItems.Count == 0) if (testResultItems.Count == 0)
{ {
var _coreHandler = new CoreHandler(_config, (bool x, string y) => { }); var _coreHandler = new CoreHandler(_config, (bool x, string y) => { });
new SpeedtestHandler(_config, _coreHandler, listprofile, Mode.ESpeedActionType.Tcping, UpdateSpeedtestHandler);
while (testResultItems.Count < listprofile.Count) if (ServerSelectMode == 0 || ServerSelectMode == 1)
new SpeedtestHandler(_config, _coreHandler, listprofile, Mode.ESpeedActionType.Tcping, UpdateSpeedtestHandler);
else if (ServerSelectMode == 2)
new SpeedtestHandler(_config, _coreHandler, listprofile, Mode.ESpeedActionType.Realping, UpdateSpeedtestHandler);
if (bStop) break;
while (!bStop && testResultItems.Count < listprofile.Count)
{ {
Thread.Sleep(20); Thread.Sleep(20);
} }
} if (bStop) break;
if (ServerSelectMode == 0)
{
List<TestResultItem> templist = new List<TestResultItem>();
for(int i=testResultItems.Count-1; i>=0; i--) { foreach (var item in listprofile)
if (testResultItems[i].latency <= 0 || testResultItems[i].indexId == _config.indexId) {
testResultItems.RemoveAt(i); var item2 = testResultItems.Find(x => x.indexId == item.indexId);
if (item2 != null)
templist.Add(item2);
}
testResultItems = templist;
}
} }
if (ServerSelectMode == 0)
if (testResultItems.Count > 0)
{ {
testResultItems.Sort((x, y) => x.latency.CompareTo(y.latency)); for (int i = testResultItems.Count - 1; i >= 0; i--)
setDefaultServerDelegates(testResultItems[0].indexId); {
//if (ConfigHandler.SetDefaultServerIndex(ref _config, testResultItems[0].indexId) == 0) if (testResultItems[i].latency <= 0 && testResultItems[i].indexId != _config.indexId)
//{ testResultItems.RemoveAt(i);
// RefreshServers(); }
// Reload(); if (testResultItems.Count > 1)
//} {
int j = testResultItems.FindIndex(x => x.indexId == _config.indexId);
testResultItems.RemoveAt(0); int k = j + 1;
if (k > testResultItems.Count - 1)
k = 0;
setDefaultServerDelegates(testResultItems[k].indexId);
testResultItems.RemoveAt(j);
if (testResultItems.Count <= 1)
testResultItems.Clear();
}
else
testResultItems.Clear();
} }
else if (ServerSelectMode == 1 || ServerSelectMode == 2)
{
for (int i = testResultItems.Count - 1; i >= 0; i--)
{
if (testResultItems[i].latency <= 0 || testResultItems[i].indexId == _config.indexId)
testResultItems.RemoveAt(i);
}
if (testResultItems.Count > 0)
{
testResultItems.Sort((x, y) => x.latency.CompareTo(y.latency));
setDefaultServerDelegates(testResultItems[0].indexId);
testResultItems.RemoveAt(0);
}
}
} }
else
{
_setAutoSwitchTog(false);
_config.autoSwitchItem.EnableAutoSwitch = false;
MessageBox.Show(ResUI.stopSwtichWarn);
}
} }
} }
else else
@ -135,7 +209,7 @@ namespace v2rayN.Handler
testResultItems.Clear(); testResultItems.Clear();
listprofile.Clear(); listprofile.Clear();
} }
LastServerSelectMode = ServerSelectMode;
} }
}); });
} }

View file

@ -167,6 +167,7 @@ namespace v2rayN.Handler
Task.Run(async () => Task.Run(async () =>
{ {
var listprofile = LazyConfig.Instance.ProfileItemsAutoSwitch();
foreach (var item in subItem) foreach (var item in subItem)
{ {
string id = item.id.TrimEx(); string id = item.id.TrimEx();
@ -275,6 +276,16 @@ namespace v2rayN.Handler
Logging.SaveLog("FailedImportSubscription"); Logging.SaveLog("FailedImportSubscription");
Logging.SaveLog(result); Logging.SaveLog(result);
} }
else
{
foreach (var item3 in listprofile)
{
var item2 = LazyConfig.Instance.GetProfileItemRemarks(item3.remarks);
item2.autoSwitch = true;
SqliteHelper.Instance.Update(item2);
}
ConfigHandler.SaveConfig(_config);
}
_updateFunc(false, _updateFunc(false,
ret > 0 ret > 0
? $"{hashCode}{ResUI.MsgUpdateSubscriptionEnd}" ? $"{hashCode}{ResUI.MsgUpdateSubscriptionEnd}"
@ -284,6 +295,8 @@ namespace v2rayN.Handler
} }
_updateFunc(true, $"{ResUI.MsgUpdateSubscriptionEnd}"); _updateFunc(true, $"{ResUI.MsgUpdateSubscriptionEnd}");
}); });
} }

View file

@ -1815,6 +1815,15 @@ namespace v2rayN.Resx {
} }
} }
/// <summary>
/// 查找类似 Because the number of selected switching servers is less than the minimum value of 2, stop automatic switching! 的本地化字符串。
/// </summary>
public static string stopSwtichWarn {
get {
return ResourceManager.GetString("stopSwtichWarn", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Group please leave blank here 的本地化字符串。 /// 查找类似 Group please leave blank here 的本地化字符串。
/// </summary> /// </summary>
@ -2770,6 +2779,24 @@ namespace v2rayN.Resx {
} }
} }
/// <summary>
/// 查找类似 Lowest RealPing Latency Server 的本地化字符串。
/// </summary>
public static string TbSettingsLowestRealPingServer {
get {
return ResourceManager.GetString("TbSettingsLowestRealPingServer", resourceCulture);
}
}
/// <summary>
/// 查找类似 Lowest TCPing Latency Server 的本地化字符串。
/// </summary>
public static string TbSettingsLowestTCPingServer {
get {
return ResourceManager.GetString("TbSettingsLowestTCPingServer", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 sing-box Mux Protocol 的本地化字符串。 /// 查找类似 sing-box Mux Protocol 的本地化字符串。
/// </summary> /// </summary>
@ -2806,6 +2833,15 @@ namespace v2rayN.Resx {
} }
} }
/// <summary>
/// 查找类似 Next Server 的本地化字符串。
/// </summary>
public static string TbSettingsNextServer {
get {
return ResourceManager.GetString("TbSettingsNextServer", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Auth pass 的本地化字符串。 /// 查找类似 Auth pass 的本地化字符串。
/// </summary> /// </summary>
@ -2833,6 +2869,15 @@ namespace v2rayN.Resx {
} }
} }
/// <summary>
/// 查找类似 Switch when server cannot connect 的本地化字符串。
/// </summary>
public static string TbSettingsServerFail {
get {
return ResourceManager.GetString("TbSettingsServerFail", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Switch to server 的本地化字符串。 /// 查找类似 Switch to server 的本地化字符串。
/// </summary> /// </summary>
@ -3292,6 +3337,15 @@ namespace v2rayN.Resx {
} }
} }
/// <summary>
/// 查找类似 The number of selected switching servers must be greater than or equal to 2 to start switching! 的本地化字符串。
/// </summary>
public static string turnOnSwitchWarn {
get {
return ResourceManager.GetString("turnOnSwitchWarn", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Ungrouped 的本地化字符串。 /// 查找类似 Ungrouped 的本地化字符串。
/// </summary> /// </summary>

View file

@ -1162,4 +1162,22 @@
<data name="TbSettingsUseSystemHosts" xml:space="preserve"> <data name="TbSettingsUseSystemHosts" xml:space="preserve">
<value>از هاست های سیستم استفاده کنید</value> <value>از هاست های سیستم استفاده کنید</value>
</data> </data>
<data name="TbSettingsNextServer" xml:space="preserve">
<value>سرور بعدی</value>
</data>
<data name="TbSettingsLowestTCPingServer" xml:space="preserve">
<value>سرور با کمترین تاخیر TCPing</value>
</data>
<data name="TbSettingsLowestRealPingServer" xml:space="preserve">
<value>کمترین سرور RealPing Latency</value>
</data>
<data name="TbSettingsServerFail" xml:space="preserve">
<value>هنگامی که سرور نمی تواند متصل شود، تغییر دهید</value>
</data>
<data name="stopSwtichWarn" xml:space="preserve">
<value>از آنجایی که تعداد سرورهای سوئیچینگ انتخابی کمتر از مقدار حداقل 2 است، تعویض خودکار را متوقف کنید!</value>
</data>
<data name="turnOnSwitchWarn" xml:space="preserve">
<value>برای شروع سوئیچینگ، تعداد سرورهای سوئیچینگ انتخابی باید بیشتر یا مساوی 2 باشد!</value>
</data>
</root> </root>

View file

@ -205,7 +205,7 @@
<value>Type</value> <value>Type</value>
</data> </data>
<data name="LvAutoSwitch" xml:space="preserve"> <data name="LvAutoSwitch" xml:space="preserve">
<value>AutoSwitch</value> <value>AutoSwitch</value>
</data> </data>
<data name="LvSubscription" xml:space="preserve"> <data name="LvSubscription" xml:space="preserve">
<value>Subs group</value> <value>Subs group</value>
@ -1198,4 +1198,22 @@
<data name="TbLocalAddress" xml:space="preserve"> <data name="TbLocalAddress" xml:space="preserve">
<value>Address(Ip,Ipv6)</value> <value>Address(Ip,Ipv6)</value>
</data> </data>
<data name="TbSettingsNextServer" xml:space="preserve">
<value>Next Server</value>
</data>
<data name="TbSettingsLowestTCPingServer" xml:space="preserve">
<value>Lowest TCPing Latency Server</value>
</data>
<data name="TbSettingsLowestRealPingServer" xml:space="preserve">
<value>Lowest RealPing Latency Server</value>
</data>
<data name="TbSettingsServerFail" xml:space="preserve">
<value>Switch when server cannot connect</value>
</data>
<data name="stopSwtichWarn" xml:space="preserve">
<value>Because the number of selected switching servers is less than the minimum value of 2, stop automatic switching!</value>
</data>
<data name="turnOnSwitchWarn" xml:space="preserve">
<value>The number of selected switching servers must be greater than or equal to 2 to start switching!</value>
</data>
</root> </root>

View file

@ -1162,4 +1162,22 @@
<data name="TbSettingsUseSystemHosts" xml:space="preserve"> <data name="TbSettingsUseSystemHosts" xml:space="preserve">
<value>Используйте системные хосты</value> <value>Используйте системные хосты</value>
</data> </data>
<data name="TbSettingsNextServer" xml:space="preserve">
<value>Следующий сервер</value>
</data>
<data name="TbSettingsLowestTCPingServer" xml:space="preserve">
<value>Сервер с наименьшей задержкой TCPing</value>
</data>
<data name="TbSettingsLowestRealPingServer" xml:space="preserve">
<value>Сервер с самой низкой задержкой RealPing</value>
</data>
<data name="TbSettingsServerFail" xml:space="preserve">
<value>Переключиться, когда сервер не может подключиться</value>
</data>
<data name="stopSwtichWarn" xml:space="preserve">
<value>Поскольку количество выбранных коммутационных серверов меньше минимального значения 2, остановите автоматическое переключение!</value>
</data>
<data name="turnOnSwitchWarn" xml:space="preserve">
<value>Для начала переключения количество выбранных коммутационных серверов должно быть больше или равно 2!</value>
</data>
</root> </root>

View file

@ -1195,4 +1195,22 @@
<data name="TbLocalAddress" xml:space="preserve"> <data name="TbLocalAddress" xml:space="preserve">
<value>Address(Ip,Ipv6)</value> <value>Address(Ip,Ipv6)</value>
</data> </data>
<data name="TbSettingsNextServer" xml:space="preserve">
<value>下一个服务器</value>
</data>
<data name="TbSettingsLowestTCPingServer" xml:space="preserve">
<value>最低TCPing延迟服务器</value>
</data>
<data name="TbSettingsLowestRealPingServer" xml:space="preserve">
<value>最低真连接延迟服务器</value>
</data>
<data name="TbSettingsServerFail" xml:space="preserve">
<value>服务器无法连接时切换</value>
</data>
<data name="stopSwtichWarn" xml:space="preserve">
<value>因为选择的切换服务器数量小于最低值2,停止自动切换!</value>
</data>
<data name="turnOnSwitchWarn" xml:space="preserve">
<value>选择的切换服务器数量必须大于等于2才能启动切换!</value>
</data>
</root> </root>

View file

@ -1136,7 +1136,7 @@
<value>IP 或 IP CIDR</value> <value>IP 或 IP CIDR</value>
</data> </data>
<data name="LvAutoSwitch" xml:space="preserve"> <data name="LvAutoSwitch" xml:space="preserve">
<value>自動切換</value> <value>切換</value>
</data> </data>
<data name="TbSettingsAutoSwitch" xml:space="preserve"> <data name="TbSettingsAutoSwitch" xml:space="preserve">
<value>自動切換設定</value> <value>自動切換設定</value>
@ -1180,4 +1180,22 @@
<data name="TbSettingsEnableIPv6Address" xml:space="preserve"> <data name="TbSettingsEnableIPv6Address" xml:space="preserve">
<value>啟用IPv6</value> <value>啟用IPv6</value>
</data> </data>
<data name="TbSettingsNextServer" xml:space="preserve">
<value>下一个服务器</value>
</data>
<data name="TbSettingsLowestTCPingServer" xml:space="preserve">
<value>TCPing 延遲最低的伺服器</value>
</data>
<data name="TbSettingsLowestRealPingServer" xml:space="preserve">
<value>最低 RealPing 延遲伺服器</value>
</data>
<data name="TbSettingsServerFail" xml:space="preserve">
<value>伺服器無法連接時切換</value>
</data>
<data name="stopSwtichWarn" xml:space="preserve">
<value>由於選擇的切換伺服器數量小於最小值2因此停止自動切換</value>
</data>
<data name="turnOnSwitchWarn" xml:space="preserve">
<value>選定的切換伺服器數量必須大於或等於2才能開始切換</value>
</data>
</root> </root>

View file

@ -13,6 +13,7 @@ using System.Reactive;
using System.Reactive.Linq; using System.Reactive.Linq;
using System.Text; using System.Text;
using System.Windows; using System.Windows;
using System.Windows.Controls;
using System.Windows.Media; using System.Windows.Media;
using v2rayN.Handler; using v2rayN.Handler;
using v2rayN.Mode; using v2rayN.Mode;
@ -22,6 +23,7 @@ using v2rayN.Views;
namespace v2rayN.ViewModels namespace v2rayN.ViewModels
{ {
public delegate void SetAutoSwitchTogDelegate(bool b); public delegate void SetAutoSwitchTogDelegate(bool b);
public delegate void SetAutoSwitchAllDelegate(bool b);
public class MainWindowViewModel : ReactiveObject public class MainWindowViewModel : ReactiveObject
{ {
#region private prop #region private prop
@ -37,10 +39,15 @@ namespace v2rayN.ViewModels
private Dictionary<string, bool> _dicHeaderSort = new(); private Dictionary<string, bool> _dicHeaderSort = new();
private Action<EViewAction> _updateView; private Action<EViewAction> _updateView;
private SetAutoSwitchTogDelegate _setAutoSwitchTog; private SetAutoSwitchTogDelegate _setAutoSwitchTog;
private SetAutoSwitchAllDelegate _setAutoSwitchAll;
public void SetDelegate(SetAutoSwitchTogDelegate s = null) public void SetDelegate(SetAutoSwitchTogDelegate s = null)
{ {
this._setAutoSwitchTog = s; this._setAutoSwitchTog = s;
} }
public void SetDelegate2(SetAutoSwitchAllDelegate s = null)
{
this._setAutoSwitchAll = s;
}
#endregion private prop #endregion private prop
#region ObservableCollection #region ObservableCollection
@ -254,9 +261,12 @@ namespace v2rayN.ViewModels
[Reactive] [Reactive]
public string CurrentLanguage { get; set; } public string CurrentLanguage { get; set; }
[Reactive]
public bool autoSwitchCheckAll { get; set; }
#endregion UI #endregion UI
public ServerAutoSwitch ServerAutoSwitchs= new ServerAutoSwitch(); public ServerAutoSwitch ServerAutoSwitchs= new ServerAutoSwitch();
@ -605,7 +615,9 @@ namespace v2rayN.ViewModels
Reload(); Reload();
ChangeSystemProxyStatus(_config.sysProxyType, true); ChangeSystemProxyStatus(_config.sysProxyType, true);
ServerAutoSwitchs.SetDelegate(SetDefaultServer); ServerAutoSwitchs.SetDelegate(SetDefaultServer);
if(_config.autoSwitchItem.EnableAutoSwitch) ServerAutoSwitchs.SetDelegate(UpdateSpeedtestHandler);
ServerAutoSwitchs.SetDelegate(_setAutoSwitchTog);
if (_config.autoSwitchItem.EnableAutoSwitch)
ServerAutoSwitchs.Start(); ServerAutoSwitchs.Start();
} }
@ -614,7 +626,7 @@ namespace v2rayN.ViewModels
Application.Current.Dispatcher.Invoke((Action)(() => Application.Current.Dispatcher.Invoke((Action)(() =>
{ {
ShowHideWindow(true); ShowHideWindow(true);
})); }));
} }
#endregion Init #endregion Init
@ -757,9 +769,9 @@ namespace v2rayN.ViewModels
{ {
Logging.SaveLog("MyAppExit Begin"); Logging.SaveLog("MyAppExit Begin");
StorageUI(); StorageUI();
ConfigHandler.SaveConfig(_config); ConfigHandler.SaveConfig(_config);
ServerAutoSwitchs.Stop();
//HttpProxyHandle.CloseHttpAgent(config); //HttpProxyHandle.CloseHttpAgent(config);
if (blWindowsShutDown) if (blWindowsShutDown)
{ {
@ -891,6 +903,16 @@ namespace v2rayN.ViewModels
RunningServerDisplay = ResUI.CheckServerSettings; RunningServerDisplay = ResUI.CheckServerSettings;
RunningServerToolTipText = ResUI.CheckServerSettings; RunningServerToolTipText = ResUI.CheckServerSettings;
} }
int autoswitchservercount = 0;
foreach (var item in lstModel)
{
if(item.autoSwitch)
autoswitchservercount++;
}
if(_setAutoSwitchAll!=null)
_setAutoSwitchAll( lstModel.Count>0 && autoswitchservercount == lstModel.Count);
})); }));
} }
@ -1668,11 +1690,11 @@ namespace v2rayN.ViewModels
var profiles = LazyConfig.Instance.ProfileItemsAutoSwitch(); var profiles = LazyConfig.Instance.ProfileItemsAutoSwitch();
if (profiles.Count < 2) if (profiles.Count < 2)
{ {
MessageBox.Show("选择的切换服务器必须大于等于2才能启动切换!"); MessageBox.Show(ResUI.turnOnSwitchWarn);
_setAutoSwitchTog(false); _setAutoSwitchTog(false);
_config.autoSwitchItem.EnableAutoSwitch = false; _config.autoSwitchItem.EnableAutoSwitch = false;
EnableAutoSwitch = false; EnableAutoSwitch = false;
ConfigHandler.SaveConfig(ref _config); ConfigHandler.SaveConfig(_config);
return; return;
} }
ServerAutoSwitchs.Start(); ServerAutoSwitchs.Start();
@ -1684,7 +1706,7 @@ namespace v2rayN.ViewModels
ServerAutoSwitchs.Stop(); ServerAutoSwitchs.Stop();
}); });
} }
ConfigHandler.SaveConfig(ref _config); ConfigHandler.SaveConfig(_config);
} }
} }
@ -1899,18 +1921,18 @@ namespace v2rayN.ViewModels
private void AutoHideStartup() private void AutoHideStartup()
{ {
if (_config.uiItem.autoHideStartup)
{
Observable.Range(1, 1) Observable.Range(1, 1)
.Delay(TimeSpan.FromSeconds(0.5)) .Delay(TimeSpan.FromSeconds(0.5))
.Subscribe(x => .Subscribe(x =>
{ {
Application.Current.Dispatcher.Invoke(() => Application.Current.Dispatcher.Invoke(() =>
{ {
ShowHideWindow(false); ShowHideWindow(!_config.uiItem.autoHideStartup);
RefreshServers();
}); });
}); });
}
} }
#endregion UI #endregion UI

View file

@ -99,6 +99,10 @@ namespace v2rayN.ViewModels
#endregion CoreType #endregion CoreType
[Reactive] public int ServerSelectMode { get; set; }
[Reactive] public int AutoSwitchMode { get; set; }
[Reactive] public int FailTimeMax { get; set; }
public ReactiveCommand<Unit, Unit> SaveCmd { get; } public ReactiveCommand<Unit, Unit> SaveCmd { get; }
public OptionSettingViewModel(Window view) public OptionSettingViewModel(Window view)
@ -181,6 +185,10 @@ namespace v2rayN.ViewModels
#endregion Tun mode #endregion Tun mode
FailTimeMax = _config.autoSwitchItem.FailTimeMax;
AutoSwitchMode = _config.autoSwitchItem.mode;
ServerSelectMode = _config.autoSwitchItem.ServerSelectMode;
InitCoreType(); InitCoreType();
SaveCmd = ReactiveCommand.Create(() => SaveCmd = ReactiveCommand.Create(() =>
@ -326,6 +334,10 @@ namespace v2rayN.ViewModels
_config.tunModeItem.enableExInbound = TunEnableExInbound; _config.tunModeItem.enableExInbound = TunEnableExInbound;
_config.tunModeItem.enableIPv6Address = TunEnableIPv6Address; _config.tunModeItem.enableIPv6Address = TunEnableIPv6Address;
_config.autoSwitchItem.mode = AutoSwitchMode;
_config.autoSwitchItem.ServerSelectMode=ServerSelectMode;
_config.autoSwitchItem.FailTimeMax = FailTimeMax;
//coreType //coreType
SaveCoreType(); SaveCoreType();

View file

@ -674,10 +674,16 @@
</DataGrid.Resources> </DataGrid.Resources>
<DataGrid.Columns> <DataGrid.Columns>
<base:MyDGBoolColumn <base:MyDGBoolColumn
x:Name="autoSwitchCheckCol"
Width="Auto" Width="Auto"
Binding="{Binding autoSwitch, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Binding="{Binding autoSwitch, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ExName="autoSwitch" ExName="autoSwitch"
Header="{x:Static resx:ResUI.LvAutoSwitch}"> Header="{x:Static resx:ResUI.LvAutoSwitch}">
<DataGridCheckBoxColumn.HeaderTemplate>
<DataTemplate>
<CheckBox Name="autoSwitchCheckAll" Content="{x:Static resx:ResUI.LvAutoSwitch}" Click="AutoSwitchAll_Click"/>
</DataTemplate>
</DataGridCheckBoxColumn.HeaderTemplate>
<DataGridCheckBoxColumn.ElementStyle> <DataGridCheckBoxColumn.ElementStyle>
<Style TargetType="CheckBox"> <Style TargetType="CheckBox">
<Setter Property= "HorizontalAlignment" Value= "Center" /> <Setter Property= "HorizontalAlignment" Value= "Center" />

View file

@ -1,6 +1,7 @@
using ReactiveUI; using ReactiveUI;
using Splat; using Splat;
using System.ComponentModel; using System.ComponentModel;
using System.Globalization;
using System.Reactive.Disposables; using System.Reactive.Disposables;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Windows; using System.Windows;
@ -61,6 +62,12 @@ namespace v2rayN.Views
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(MainWindowViewModel)); Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(MainWindowViewModel));
ViewModel.SetDelegate((bool b)=> togEnableAutoSwitch.IsChecked = b); ViewModel.SetDelegate((bool b)=> togEnableAutoSwitch.IsChecked = b);
ViewModel.SetDelegate2((bool b) =>
{
var autoswitchcheckbox = FindVisualChild<CheckBox>(lstProfiles, "autoSwitchCheckAll");
if(autoswitchcheckbox != null)
autoswitchcheckbox.IsChecked = b;
});
for (int i = Global.MinFontSize; i <= Global.MinFontSize + 8; i++) for (int i = Global.MinFontSize; i <= Global.MinFontSize + 8; i++)
{ {
@ -208,8 +215,9 @@ namespace v2rayN.Views
this.Bind(ViewModel, vm => vm.SelectedSwatch, v => v.cmbSwatches.SelectedItem).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSwatch, v => v.cmbSwatches.SelectedItem).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CurrentFontSize, v => v.cmbCurrentFontSize.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.CurrentFontSize, v => v.cmbCurrentFontSize.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CurrentLanguage, v => v.cmbCurrentLanguage.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.CurrentLanguage, v => v.cmbCurrentLanguage.Text).DisposeWith(disposables);
}); });
RestoreUI(); RestoreUI();
AddHelpMenuItem(); AddHelpMenuItem();
@ -312,9 +320,10 @@ namespace v2rayN.Views
{ {
return; return;
} }
var colHeader2 = colHeader.Column as MyDGTextColumn;
var colName = ((MyDGTextColumn)colHeader.Column).ExName; var colName = colHeader2==null ? "" : colHeader2.ExName;
ViewModel?.SortServer(colName); if(colName != "")
ViewModel?.SortServer(colName);
} }
private void menuSelectAll_Click(object sender, RoutedEventArgs e) private void menuSelectAll_Click(object sender, RoutedEventArgs e)
@ -481,13 +490,27 @@ namespace v2rayN.Views
} }
var lvColumnItem = _config.uiItem.mainColumnItem.OrderBy(t => t.Index).ToList(); var lvColumnItem = _config.uiItem.mainColumnItem.OrderBy(t => t.Index).ToList();
if(lvColumnItem.FindIndex(t=>t.Name== "autoSwitch")==-1)
{
lvColumnItem.Insert(0,new()
{
Name = "autoSwitch",
Width = 79,
Index = 0
});
for(var p=0;p<lvColumnItem.Count;p++)
{
lvColumnItem[p].Index = p;
}
}
for (int i = 0; i < lvColumnItem.Count; i++) for (int i = 0; i < lvColumnItem.Count; i++)
{ {
var item = lvColumnItem[i]; var item = lvColumnItem[i];
for (int k = 0; k < lstProfiles.Columns.Count; k++) for (int k = 0; k < lstProfiles.Columns.Count; k++)
{ {
var item2 = (MyDGTextColumn)lstProfiles.Columns[k]; var item2 = lstProfiles.Columns[k] as MyDGTextColumn;
if (item2.ExName == item.Name)
if(item2 != null && item2.ExName == item.Name)
{ {
if (item.Width < 0) if (item.Width < 0)
{ {
@ -499,6 +522,23 @@ namespace v2rayN.Views
item2.DisplayIndex = i; item2.DisplayIndex = i;
} }
} }
else
{
var item3 = lstProfiles.Columns[k] as MyDGBoolColumn;
if (item3 != null && item3.ExName == item.Name)
{
if (item.Width < 0)
{
item3.Visibility = Visibility.Hidden;
}
else
{
item3.Width = item.Width;
item3.DisplayIndex = i;
}
}
}
} }
} }
@ -526,13 +566,25 @@ namespace v2rayN.Views
List<ColumnItem> lvColumnItem = new(); List<ColumnItem> lvColumnItem = new();
for (int k = 0; k < lstProfiles.Columns.Count; k++) for (int k = 0; k < lstProfiles.Columns.Count; k++)
{ {
var item2 = (MyDGTextColumn)lstProfiles.Columns[k]; var item2 = lstProfiles.Columns[k] as MyDGTextColumn;
if(item2!=null)
lvColumnItem.Add(new() lvColumnItem.Add(new()
{ {
Name = item2.ExName, Name = item2.ExName,
Width = item2.Visibility == Visibility.Visible ? Convert.ToInt32(item2.ActualWidth) : -1, Width = item2.Visibility == Visibility.Visible ? Convert.ToInt32(item2.ActualWidth) : -1,
Index = item2.DisplayIndex Index = item2.DisplayIndex
}); });
else
{
var item3 = lstProfiles.Columns[k] as MyDGBoolColumn;
if (item3 != null)
lvColumnItem.Add(new()
{
Name = item3.ExName,
Width = item3.Visibility == Visibility.Visible ? Convert.ToInt32(item3.ActualWidth) : -1,
Index = item3.DisplayIndex
});
}
} }
_config.uiItem.mainColumnItem = lvColumnItem; _config.uiItem.mainColumnItem = lvColumnItem;
@ -566,12 +618,108 @@ namespace v2rayN.Views
Utils.ProcessStart(item.Tag.ToString()); Utils.ProcessStart(item.Tag.ToString());
} }
} }
private void AutoSwitchAll_Click(object sender, RoutedEventArgs e)
{
CheckBox cb = sender as CheckBox;
foreach (var item in ViewModel.ProfileItems)
{
item.autoSwitch = cb.IsChecked ?? item.autoSwitch;
var item2 = LazyConfig.Instance.GetProfileItem(item.indexId);
if(item2.autoSwitch != item.autoSwitch)
{
item2.autoSwitch = item.autoSwitch;
SqliteHelper.Instance.Update(item2);
}
}
ViewModel.RefreshServers();
var profiles = LazyConfig.Instance.ProfileItemsAutoSwitch();
if (profiles.Count < 2 && _config.autoSwitchItem.EnableAutoSwitch)
{
togEnableAutoSwitch.IsChecked = false;
_config.autoSwitchItem.EnableAutoSwitch = false;
MessageBox.Show(ResUI.stopSwtichWarn);
}
}
private T FindVisualChild<T>(DependencyObject obj,string name = null)
where T : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
var currChildElement = child as FrameworkElement;
if (child != null && child is T)
{
if(string.IsNullOrEmpty(name) == false && currChildElement.Name == name)
return (T)child;
}
else
{
T childOfChild = FindVisualChild<T>(child,name);
if (childOfChild != null)
return childOfChild;
}
}
return null;
}
public static T GetVisualChild<T>(DependencyObject parent,
string name = null) where T : DependencyObject
{
T result;
int count;
DependencyObject currChild;
FrameworkElement currChildElement;
count = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < count; i++)
{
currChild = VisualTreeHelper.GetChild(parent, i);
currChildElement = currChild as FrameworkElement;
if (currChild is T)
{
if (string.IsNullOrEmpty(name) == false)
{
// A name was given so the child must match it
if ((currChildElement != null) &&
(currChildElement.Name == name))
{
return ((T)currChild);
}
}
else
{
// No name was given but the type matches
return ((T)currChild);
}
}
// Recursively search children
result = GetVisualChild<T>(currChild, name);
if (result != null)
{
return (result);
}
}
return (null);
}
private void AutoSwitch_Click(object sender, RoutedEventArgs e) private void AutoSwitch_Click(object sender, RoutedEventArgs e)
{ {
//CheckBox cb = sender as CheckBox; //CheckBox cb = sender as CheckBox;
var item = LazyConfig.Instance.GetProfileItem(ViewModel.SelectedProfile.indexId); var item = LazyConfig.Instance.GetProfileItem(ViewModel.SelectedProfile.indexId);
item.autoSwitch = ViewModel.SelectedProfile.autoSwitch; item.autoSwitch = ViewModel.SelectedProfile.autoSwitch;
SqliteHelper.Instance.Update(item); SqliteHelper.Instance.Update(item);
if(!ViewModel.SelectedProfile.autoSwitch)
{
var profiles = LazyConfig.Instance.ProfileItemsAutoSwitch();
if(profiles.Count< 2 && _config.autoSwitchItem.EnableAutoSwitch)
{
togEnableAutoSwitch.IsChecked = false;
_config.autoSwitchItem.EnableAutoSwitch = false;
MessageBox.Show(ResUI.stopSwtichWarn);
}
}
//if(ViewModel.EnableAutoSwitch) //if(ViewModel.EnableAutoSwitch)
//{ //{
// ViewModel.ServerAutoSwitchs.Start(); // ViewModel.ServerAutoSwitchs.Start();

View file

@ -2,7 +2,9 @@
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Reactive.Disposables; using System.Reactive.Disposables;
using System.Timers;
using System.Windows; using System.Windows;
using System.Windows.Controls;
using System.Windows.Media; using System.Windows.Media;
using v2rayN.Handler; using v2rayN.Handler;
using v2rayN.Mode; using v2rayN.Mode;
@ -85,6 +87,11 @@ namespace v2rayN.Views
cmbSubConvertUrl.Items.Add(it); cmbSubConvertUrl.Items.Add(it);
}); });
cmbServerSelectMode.Items.Add(Resx.ResUI.TbSettingsNextServer);
cmbServerSelectMode.Items.Add(Resx.ResUI.TbSettingsLowestTCPingServer);
cmbServerSelectMode.Items.Add(Resx.ResUI.TbSettingsLowestRealPingServer);
cmbAutoSwitchMainMode.Items.Add(Resx.ResUI.TbSettingsServerFail);
//fill fonts //fill fonts
try try
{ {
@ -195,10 +202,38 @@ namespace v2rayN.Views
this.Bind(ViewModel, vm => vm.CoreType5, v => v.cmbCoreType5.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.CoreType5, v => v.cmbCoreType5.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType6, v => v.cmbCoreType6.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.CoreType6, v => v.cmbCoreType6.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.AutoSwitchMode, v => v.cmbAutoSwitchMainMode.SelectedIndex).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.ServerSelectMode, v => v.cmbServerSelectMode.SelectedIndex).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.FailTimeMax, v => v.txtFailTimeMax.Text).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
}); });
}
txtFailTimeMax.TextChanged += (s, e) =>
{
txtFailTimeMax_OnTextChanged(s, e);
};
}
private async void txtFailTimeMax_OnTextChanged(object sender, TextChangedEventArgs e)
{
if (sender is TextBox tb)
{
string beginContext = tb.Text;
await Task.Delay(2000);
if (beginContext == tb.Text)
{
int value;
if (int.TryParse(tb.Text, out value))
{
if (value > 600)
tb.Text = "600";
else if (value < 30)
tb.Text = "30";
}
ViewModel.FailTimeMax=value;
}
}
}
private void btnCancel_Click(object sender, RoutedEventArgs e) private void btnCancel_Click(object sender, RoutedEventArgs e)
{ {
this.Close(); this.Close();