Compare commits

...

80 Commits

Author SHA1 Message Date
2dust
a6af25ae88 up 1.8.15 2024-02-04 16:49:12 +08:00
2dust
3ac89d68fb Up implementation 2024-02-04 16:46:01 +08:00
2dust
267a43fd97 Refactor 2024-02-04 12:28:58 +08:00
2dust
6d0384b6f1 Bug fix 2024-02-02 16:47:22 +08:00
2dust
7ae4be402f Merge pull request #2785 from maskedeken/fix-wg
MUX should be disabled when using wireguard
2024-01-25 16:26:41 +08:00
2dust
46f0b7be5b Merge pull request #2783 from admarty/master
Update Vietnamese translation.
2024-01-25 16:26:19 +08:00
Eken Chan
b253f2d947 MUX should be disabled when using wireguard 2024-01-24 16:15:44 +08:00
admarty
444ade8afe Update Vietnamese translation 2024-01-23 19:27:46 +07:00
admarty
522dbdd170 Update Vietnamese translation. 2024-01-23 19:00:37 +07:00
2dust
d89238e1aa up 1.8.14 2024-01-19 18:56:44 +08:00
2dust
52710020ea update subscription 4 custom configuration 2024-01-19 17:44:35 +08:00
2dust
a18b8f4f2b Bug fix 2024-01-19 10:29:43 +08:00
2dust
01e0a6570d Merge pull request #2768 from solokot/master
Update Russian translation
2024-01-19 08:36:22 +08:00
solokot
cd6ef8f062 Update Russian translation
MTU menu
2024-01-18 08:38:45 +03:00
2dust
ea383c43bd Add share 4 Wireguard 2024-01-18 11:56:03 +08:00
2dust
68e08e3866 Merge pull request #2763 from admarty/master
Update vi translation
2024-01-18 09:57:21 +08:00
admarty
329ed26e85 Update vi translation 2024-01-17 12:56:39 +07:00
admarty
c830a4cea4 Revert "Updated vi translation"
This reverts commit e7e088ec83.
2024-01-17 12:53:53 +07:00
admarty
e7e088ec83 Updated vi translation 2024-01-17 12:46:48 +07:00
2dust
cf6c814eb4 Add share 4 Wireguard 2024-01-16 18:49:00 +08:00
2dust
8f62e42ae8 Add Wireguard Mtu 2024-01-16 17:48:04 +08:00
2dust
89b4060ba2 Merge pull request #2748 from admarty/master
Improve vietnamese translation
2024-01-11 17:53:51 +08:00
admarty
e4d4b329a0 fix typo 2024-01-11 13:03:53 +07:00
admarty
09c5f41995 Improve Vietnamese translation 2024-01-11 12:04:57 +07:00
admarty
02b25787c1 Improve Vietnamese translation 2024-01-11 00:48:28 +07:00
2dust
cdb050b6c5 up 1.8.13 2024-01-08 09:13:42 +08:00
2dust
21f47b536d Bug fix 2024-01-07 17:15:09 +08:00
2dust
f14484c986 Merge pull request #2733 from solokot/master
Russian translation
2024-01-07 16:31:38 +08:00
solokot
358dd78dc3 Russian translation
Small cosmetic fixes
2024-01-07 06:58:48 +00:00
2dust
d79e072316 Merge pull request #2732 from admarty/master
Improve Vietnamese translation
2024-01-07 09:25:27 +08:00
2dust
c24dee567a Modify background color 2024-01-07 09:24:48 +08:00
admarty
9f75bafcea Update strings.xml 2024-01-06 21:11:34 +07:00
admarty
2150452629 Update strings.xml 2024-01-06 20:50:50 +07:00
admarty
507a32a08e Improve Vietnamese translation 2024-01-06 20:22:52 +07:00
2dust
44dee8e850 Add main interface filter cache 2024-01-06 16:56:19 +08:00
2dust
5e4d9246c2 Merge pull request #2721 from mosayeb-a/fix-bug
fix a bug(ConcurrentModificationException)
2024-01-05 12:12:22 +08:00
mosayeb
9fe7419467 fix a bug(ConcurrentModificationException) 2024-01-03 11:15:00 -08:00
2dust
41b2251dfe Merge pull request #2718 from yuhan6665/wireguard
Wireguard can configure tun address
2024-01-02 12:06:08 +08:00
yuhan6665
73fad43573 Handle IPv6 address correctly 2024-01-01 22:16:07 -05:00
yuhan6665
4676717582 Wireguard can configure tun address
local tun address is also used by Wireguard server to check against its "allowedIPs" setting
2024-01-01 21:49:54 -05:00
2dust
834766e6e7 Merge pull request #2704 from yuhan6665/wireguard
Add Wireguard maunal config
2023-12-24 16:32:26 +08:00
yuhan6665
e304dce347 Add Wireguard maunal config
No support for export and import via links yet
2023-12-23 23:51:09 -05:00
2dust
61654aefeb Merge pull request #2690 from user09283/patch-1
Update strings.xml
2023-12-18 15:49:06 +08:00
user09283
1664aaa25b Update strings.xml
Update Vietnamese language
2023-12-18 00:41:47 +07:00
2dust
68f1f64f3d Merge pull request #2674 from mmrabbani/patch-1
Add HTTP3 to ALPN options
2023-12-14 09:20:11 +08:00
2dust
7bad57ca52 Merge pull request #2671 from solokot/master
Update Russian translation
2023-12-14 09:18:57 +08:00
MMR
1c8e1f0993 Add HTTP3 to ALPN options 2023-12-13 16:01:12 +03:30
solokot
6037ae6fc4 Update Russian translation 2023-12-09 18:59:33 +03:00
2dust
dc1c5400b8 Add mux concurency setting 2023-12-07 17:49:09 +08:00
2dust
9ae4688171 Update build.yml 2023-12-05 17:52:45 +08:00
2dust
e3f39234b2 Bug fix 2023-12-05 16:51:31 +08:00
2dust
0df0b2d6ac Merge pull request #2662 from solokot/master
Update Russian
2023-12-05 16:43:00 +08:00
2dust
9ce96d0591 up 1.8.12 2023-12-04 17:13:03 +08:00
2dust
a7ef0618ba Adjust FOREGROUND_SERVICE_SPECIAL_USE 2023-12-04 17:11:53 +08:00
solokot
cc9b083e5d Update Russian 2023-12-04 10:00:31 +03:00
2dust
5af322552c Merge pull request #2660 from yuhan6665/xudp
Use Android ID as XUDP basekey
2023-12-04 12:14:38 +08:00
yuhan6665
fc852281dd Use Android ID as XUDP basekey
Since Xray 1.8.1, XUDP pass basekey as the global ID. It can maintain the same UDP port on the proxy server outbound.
To enable maximum UDP connectivity, client should pass the device unique ID in the environment variable.
2023-12-03 22:06:06 -05:00
2dust
0b2f036a22 Up targetSdkVersion 34 2023-12-04 10:55:26 +08:00
2dust
cf2becb5e9 Bug fix 2023-12-04 08:52:26 +08:00
2dust
82d8eba1b9 Merge pull request #2636 from hadi-norouzi/feature/update_sub
Fix minimum update interval for subscription auto update
2023-11-21 16:47:17 +08:00
Hadi Norouzi
286ad34d94 fix minimum subscription update interval 2023-11-20 21:25:35 +03:30
Hadi Norouzi
ff0bc6594d Merge branch 'master' into feature/update_sub 2023-11-20 21:23:38 +03:30
Hadi Norouzi
7b8113aef1 Merge remote-tracking branch 'origin/feature/update_sub' into feature/update_sub
# Conflicts:
#	V2rayNG/app/build.gradle
2023-11-20 21:21:24 +03:30
2dust
81d2ef5db5 Merge pull request #2421 from Fangliding/master
自己写了个小破自动编译action
2023-11-18 18:56:13 +08:00
2dust
bdea3ef88c up 1.8.11 2023-11-18 17:24:37 +08:00
2dust
c62c86fc29 Adjust UI 2023-11-18 16:39:04 +08:00
2dust
59f698f755 Merge pull request #2614 from solokot/master
Update Russian translation
2023-11-18 16:32:06 +08:00
solokot
9ac979006e Update Russian translation 2023-11-17 20:44:48 +03:00
2dust
da6291a965 Add menu for privacy policy 2023-11-17 16:29:32 +08:00
2dust
bf6555e57c Update CR.md 2023-11-17 14:15:29 +08:00
2dust
35b114220e Update CR.md 2023-11-17 10:15:10 +08:00
Hadi Norouzi
800bb6a4e9 Merge branch 'master' of https://github.com/2dust/v2rayNG 2023-11-15 23:51:10 +03:30
Hadi Norouzi
c93edd8875 Merge branch '2dust:master' into master 2023-10-05 20:13:50 +03:30
Hadi Norouzi
caa2edcf05 bump version 2023-09-12 22:32:41 +03:30
风扇滑翔翼
71bd684b46 Update build.yml 2023-09-08 23:51:26 +08:00
风扇滑翔翼
02ae19f0c7 Merge remote-tracking branch 'upstream/master' 2023-09-08 15:25:46 +00:00
风扇滑翔翼
775fa5ea62 update and remove unnecessary step 2023-08-12 00:54:19 +08:00
风扇滑翔翼
a5bb39ac8a 允许手动指定编译时的Xray core版本
and update sth
2023-07-23 11:16:06 +08:00
风扇滑翔翼
073c7c0410 新增触发条件 2023-07-23 03:29:07 +08:00
风扇滑翔翼
7e88e3ba4f 新建Build action 2023-07-23 03:23:02 +08:00
47 changed files with 952 additions and 273 deletions

64
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,64 @@
name: Build APK
on:
push:
workflow_dispatch:
inputs:
XRAY_CORE_VERSION:
description: 'Xray core version or commit hash'
required: false
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Java
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'
- name: Setup Golang
uses: actions/setup-go@v4
with:
go-version: '1.21.4'
- name: Install gomobile
run: |
go install golang.org/x/mobile/cmd/gomobile@latest
echo "$(go env GOPATH)/bin" >> $GITHUB_PATH
- name: Setup Android environment
uses: android-actions/setup-android@v2
- name: Build dependencies
run: |
mkdir ${{ github.workspace }}/build
cd ${{ github.workspace }}/build
git clone --depth=1 -b main https://github.com/2dust/AndroidLibXrayLite.git
cd AndroidLibXrayLite
go get github.com/xtls/xray-core@${{ github.event.inputs.XRAY_CORE_VERSION }} || true
gomobile init
go mod tidy -v
gomobile bind -v -androidapi 19 -ldflags='-s -w' ./
cp *.aar ${{ github.workspace }}/V2rayNG/app/libs/
- name: Build APK
run: |
cd ${{ github.workspace }}/V2rayNG
chmod 777 *
sed -i 's/org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8/org.gradle.jvmargs=-Xmx2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8/' ${{ github.workspace }}/V2rayNG/gradle.properties
./gradlew assembleDebug
- name: Upload APK
uses: actions/upload-artifact@v3
with:
name: apk
path: ${{ github.workspace }}/V2rayNG/app/build/outputs/apk/debug/

41
CR.md
View File

@@ -1,29 +1,40 @@
v2rayNG 隐私条款
**v2rayNG 隐私权政策**
最后更新 2017-11-22
本政策自2023年11月17日起施行
v2rayNG 尊重并保护所有用户的个人隐私权,为此我们向大众公开这份隐私条款。**您使用 v2rayNG 即代表您以阅读并同意了这份条款,如果您不同意这份条款请立即停止使用并卸载 v2rayNG。**
2dust 将 v2rayNG 应用程序构建为开源应用程序。 本服务由 2dust 免费提供,并且旨在按原样使用。
1. 信息收集
v2rayNG 尊重并保护所有用户的个人隐私权,为此我们向大众公开这份隐私权政策。**您使用 v2rayNG 即代表您以阅读并同意了这份条款,如果您不同意这份条款请立即停止使用并卸载 v2rayNG。**
v2rayNG 软件自身不会发送任何信息到开发者,但是您下载软件的应用市场(如 Google Play可能会收集关于应用运行状态的相关信息并提供给 v2rayNG 开发者。有关这些信息,请阅读您使用的应用市场所提供的隐私条款。
**信息收集**
v2rayNG 软件中可能包含需要通过 IAP 支付解锁的功能,您的支付信息将由相关的 IAP 渠道进行处理,而我们对支付信息没有访问权
v2rayNG 软件自身不会发送任何信息到开发者,但是您下载软件的应用市场(如 Google Play可能会收集关于应用运行状态的相关信息并提供给 v2rayNG 开发者。有关这些信息,请阅读您使用的应用市场所提供的隐私权政策
当您向 v2rayNG 开发者反馈软件运行中的错误时,开发者可能会要求您提供软件以及系统的日志以帮助确认问题的原因。因日志中可能包括敏感信息,此类信息只能由您自己操作发送。**我们不对任何传输服务的安全性和隐私性做任何明示或暗示的担保,请您在传送相关信息时选择可以您自身可以接受的方式。**
v2rayNG 软件中可能包含需要通过 IAP 支付解锁的功能,您的支付信息将由相关的 IAP 渠道进行处理,而我们对支付信息没有访问权。
2. 信息共享
当您向 v2rayNG 开发者反馈软件运行中的错误时,开发者可能会要求您提供软件以及系统的日志以帮助确认问题的原因。因日志中可能包括敏感信息,此类信息只能由您自己操作发送。**我们不对任何传输服务的安全性和隐私性做任何明示或暗示的担保,请您在传送相关信息时选择可以您自身可以接受的方式。**
我们不会向任何第三方出售收集到的用户数据。我们可能向外部开发者提供信息以协助软件的开发,但是在提供信息之前我们会传达相关保密义务并确定其可以遵守。
**信息共享**
3. 信息存留
我们不会向任何第三方出售收集到的用户数据。我们可能向外部开发者提供信息以协助软件的开发,但是在提供信息之前我们会传达相关保密义务并确定其可以遵守。
除非有相关法律规定,我们会在 30 天内清除不需要继续使用的用户数据,或将统计数据整合为无法识别单个用户的综合报告。
**信息存留**
4. 信息泄露
除非有相关法律规定,我们会在 30 天内清除不需要继续使用的用户数据,或将统计数据整合为无法识别单个用户的综合报告。
我们会使用合理的技术和安全手段尽力保护用户的数据,但是无法保证数据的绝对安全。如果我们确认数据发生了泄露,我们会在 7 天内通过可用的渠道通知用户。**您同意不向我们追责任何因不可抗力而造成的损失。**
**信息泄露**
5. 条款修改
我们会使用合理的技术和安全手段尽力保护用户的数据,但是无法保证数据的绝对安全。如果我们确认数据发生了泄露,我们会在 7 天内通过可用的渠道通知用户。**您同意不向我们追责任何因不可抗力而造成的损失。**
**儿童隐私**
这些服务不针对 13 岁以下的任何人。我不会故意收集 13 岁以下儿童的个人身份信息。 如果我发现 13 岁以下的儿童向我提供了个人信息,我会立即从我们的服务器中删除该信息。 如果您是父母或监护人,并且您知道您的孩子向我们提供了个人信息,请与我联系,以便我能够采取必要的行动。
**条款修改**
我们保留修改这份隐私权政策的权利,但是会确保在更新条款前至少 30 天通过我们的可用渠道和应用内提示来通知用户。**在新条款生效后继续使用软件即表示您同意修改后的隐私权政策。**
**联系我们**
如果您对我的隐私政策有任何疑问或建议,请随时通过 CaptainIronng@protonmail.com 与我联系。
我们保留修改这份隐私条款的权利,但是会确保在更新条款前至少 30 天通过我们的可用渠道和应用内提示来通知用户。**在新条款生效后继续使用软件即表示您同意修改后的隐私条款。**

View File

@@ -17,8 +17,8 @@ android {
minSdkVersion 21
targetSdkVersion Integer.parseInt("$targetSdkVer")
multiDexEnabled true
versionCode 526
versionName "1.8.10"
versionCode 540
versionName "1.8.15"
}
buildTypes {
@@ -101,13 +101,13 @@ dependencies {
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.9.0'
implementation 'com.google.android.material:material:1.11.0'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.preference:preference-ktx:1.2.0'
implementation 'androidx.recyclerview:recyclerview:1.3.0'
implementation 'androidx.recyclerview:recyclerview:1.3.2'
implementation 'androidx.fragment:fragment-ktx:1.5.7'
implementation 'androidx.multidex:multidex:2.0.1'
implementation 'androidx.viewpager2:viewpager2:1.1.0-beta01'
implementation 'androidx.viewpager2:viewpager2:1.1.0-beta02'
// Androidx ktx
implementation 'androidx.activity:activity-ktx:1.7.1'
@@ -120,7 +120,7 @@ dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4"
implementation 'com.tencent:mmkv-static:1.2.15'
implementation 'com.tencent:mmkv-static:1.3.3'
implementation 'com.google.code.gson:gson:2.10.1'
implementation 'io.reactivex:rxjava:1.3.8'
implementation 'io.reactivex:rxandroid:1.2.1'
@@ -131,7 +131,7 @@ dependencies {
implementation 'com.blacksquircle.ui:language-base:2.8.0'
implementation 'com.blacksquircle.ui:language-json:2.8.0'
implementation 'io.github.g00fy2.quickie:quickie-bundled:1.6.0'
implementation 'com.google.zxing:core:3.5.1'
implementation 'com.google.zxing:core:3.5.3'
def work_version = "2.8.1"

View File

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
xmlns:tools="http://schemas.android.com/tools"
tools:ignore="MissingLeanbackLauncher">
<supports-screens
android:anyDensity="true"
@@ -26,6 +27,9 @@
<!-- <useapplications-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission
android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"
android:minSdkVersion="34" />
<!-- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
@@ -127,6 +131,7 @@
android:exported="false"
android:label="@string/app_name"
android:permission="android.permission.BIND_VPN_SERVICE"
android:foregroundServiceType="specialUse"
android:process=":RunSoLibV2RayDaemon">
<intent-filter>
<action android:name="android.net.VpnService" />
@@ -134,12 +139,19 @@
<meta-data
android:name="android.net.VpnService.SUPPORTS_ALWAYS_ON"
android:value="true" />
<property
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
android:value="vpn" />
</service>
<service android:name=".service.V2RayProxyOnlyService"
android:exported="false"
android:label="@string/app_name"
android:foregroundServiceType="specialUse"
android:process=":RunSoLibV2RayDaemon">
<property
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
android:value="proxy" />
</service>
<service android:name=".service.V2RayTestService"
@@ -166,11 +178,15 @@
android:name=".service.QSTileService"
android:icon="@drawable/ic_stat_name"
android:label="@string/app_tile_name"
android:foregroundServiceType="specialUse"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
android:process=":RunSoLibV2RayDaemon">
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE" />
</intent-filter>
<property
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
android:value="tile" />
</service>
<!-- =====================Tasker===================== -->
<activity

View File

@@ -41,6 +41,7 @@ object AppConfig {
const val PREF_CONFIRM_REMOVE = "pref_confirm_remove"
const val PREF_START_SCAN_IMMEDIATE = "pref_start_scan_immediate"
const val PREF_MUX_ENABLED = "pref_mux_enabled"
const val PREF_MUX_CONCURRENCY = "pref_mux_concurency"
const val PREF_MUX_XUDP_CONCURRENCY = "pref_mux_xudp_concurency"
const val PREF_MUX_XUDP_QUIC = "pref_mux_xudp_quic"
@@ -61,12 +62,15 @@ object AppConfig {
const val TAG_DIRECT = "direct"
const val TAG_BLOCKED = "block"
const val androidpackagenamelistUrl = "https://raw.githubusercontent.com/2dust/androidpackagenamelist/master/proxy.txt"
const val v2rayCustomRoutingListUrl = "https://raw.githubusercontent.com/2dust/v2rayCustomRoutingList/master/"
const val androidpackagenamelistUrl =
"https://raw.githubusercontent.com/2dust/androidpackagenamelist/master/proxy.txt"
const val v2rayCustomRoutingListUrl =
"https://raw.githubusercontent.com/2dust/v2rayCustomRoutingList/master/"
const val v2rayNGIssues = "https://github.com/2dust/v2rayNG/issues"
const val v2rayNGWikiMode = "https://github.com/2dust/v2rayNG/wiki/Mode"
const val v2rayNGPrivacyPolicy = "https://raw.githubusercontent.com/2dust/v2rayNG/master/CR.md"
const val promotionUrl = "aHR0cHM6Ly85LjIzNDQ1Ni54eXovYWJjLmh0bWw="
const val geoUrl = "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/";
const val geoUrl = "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/"
const val DNS_AGENT = "1.1.1.1"
const val DNS_DIRECT = "223.5.5.5"
@@ -74,6 +78,9 @@ object AppConfig {
const val PORT_LOCAL_DNS = "10853"
const val PORT_SOCKS = "10808"
const val PORT_HTTP = "10809"
const val WIREGUARD_LOCAL_ADDRESS_V4 = "172.16.0.2/32"
const val WIREGUARD_LOCAL_ADDRESS_V6 = "2606:4700:110:8f81:d551:a0:532e:a2b3/128"
const val WIREGUARD_LOCAL_MTU = "1420"
const val MSG_REGISTER_CLIENT = 1
const val MSG_STATE_RUNNING = 11
@@ -97,4 +104,6 @@ object AppConfig {
const val SUBSCRIPTION_DEFAULT_UPDATE_INTERVAL = "1440" // 24 hours
const val SUBSCRIPTION_UPDATE_TASK_NAME = "subscription_updater"
const val CACHE_SUBSCRIPTION_ID = "cache_subscription_id"
const val CACHE_KEYWORD_FILTER = "cache_keyword_filter"
}

View File

@@ -19,23 +19,32 @@ data class ServerConfig(
when(configType) {
EConfigType.VMESS, EConfigType.VLESS ->
return ServerConfig(
configType = configType,
outboundBean = V2rayConfig.OutboundBean(
protocol = configType.name.lowercase(),
settings = V2rayConfig.OutboundBean.OutSettingsBean(
vnext = listOf(V2rayConfig.OutboundBean.OutSettingsBean.VnextBean(
users = listOf(V2rayConfig.OutboundBean.OutSettingsBean.VnextBean.UsersBean())))),
streamSettings = V2rayConfig.OutboundBean.StreamSettingsBean()))
EConfigType.CUSTOM, EConfigType.WIREGUARD ->
configType = configType,
outboundBean = V2rayConfig.OutboundBean(
protocol = configType.name.lowercase(),
settings = V2rayConfig.OutboundBean.OutSettingsBean(
vnext = listOf(V2rayConfig.OutboundBean.OutSettingsBean.VnextBean(
users = listOf(V2rayConfig.OutboundBean.OutSettingsBean.VnextBean.UsersBean())))),
streamSettings = V2rayConfig.OutboundBean.StreamSettingsBean()))
EConfigType.CUSTOM ->
return ServerConfig(configType = configType)
EConfigType.SHADOWSOCKS, EConfigType.SOCKS, EConfigType.TROJAN ->
return ServerConfig(
configType = configType,
outboundBean = V2rayConfig.OutboundBean(
protocol = configType.name.lowercase(),
settings = V2rayConfig.OutboundBean.OutSettingsBean(
servers = listOf(V2rayConfig.OutboundBean.OutSettingsBean.ServersBean())),
streamSettings = V2rayConfig.OutboundBean.StreamSettingsBean()))
configType = configType,
outboundBean = V2rayConfig.OutboundBean(
protocol = configType.name.lowercase(),
settings = V2rayConfig.OutboundBean.OutSettingsBean(
servers = listOf(V2rayConfig.OutboundBean.OutSettingsBean.ServersBean())),
streamSettings = V2rayConfig.OutboundBean.StreamSettingsBean()))
EConfigType.WIREGUARD ->
return ServerConfig(
configType = configType,
outboundBean = V2rayConfig.OutboundBean(
protocol = configType.name.lowercase(),
settings = V2rayConfig.OutboundBean.OutSettingsBean(
secretKey = "",
peers = listOf(V2rayConfig.OutboundBean.OutSettingsBean.WireGuardBean())
)))
}
}
}
@@ -60,10 +69,6 @@ data class ServerConfig(
fun getV2rayPointDomainAndPort(): String {
val address = getProxyOutbound()?.getServerAddress().orEmpty()
val port = getProxyOutbound()?.getServerPort()
return if (Utils.isIpv6Address(address)) {
String.format("[%s]:%s", address, port)
} else {
String.format("%s:%s", address, port)
}
return Utils.getIpv6Address(address) + ":" + port
}
}

View File

@@ -74,7 +74,7 @@ data class V2rayConfig(
var response: Response? = null,
/*DNS*/
val network: String? = null,
val address: Any? = null,
var address: Any? = null,
val port: Int? = null,
/*Freedom*/
var domainStrategy: String? = null,
@@ -83,8 +83,10 @@ data class V2rayConfig(
/*Loopback*/
val inboundTag: String? = null,
/*Wireguard*/
val secretKey: String? = null,
var secretKey: String? = null,
val peers: List<WireGuardBean>? = null,
var reserved: List<Int>? = null,
var mtu :Int? = null
) {
data class VnextBean(var address: String = "",

View File

@@ -17,16 +17,16 @@ val Context.v2RayApplication: AngApplication
get() = applicationContext as AngApplication
fun Context.toast(message: Int): Toast = ToastCompat
.makeText(this, message, Toast.LENGTH_SHORT)
.apply {
show()
}
.makeText(this, message, Toast.LENGTH_SHORT)
.apply {
show()
}
fun Context.toast(message: CharSequence): Toast = ToastCompat
.makeText(this, message, Toast.LENGTH_SHORT)
.apply {
show()
}
.makeText(this, message, Toast.LENGTH_SHORT)
.apply {
show()
}
fun JSONObject.putOpt(pair: Pair<String, Any>) = putOpt(pair.first, pair.second)
fun JSONObject.putOpt(pairs: Map<String, Any>) = pairs.forEach { putOpt(it.key to it.value) }
@@ -77,4 +77,8 @@ val URLConnection.responseLength: Long
get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) contentLengthLong else contentLength.toLong()
val URI.idnHost: String
get() = (host!!).replace("[", "").replace("]", "")
get() = (host!!).replace("[", "").replace("]", "")
fun String.removeWhiteSpace(): String {
return this.replace(" ", "")
}

View File

@@ -36,7 +36,12 @@ class QSTileService : TileService() {
super.onStartListening()
setState(Tile.STATE_INACTIVE)
mMsgReceive = ReceiveMessageHandler(this)
registerReceiver(mMsgReceive, IntentFilter(AppConfig.BROADCAST_ACTION_ACTIVITY))
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
registerReceiver(mMsgReceive, IntentFilter(AppConfig.BROADCAST_ACTION_ACTIVITY), Context.RECEIVER_EXPORTED)
} else {
registerReceiver(mMsgReceive, IntentFilter(AppConfig.BROADCAST_ACTION_ACTIVITY))
}
MessageUtil.sendMsg2Service(this, AppConfig.MSG_REGISTER_CLIENT, "")
}

View File

@@ -73,5 +73,8 @@ object SubscriptionUpdater {
if (count <= 0) {
AngConfigManager.importBatchConfig(Utils.decode(server!!), subid, append)
}
if (count <= 0) {
AngConfigManager.appendCustomConfigServer(server, subid)
}
}
}

View File

@@ -50,7 +50,7 @@ object V2RayServiceManager {
set(value) {
field = value
Seq.setContext(value?.get()?.getService()?.applicationContext)
Libv2ray.initV2Env(Utils.userAssetPath(value?.get()?.getService()))
Libv2ray.initV2Env(Utils.userAssetPath(value?.get()?.getService()), Utils.getDeviceIdForXUDPBaseKey())
}
var currentConfig: ServerConfig? = null
@@ -133,7 +133,11 @@ object V2RayServiceManager {
mFilter.addAction(Intent.ACTION_SCREEN_ON)
mFilter.addAction(Intent.ACTION_SCREEN_OFF)
mFilter.addAction(Intent.ACTION_USER_PRESENT)
service.registerReceiver(mMsgReceive, mFilter)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
service.registerReceiver(mMsgReceive, mFilter, Context.RECEIVER_EXPORTED)
} else {
service.registerReceiver(mMsgReceive, mFilter)
}
} catch (e: Exception) {
Log.d(ANG_PACKAGE, e.toString())
}

View File

@@ -20,7 +20,7 @@ class V2RayTestService : Service() {
override fun onCreate() {
super.onCreate()
Seq.setContext(this)
Libv2ray.initV2Env(Utils.userAssetPath(this))
Libv2ray.initV2Env(Utils.userAssetPath(this), Utils.getDeviceIdForXUDPBaseKey())
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {

View File

@@ -103,7 +103,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
binding.drawerLayout.addDrawerListener(toggle)
toggle.syncState()
binding.navView.setNavigationItemSelectedListener(this)
binding.version.text = "v${BuildConfig.VERSION_NAME} (${SpeedtestUtil.getLibVersion()})"
"v${BuildConfig.VERSION_NAME} (${SpeedtestUtil.getLibVersion()})".also { binding.version.text = it }
setupViewModel()
copyAssets()
@@ -253,6 +253,10 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
importManually(EConfigType.TROJAN.value)
true
}
R.id.import_manually_wireguard -> {
importManually(EConfigType.WIREGUARD.value)
true
}
R.id.import_config_custom_clipboard -> {
importConfigCustomClipboard()
true
@@ -415,6 +419,9 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
if (count <= 0) {
count = AngConfigManager.importBatchConfig(Utils.decode(server!!), subid2, append)
}
if (count <= 0) {
count = AngConfigManager.appendCustomConfigServer(server, subid2)
}
if (count > 0) {
toast(R.string.toast_success)
mainViewModel.reloadServerList()
@@ -682,6 +689,9 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
R.id.logcat -> {
startActivity(Intent(this, LogcatActivity::class.java))
}
R.id.privacy_policy-> {
Utils.openUri(this, AppConfig.v2rayNGPrivacyPolicy)
}
}
binding.drawerLayout.closeDrawer(GravityCompat.START)
return true

View File

@@ -96,7 +96,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
holder.itemMainBinding.tvType.text = config.configType.name.lowercase()
}
}
val strState = "***${outbound?.getServerAddress()?.drop(3)} : ${outbound?.getServerPort()}"
val strState = "${outbound?.getServerAddress()?.dropLast(3)}*** : ${outbound?.getServerPort()}"
holder.itemMainBinding.tvStatistics.text = strState
holder.itemMainBinding.layoutShare.setOnClickListener {

View File

@@ -21,7 +21,6 @@ import com.v2ray.ang.extension.v2RayApplication
import com.v2ray.ang.util.AppManagerUtil
import com.v2ray.ang.util.Utils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import rx.android.schedulers.AndroidSchedulers
import rx.schedulers.Schedulers

View File

@@ -1,7 +1,7 @@
package com.v2ray.ang.ui
import android.Manifest
import android.app.Activity.RESULT_OK
import androidx.appcompat.app.AppCompatActivity.RESULT_OK
import android.content.Intent
import android.os.Bundle
import android.text.TextUtils
@@ -18,7 +18,6 @@ import com.v2ray.ang.extension.toast
import com.v2ray.ang.extension.v2RayApplication
import com.v2ray.ang.util.Utils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
class RoutingSettingsFragment : Fragment() {

View File

@@ -1,7 +1,7 @@
package com.v2ray.ang.ui
import android.Manifest
import android.app.Activity
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.content.Intent
import android.graphics.BitmapFactory
@@ -54,7 +54,7 @@ class ScannerActivity : BaseActivity(){
private fun finished(text: String) {
val intent = Intent()
intent.putExtra("SCAN_RESULT", text)
setResult(Activity.RESULT_OK, intent)
setResult(AppCompatActivity.RESULT_OK, intent)
finish()
}

View File

@@ -10,22 +10,32 @@ import androidx.appcompat.app.AlertDialog
import com.tencent.mmkv.MMKV
import com.v2ray.ang.AppConfig
import com.v2ray.ang.AppConfig.PREF_ALLOW_INSECURE
import com.v2ray.ang.AppConfig.WIREGUARD_LOCAL_ADDRESS_V4
import com.v2ray.ang.AppConfig.WIREGUARD_LOCAL_ADDRESS_V6
import com.v2ray.ang.AppConfig.WIREGUARD_LOCAL_MTU
import com.v2ray.ang.R
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.dto.ServerConfig
import com.v2ray.ang.dto.V2rayConfig
import com.v2ray.ang.dto.V2rayConfig.Companion.DEFAULT_PORT
import com.v2ray.ang.dto.V2rayConfig.Companion.TLS
import com.v2ray.ang.extension.removeWhiteSpace
import com.v2ray.ang.extension.toast
import com.v2ray.ang.util.MmkvManager
import com.v2ray.ang.util.MmkvManager.ID_MAIN
import com.v2ray.ang.util.MmkvManager.KEY_SELECTED_SERVER
import com.v2ray.ang.util.Utils
import com.v2ray.ang.util.Utils.getIpv6Address
class ServerActivity : BaseActivity() {
private val mainStorage by lazy { MMKV.mmkvWithID(ID_MAIN, MMKV.MULTI_PROCESS_MODE) }
private val settingsStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SETTING, MMKV.MULTI_PROCESS_MODE) }
private val settingsStorage by lazy {
MMKV.mmkvWithID(
MmkvManager.ID_SETTING,
MMKV.MULTI_PROCESS_MODE
)
}
private val editGuid by lazy { intent.getStringExtra("guid").orEmpty() }
private val isRunning by lazy {
intent.getBooleanExtra("isRunning", false)
@@ -33,7 +43,8 @@ class ServerActivity : BaseActivity() {
&& editGuid == mainStorage?.decodeString(KEY_SELECTED_SERVER)
}
private val createConfigType by lazy {
EConfigType.fromInt(intent.getIntExtra("createConfigType", EConfigType.VMESS.value)) ?: EConfigType.VMESS
EConfigType.fromInt(intent.getIntExtra("createConfigType", EConfigType.VMESS.value))
?: EConfigType.VMESS
}
private val subscriptionId by lazy {
intent.getStringExtra("subscriptionId")
@@ -66,12 +77,13 @@ class ServerActivity : BaseActivity() {
private val allowinsecures: Array<out String> by lazy {
resources.getStringArray(R.array.allowinsecures)
}
private val uTlsItems: Array<out String> by lazy {
private val uTlsItems: Array<out String> by lazy {
resources.getStringArray(R.array.streamsecurity_utls)
}
private val alpns: Array<out String> by lazy {
resources.getStringArray(R.array.streamsecurity_alpn)
}
// Kotlin synthetics was used, but since it is removed in 1.8. We switch to old manual approach.
// We don't use AndroidViewBinding because, it is better to share similar logics for different
// protocols. Use findViewById manually ensures the xml are de-coupled with the activity logic.
@@ -103,26 +115,38 @@ class ServerActivity : BaseActivity() {
private val container_short_id: LinearLayout? by lazy { findViewById(R.id.l7) }
private val et_spider_x: EditText? by lazy { findViewById(R.id.et_spider_x) }
private val container_spider_x: LinearLayout? by lazy { findViewById(R.id.l8) }
private val et_reserved1: EditText? by lazy { findViewById(R.id.et_reserved1) }
private val et_reserved2: EditText? by lazy { findViewById(R.id.et_reserved2) }
private val et_reserved3: EditText? by lazy { findViewById(R.id.et_reserved3) }
private val et_local_address: EditText? by lazy { findViewById(R.id.et_local_address) }
private val et_local_mtu: EditText? by lazy { findViewById(R.id.et_local_mtu) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
title = getString(R.string.title_server)
val config = MmkvManager.decodeServerConfig(editGuid)
when(config?.configType ?: createConfigType) {
when (config?.configType ?: createConfigType) {
EConfigType.VMESS -> setContentView(R.layout.activity_server_vmess)
EConfigType.CUSTOM -> return
EConfigType.SHADOWSOCKS -> setContentView(R.layout.activity_server_shadowsocks)
EConfigType.SOCKS -> setContentView(R.layout.activity_server_socks)
EConfigType.VLESS -> setContentView(R.layout.activity_server_vless)
EConfigType.TROJAN -> setContentView(R.layout.activity_server_trojan)
else -> setContentView(R.layout.activity_server_vmess)
EConfigType.WIREGUARD -> setContentView(R.layout.activity_server_wireguard)
}
sp_network?.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
override fun onItemSelected(
parent: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
val types = transportTypes(networks[position])
sp_header_type?.isEnabled = types.size > 1
val adapter = ArrayAdapter(this@ServerActivity, android.R.layout.simple_spinner_item, types)
val adapter =
ArrayAdapter(this@ServerActivity, android.R.layout.simple_spinner_item, types)
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
sp_header_type?.adapter = adapter
sp_header_type_title?.text = if (networks[position] == "grpc")
@@ -134,12 +158,18 @@ class ServerActivity : BaseActivity() {
et_path?.text = Utils.getEditable(transportDetails[2])
}
}
override fun onNothingSelected(parent: AdapterView<*>?) {
// do nothing
}
}
sp_stream_security?.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
override fun onItemSelected(
parent: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
if (streamSecuritys[position].isBlank()) {
container_sni?.visibility = View.GONE
container_fingerprint?.visibility = View.GONE
@@ -166,6 +196,7 @@ class ServerActivity : BaseActivity() {
}
}
}
override fun onNothingSelected(p0: AdapterView<*>?) {
// do nothing
}
@@ -182,37 +213,67 @@ class ServerActivity : BaseActivity() {
*/
private fun bindingServer(config: ServerConfig): Boolean {
val outbound = config.getProxyOutbound() ?: return false
val streamSetting = config.outboundBean?.streamSettings ?: return false
et_remarks.text = Utils.getEditable(config.remarks)
et_address.text = Utils.getEditable(outbound.getServerAddress().orEmpty())
et_port.text = Utils.getEditable(outbound.getServerPort()?.toString() ?: DEFAULT_PORT.toString())
et_port.text =
Utils.getEditable(outbound.getServerPort()?.toString() ?: DEFAULT_PORT.toString())
et_id.text = Utils.getEditable(outbound.getPassword().orEmpty())
et_alterId?.text = Utils.getEditable(outbound.settings?.vnext?.get(0)?.users?.get(0)?.alterId.toString())
et_alterId?.text =
Utils.getEditable(outbound.settings?.vnext?.get(0)?.users?.get(0)?.alterId.toString())
if (config.configType == EConfigType.SOCKS) {
et_security?.text = Utils.getEditable(outbound.settings?.servers?.get(0)?.users?.get(0)?.user.orEmpty())
et_security?.text =
Utils.getEditable(outbound.settings?.servers?.get(0)?.users?.get(0)?.user.orEmpty())
} else if (config.configType == EConfigType.VLESS) {
et_security?.text = Utils.getEditable(outbound.getSecurityEncryption().orEmpty())
val flow = Utils.arrayFind(flows, outbound.settings?.vnext?.get(0)?.users?.get(0)?.flow.orEmpty())
val flow = Utils.arrayFind(
flows,
outbound.settings?.vnext?.get(0)?.users?.get(0)?.flow.orEmpty()
)
if (flow >= 0) {
sp_flow?.setSelection(flow)
}
} else if (config.configType == EConfigType.TROJAN) {
val flow = Utils.arrayFind(flows, outbound.settings?.servers?.get(0)?.flow.orEmpty())
if (flow >= 0) {
sp_flow?.setSelection(flow)
} else if (config.configType == EConfigType.WIREGUARD) {
et_public_key?.text =
Utils.getEditable(outbound.settings?.peers?.get(0)?.publicKey.orEmpty())
if (outbound.settings?.reserved == null) {
et_reserved1?.text = Utils.getEditable("0")
et_reserved2?.text = Utils.getEditable("0")
et_reserved3?.text = Utils.getEditable("0")
} else {
et_reserved1?.text =
Utils.getEditable(outbound.settings?.reserved?.get(0).toString())
et_reserved2?.text =
Utils.getEditable(outbound.settings?.reserved?.get(1).toString())
et_reserved3?.text =
Utils.getEditable(outbound.settings?.reserved?.get(2).toString())
}
if (outbound.settings?.address == null) {
et_local_address?.text =
Utils.getEditable("${WIREGUARD_LOCAL_ADDRESS_V4},${WIREGUARD_LOCAL_ADDRESS_V6}")
} else {
val list = outbound.settings?.address as List<*>
et_local_address?.text = Utils.getEditable(list.joinToString())
}
if (outbound.settings?.mtu == null) {
et_local_mtu?.text = Utils.getEditable(WIREGUARD_LOCAL_MTU)
} else {
et_local_mtu?.text = Utils.getEditable(outbound.settings?.mtu.toString())
}
}
val securityEncryptions = if (config.configType == EConfigType.SHADOWSOCKS) shadowsocksSecuritys else securitys
val security = Utils.arrayFind(securityEncryptions, outbound.getSecurityEncryption().orEmpty())
val securityEncryptions =
if (config.configType == EConfigType.SHADOWSOCKS) shadowsocksSecuritys else securitys
val security =
Utils.arrayFind(securityEncryptions, outbound.getSecurityEncryption().orEmpty())
if (security >= 0) {
sp_security?.setSelection(security)
}
val streamSetting = config.outboundBean?.streamSettings ?: return true
val streamSecurity = Utils.arrayFind(streamSecuritys, streamSetting.security)
if (streamSecurity >= 0) {
sp_stream_security?.setSelection(streamSecurity)
(streamSetting.tlsSettings?: streamSetting.realitySettings)?.let { tlsSetting ->
(streamSetting.tlsSettings ?: streamSetting.realitySettings)?.let { tlsSetting ->
container_sni?.visibility = View.VISIBLE
container_fingerprint?.visibility = View.VISIBLE
container_alpn?.visibility = View.VISIBLE
@@ -222,12 +283,16 @@ class ServerActivity : BaseActivity() {
sp_stream_fingerprint?.setSelection(utlsIndex)
}
tlsSetting.alpn?.let {
val alpnIndex = Utils.arrayFind(alpns, Utils.removeWhiteSpace(tlsSetting.alpn.joinToString())!!)
val alpnIndex = Utils.arrayFind(
alpns,
Utils.removeWhiteSpace(tlsSetting.alpn.joinToString())!!
)
sp_stream_alpn?.setSelection(alpnIndex)
}
if (streamSetting.tlsSettings != null) {
container_allow_insecure?.visibility = View.VISIBLE
val allowinsecure = Utils.arrayFind(allowinsecures, tlsSetting.allowInsecure.toString())
val allowinsecure =
Utils.arrayFind(allowinsecures, tlsSetting.allowInsecure.toString())
if (allowinsecure >= 0) {
sp_allow_insecure?.setSelection(allowinsecure)
}
@@ -282,6 +347,13 @@ class ServerActivity : BaseActivity() {
//et_security.text = null
sp_flow?.setSelection(0)
et_public_key?.text = null
et_reserved1?.text = Utils.getEditable("0")
et_reserved2?.text = Utils.getEditable("0")
et_reserved3?.text = Utils.getEditable("0")
et_local_address?.text =
Utils.getEditable("${WIREGUARD_LOCAL_ADDRESS_V4},${WIREGUARD_LOCAL_ADDRESS_V6}")
et_local_mtu?.text = Utils.getEditable(WIREGUARD_LOCAL_MTU)
return true
}
@@ -302,11 +374,12 @@ class ServerActivity : BaseActivity() {
toast(R.string.server_lab_port)
return false
}
val config = MmkvManager.decodeServerConfig(editGuid) ?: ServerConfig.create(createConfigType)
val config =
MmkvManager.decodeServerConfig(editGuid) ?: ServerConfig.create(createConfigType)
if (config.configType != EConfigType.SOCKS && TextUtils.isEmpty(et_id.text.toString())) {
if (config.configType == EConfigType.TROJAN || config.configType == EConfigType.SHADOWSOCKS) {
toast(R.string.server_lab_id3)
}else{
} else {
toast(R.string.server_lab_id)
}
return false
@@ -332,10 +405,14 @@ class ServerActivity : BaseActivity() {
config.outboundBean?.settings?.servers?.get(0)?.let { server ->
saveServers(server, port, config)
}
val wireguard = config.outboundBean?.settings
wireguard?.peers?.get(0)?.let { _ ->
savePeer(wireguard, port)
}
config.outboundBean?.streamSettings?.let {
saveStreamSettings(it)
}
if(config.subscriptionId.isEmpty() && !subscriptionId.isNullOrEmpty()) {
if (config.subscriptionId.isEmpty() && !subscriptionId.isNullOrEmpty()) {
config.subscriptionId = subscriptionId!!
}
@@ -345,7 +422,11 @@ class ServerActivity : BaseActivity() {
return true
}
private fun saveVnext(vnext: V2rayConfig.OutboundBean.OutSettingsBean.VnextBean, port: Int, config: ServerConfig) {
private fun saveVnext(
vnext: V2rayConfig.OutboundBean.OutSettingsBean.VnextBean,
port: Int,
config: ServerConfig
) {
vnext.address = et_address.text.toString().trim()
vnext.port = port
vnext.users[0].id = et_id.text.toString().trim()
@@ -358,7 +439,11 @@ class ServerActivity : BaseActivity() {
}
}
private fun saveServers(server: V2rayConfig.OutboundBean.OutSettingsBean.ServersBean, port: Int, config: ServerConfig) {
private fun saveServers(
server: V2rayConfig.OutboundBean.OutSettingsBean.ServersBean,
port: Int,
config: ServerConfig
) {
server.address = et_address.text.toString().trim()
server.port = port
if (config.configType == EConfigType.SHADOWSOCKS) {
@@ -368,7 +453,8 @@ class ServerActivity : BaseActivity() {
if (TextUtils.isEmpty(et_security?.text) && TextUtils.isEmpty(et_id.text)) {
server.users = null
} else {
val socksUsersBean = V2rayConfig.OutboundBean.OutSettingsBean.ServersBean.SocksUsersBean()
val socksUsersBean =
V2rayConfig.OutboundBean.OutSettingsBean.ServersBean.SocksUsersBean()
socksUsersBean.user = et_security?.text.toString().trim()
socksUsersBean.pass = et_id.text.toString().trim()
server.users = listOf(socksUsersBean)
@@ -378,6 +464,23 @@ class ServerActivity : BaseActivity() {
}
}
private fun savePeer(wireguard: V2rayConfig.OutboundBean.OutSettingsBean, port: Int) {
wireguard.secretKey = et_id.text.toString().trim()
wireguard.peers?.get(0)?.publicKey = et_public_key?.text.toString().trim()
wireguard.peers?.get(0)?.endpoint =
getIpv6Address(et_address.text.toString().trim()) + ":" + port
val reserved1 = Utils.parseInt(et_reserved1?.text.toString())
val reserved2 = Utils.parseInt(et_reserved2?.text.toString())
val reserved3 = Utils.parseInt(et_reserved3?.text.toString())
if (reserved1 > 0 || reserved2 > 0 || reserved3 > 0) {
wireguard.reserved = listOf(reserved1, reserved2, reserved3)
} else {
wireguard.reserved = null
}
wireguard.address = et_local_address?.text.toString().removeWhiteSpace().split(",")
wireguard.mtu = Utils.parseInt(et_local_mtu?.text.toString())
}
private fun saveStreamSettings(streamSetting: V2rayConfig.OutboundBean.StreamSettingsBean) {
val network = sp_network?.selectedItemPosition ?: return
val type = sp_header_type?.selectedItemPosition ?: return
@@ -393,15 +496,15 @@ class ServerActivity : BaseActivity() {
val spiderX = et_spider_x?.text?.toString()?.trim() ?: return
var sni = streamSetting.populateTransportSettings(
transport = networks[network],
headerType = transportTypes(networks[network])[type],
host = requestHost,
path = path,
seed = path,
quicSecurity = requestHost,
key = path,
mode = transportTypes(networks[network])[type],
serviceName = path
transport = networks[network],
headerType = transportTypes(networks[network])[type],
host = requestHost,
path = path,
seed = path,
quicSecurity = requestHost,
key = path,
mode = transportTypes(networks[network])[type],
serviceName = path
)
if (sniField.isNotBlank()) {
sni = sniField
@@ -413,14 +516,14 @@ class ServerActivity : BaseActivity() {
}
streamSetting.populateTlsSettings(
streamSecurity = streamSecuritys[streamSecurity],
allowInsecure = allowInsecure,
sni = sni,
fingerprint = uTlsItems[utlsIndex],
alpns = alpns[alpnIndex],
publicKey = publicKey,
shortId = shortId,
spiderX = spiderX
streamSecurity = streamSecuritys[streamSecurity],
allowInsecure = allowInsecure,
sni = sni,
fingerprint = uTlsItems[utlsIndex],
alpns = alpns[alpnIndex],
publicKey = publicKey,
shortId = shortId,
spiderX = spiderX
)
}
@@ -429,12 +532,15 @@ class ServerActivity : BaseActivity() {
"tcp" -> {
tcpTypes
}
"kcp", "quic" -> {
kcpAndQuicTypes
}
"grpc" -> {
grpcModes
}
else -> {
arrayOf("---")
}
@@ -485,10 +591,12 @@ class ServerActivity : BaseActivity() {
deleteServer()
true
}
R.id.save_config -> {
saveServer()
true
}
else -> super.onOptionsItemSelected(item)
}
}

View File

@@ -6,9 +6,7 @@ import android.text.TextUtils
import android.view.View
import androidx.activity.viewModels
import androidx.preference.*
import androidx.work.Constraints
import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.NetworkType
import androidx.work.PeriodicWorkRequest
import androidx.work.multiprocess.RemoteWorkManager
import com.v2ray.ang.AngApplication
@@ -39,6 +37,7 @@ class SettingsActivity : BaseActivity() {
private val vpnDns by lazy { findPreference<EditTextPreference>(AppConfig.PREF_VPN_DNS) }
private val mux by lazy { findPreference<CheckBoxPreference>(AppConfig.PREF_MUX_ENABLED) }
private val muxConcurrency by lazy { findPreference<EditTextPreference>(AppConfig.PREF_MUX_CONCURRENCY) }
private val muxXudpConcurrency by lazy { findPreference<EditTextPreference>(AppConfig.PREF_MUX_XUDP_CONCURRENCY) }
private val muxXudpQuic by lazy { findPreference<ListPreference>(AppConfig.PREF_MUX_XUDP_QUIC) }
@@ -76,9 +75,12 @@ class SettingsActivity : BaseActivity() {
}
autoUpdateInterval?.setOnPreferenceChangeListener { _, any ->
val nval = any as String
autoUpdateInterval?.summary =
if (TextUtils.isEmpty(nval) or (nval.toLong() < 1)) AppConfig.SUBSCRIPTION_DEFAULT_UPDATE_INTERVAL else nval
var nval = any as String
autoUpdateInterval?.summary = nval
// It must be greater than 15 minutes because WorkManager couldn't run tasks under 15 minutes intervals
nval =
if (TextUtils.isEmpty(nval) or (nval.toLong() < 15)) AppConfig.SUBSCRIPTION_DEFAULT_UPDATE_INTERVAL else nval
configureUpdateTask(nval.toLong())
true
}
@@ -158,10 +160,14 @@ class SettingsActivity : BaseActivity() {
updateMux(newValue as Boolean)
true
}
muxXudpConcurrency?.setOnPreferenceChangeListener { _, newValue ->
muxConcurrency?.setOnPreferenceChangeListener { _, newValue ->
updateMuxConcurrency(newValue as String)
true
}
muxXudpConcurrency?.setOnPreferenceChangeListener { _, newValue ->
updateMuxXudpConcurrency(newValue as String)
true
}
}
override fun onStart() {
@@ -176,6 +182,7 @@ class SettingsActivity : BaseActivity() {
socksPort?.summary = defaultSharedPreferences.getString(AppConfig.PREF_SOCKS_PORT, AppConfig.PORT_SOCKS)
httpPort?.summary = defaultSharedPreferences.getString(AppConfig.PREF_HTTP_PORT, AppConfig.PORT_HTTP)
updateMux(defaultSharedPreferences.getBoolean(AppConfig.PREF_MUX_ENABLED, false))
muxConcurrency?.summary = defaultSharedPreferences.getString(AppConfig.PREF_MUX_CONCURRENCY, "8")
muxXudpConcurrency?.summary = defaultSharedPreferences.getString(AppConfig.PREF_MUX_XUDP_CONCURRENCY, "8")
autoUpdateInterval?.summary = defaultSharedPreferences.getString(AppConfig.SUBSCRIPTION_AUTO_UPDATE_INTERVAL,AppConfig.SUBSCRIPTION_DEFAULT_UPDATE_INTERVAL)
autoUpdateInterval?.isEnabled = defaultSharedPreferences.getBoolean(AppConfig.SUBSCRIPTION_AUTO_UPDATE, false)
@@ -254,14 +261,24 @@ class SettingsActivity : BaseActivity() {
private fun updateMux(enabled: Boolean) {
val defaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity())
muxConcurrency?.isEnabled = enabled
muxXudpConcurrency?.isEnabled = enabled
muxXudpQuic?.isEnabled = enabled
if (enabled) {
updateMuxConcurrency(defaultSharedPreferences.getString(AppConfig.PREF_MUX_XUDP_CONCURRENCY, "8"))
updateMuxConcurrency(defaultSharedPreferences.getString(AppConfig.PREF_MUX_CONCURRENCY, "8"))
updateMuxXudpConcurrency(defaultSharedPreferences.getString(AppConfig.PREF_MUX_XUDP_CONCURRENCY, "8"))
}
}
private fun updateMuxConcurrency(value: String?) {
if (value == null) {
} else {
val concurrency = value.toIntOrNull() ?: 8
muxConcurrency?.summary = concurrency.toString()
}
}
private fun updateMuxXudpConcurrency(value: String?) {
if (value == null) {
muxXudpQuic?.isEnabled = true
} else {

View File

@@ -1,6 +1,6 @@
package com.v2ray.ang.ui
import android.app.Activity
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.ArrayAdapter
@@ -90,7 +90,7 @@ class TaskerActivity : BaseActivity() {
intent.putExtra(AppConfig.TASKER_EXTRA_BUNDLE, extraBundle)
intent.putExtra(AppConfig.TASKER_EXTRA_STRING_BLURB, blurb)
setResult(Activity.RESULT_OK, intent)
setResult(AppCompatActivity.RESULT_OK, intent)
finish()
}

View File

@@ -12,6 +12,8 @@ import com.v2ray.ang.AppConfig
import com.v2ray.ang.AppConfig.ANG_CONFIG
import com.v2ray.ang.AppConfig.HTTPS_PROTOCOL
import com.v2ray.ang.AppConfig.HTTP_PROTOCOL
import com.v2ray.ang.AppConfig.WIREGUARD_LOCAL_ADDRESS_V4
import com.v2ray.ang.AppConfig.WIREGUARD_LOCAL_MTU
import com.v2ray.ang.R
import com.v2ray.ang.dto.*
import com.v2ray.ang.dto.V2rayConfig.Companion.DEFAULT_SECURITY
@@ -20,6 +22,7 @@ import com.v2ray.ang.util.MmkvManager.KEY_SELECTED_SERVER
import java.net.URI
import java.util.*
import com.v2ray.ang.extension.idnHost
import com.v2ray.ang.extension.removeWhiteSpace
import com.v2ray.ang.extension.toast
object AngConfigManager {
@@ -432,6 +435,28 @@ object AngConfigManager {
queryParam["security"] ?: "", allowInsecure,
queryParam["sni"] ?: sni, fingerprint, queryParam["alpn"], pbk, sid, spx
)
} else if (str.startsWith(EConfigType.WIREGUARD.protocolScheme)) {
val uri = URI(Utils.fixIllegalUrl(str))
config = ServerConfig.create(EConfigType.WIREGUARD)
config.remarks = Utils.urlDecode(uri.fragment ?: "")
if (uri.rawQuery != null) {
val queryParam = uri.rawQuery.split("&")
.associate { it.split("=").let { (k, v) -> k to Utils.urlDecode(v) } }
config.outboundBean?.settings?.let { wireguard ->
wireguard.secretKey = uri.userInfo
wireguard.address =
(queryParam["address"] ?: WIREGUARD_LOCAL_ADDRESS_V4).removeWhiteSpace()
.split(",")
wireguard.peers?.get(0)?.publicKey = queryParam["publickey"] ?: ""
wireguard.peers?.get(0)?.endpoint = "${uri.idnHost}:${uri.port}"
wireguard.mtu = Utils.parseInt(queryParam["mtu"] ?: WIREGUARD_LOCAL_MTU)
wireguard.reserved =
(queryParam["reserved"] ?: "0,0,0").removeWhiteSpace().split(",")
.map { it.toInt() }
}
}
}
if (config == null) {
return R.string.toast_incorrect_protocol
@@ -443,7 +468,8 @@ object AngConfigManager {
?.getServerAddress() == removedSelectedServer.getProxyOutbound()
?.getServerAddress() &&
config.getProxyOutbound()
?.getServerPort() == removedSelectedServer.getProxyOutbound()?.getServerPort()
?.getServerPort() == removedSelectedServer.getProxyOutbound()
?.getServerPort()
) {
mainStorage?.encode(KEY_SELECTED_SERVER, guid)
}
@@ -571,7 +597,11 @@ object AngConfigManager {
try {
val config = MmkvManager.decodeServerConfig(guid) ?: return ""
val outbound = config.getProxyOutbound() ?: return ""
val streamSetting = outbound.streamSettings ?: return ""
val streamSetting =
outbound.streamSettings ?: V2rayConfig.OutboundBean.StreamSettingsBean()
if (config.configType != EConfigType.WIREGUARD) {
if (outbound.streamSettings == null) return ""
}
return config.configType.protocolScheme + when (config.configType) {
EConfigType.VMESS -> {
val vmessQRCode = VmessQRCode()
@@ -600,7 +630,8 @@ object AngConfigManager {
Utils.encode(json)
}
EConfigType.CUSTOM, EConfigType.WIREGUARD -> ""
EConfigType.CUSTOM -> ""
EConfigType.SHADOWSOCKS -> {
val remark = "#" + Utils.urlEncode(config.remarks)
val pw =
@@ -617,10 +648,13 @@ object AngConfigManager {
EConfigType.SOCKS -> {
val remark = "#" + Utils.urlEncode(config.remarks)
val pw =
Utils.encode("${outbound.settings?.servers?.get(0)?.users?.get(0)?.user}:${outbound.getPassword()}")
if (outbound.settings?.servers?.get(0)?.users?.get(0)?.user != null)
"${outbound.settings?.servers?.get(0)?.users?.get(0)?.user}:${outbound.getPassword()}"
else
":"
val url = String.format(
"%s@%s:%s",
pw,
Utils.encode(pw),
Utils.getIpv6Address(outbound.getServerAddress()!!),
outbound.getServerPort()
)
@@ -672,7 +706,8 @@ object AngConfigManager {
dicQuery["spx"] = Utils.urlEncode(tlsSetting.spiderX!!)
}
}
dicQuery["type"] = streamSetting.network.ifEmpty { V2rayConfig.DEFAULT_NETWORK }
dicQuery["type"] =
streamSetting.network.ifEmpty { V2rayConfig.DEFAULT_NETWORK }
outbound.getTransportSettingDetails()?.let { transportDetails ->
when (streamSetting.network) {
@@ -733,6 +768,37 @@ object AngConfigManager {
)
url + query + remark
}
EConfigType.WIREGUARD -> {
val remark = "#" + Utils.urlEncode(config.remarks)
val dicQuery = HashMap<String, String>()
dicQuery["publickey"] =
Utils.urlEncode(outbound.settings?.peers?.get(0)?.publicKey.toString())
if (outbound.settings?.reserved != null) {
dicQuery["reserved"] = Utils.urlEncode(
Utils.removeWhiteSpace(outbound.settings?.reserved?.joinToString())
.toString()
)}
dicQuery["address"] = Utils.urlEncode(
Utils.removeWhiteSpace((outbound.settings?.address as List<*>).joinToString())
.toString()
)
if (outbound.settings?.mtu != null) {
dicQuery["mtu"] = outbound.settings?.mtu.toString()
}
val query = "?" + dicQuery.toList().joinToString(
separator = "&",
transform = { it.first + "=" + it.second })
val url = String.format(
"%s@%s:%s",
Utils.urlEncode(outbound.getPassword().toString()),
Utils.getIpv6Address(outbound.getServerAddress()!!),
outbound.getServerPort()
)
url + query + remark
}
}
} catch (e: Exception) {
e.printStackTrace()
@@ -858,17 +924,19 @@ object AngConfigManager {
return 0
}
val removedSelectedServer =
if (!TextUtils.isEmpty(subid) && !append) {
MmkvManager.decodeServerConfig(mainStorage?.decodeString(KEY_SELECTED_SERVER) ?: "")?.let {
if (it.subscriptionId == subid) {
return@let it
}
return@let null
if (!TextUtils.isEmpty(subid) && !append) {
MmkvManager.decodeServerConfig(
mainStorage?.decodeString(KEY_SELECTED_SERVER) ?: ""
)?.let {
if (it.subscriptionId == subid) {
return@let it
}
} else {
null
return@let null
}
if(!append) {
} else {
null
}
if (!append) {
MmkvManager.removeServerViaSubid(subid)
}
// var servers = server
@@ -908,4 +976,24 @@ object AngConfigManager {
return true
}
fun appendCustomConfigServer(server: String?, subid: String): Int {
if (server == null) {
return 0
}
if (server.contains("inbounds")
&& server.contains("outbounds")
&& server.contains("routing")
) {
val config = ServerConfig.create(EConfigType.CUSTOM)
config.remarks = System.currentTimeMillis().toString()
config.subscriptionId = subid
config.fullConfig = Gson().fromJson(server, V2rayConfig::class.java)
val key = MmkvManager.encodeServerConfig("", config)
serverRawStorage?.encode(key, server)
return 1
} else {
return 0
}
}
}

View File

@@ -13,6 +13,7 @@ import android.content.res.Configuration.UI_MODE_NIGHT_NO
import android.net.Uri
import android.os.Build
import android.os.LocaleList
import android.provider.Settings
import android.util.Log
import android.util.Patterns
import android.webkit.URLUtil
@@ -282,7 +283,7 @@ object Utils {
fun urlDecode(url: String): String {
return try {
URLDecoder.decode(URLDecoder.decode(url), "utf-8")
URLDecoder.decode(url, "UTF-8")
} catch (e: Exception) {
e.printStackTrace()
url
@@ -317,6 +318,11 @@ object Utils {
return extDir.absolutePath
}
fun getDeviceIdForXUDPBaseKey(): String {
val androidId = Settings.Secure.ANDROID_ID.toByteArray(charset("UTF-8"))
return Base64.encodeToString(androidId.copyOf(32), Base64.NO_PADDING.or(Base64.URL_SAFE))
}
fun getUrlContext(url: String, timeout: Int): String {
var result: String
var conn: HttpURLConnection? = null
@@ -360,7 +366,7 @@ object Utils {
}
fun getIpv6Address(address: String): String {
return if (isIpv6Address(address)) {
return if (isIpv6Address(address) && !address.contains('[') && !address.contains(']')) {
String.format("[%s]", address)
} else {
address

View File

@@ -7,6 +7,8 @@ import com.google.gson.*
import com.tencent.mmkv.MMKV
import com.v2ray.ang.AppConfig
import com.v2ray.ang.AppConfig.ANG_PACKAGE
import com.v2ray.ang.AppConfig.WIREGUARD_LOCAL_ADDRESS_V4
import com.v2ray.ang.AppConfig.WIREGUARD_LOCAL_ADDRESS_V6
import com.v2ray.ang.dto.V2rayConfig
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.dto.ERoutingMode
@@ -32,12 +34,12 @@ object V2rayConfigUtil {
} else {
raw
}
Log.d(ANG_PACKAGE, customConfig)
//Log.d(ANG_PACKAGE, customConfig)
return Result(true, customConfig)
}
val outbound = config.getProxyOutbound() ?: return Result(false, "")
val result = getV2rayNonCustomConfig(context, outbound)
Log.d(ANG_PACKAGE, result.content)
//Log.d(ANG_PACKAGE, result.content)
return result
} catch (e: Exception) {
e.printStackTrace()
@@ -402,11 +404,11 @@ object V2rayConfigUtil {
private fun updateOutboundWithGlobalSettings(outbound: V2rayConfig.OutboundBean): Boolean {
try {
var muxEnabled = settingsStorage?.decodeBool(AppConfig.PREF_MUX_ENABLED, false)
val protocol = outbound.protocol
if (protocol.equals(EConfigType.SHADOWSOCKS.name, true)
|| protocol.equals(EConfigType.SOCKS.name, true)
|| protocol.equals(EConfigType.TROJAN.name, true)
|| protocol.equals(EConfigType.WIREGUARD.name, true)
) {
muxEnabled = false
} else if (protocol.equals(EConfigType.VLESS.name, true)
@@ -414,10 +416,10 @@ object V2rayConfigUtil {
) {
muxEnabled = false
}
if (muxEnabled == true) {
outbound.mux?.enabled = true
outbound.mux?.concurrency = 8
outbound.mux?.concurrency =
settingsStorage?.decodeInt(AppConfig.PREF_MUX_CONCURRENCY) ?: 8
outbound.mux?.xudpConcurrency =
settingsStorage?.decodeInt(AppConfig.PREF_MUX_XUDP_CONCURRENCY) ?: 8
outbound.mux?.xudpProxyUDP443 =
@@ -427,6 +429,18 @@ object V2rayConfigUtil {
outbound.mux?.concurrency = -1
}
if (protocol.equals(EConfigType.WIREGUARD.name, true)) {
var localTunAddr = if (outbound.settings?.address == null) {
listOf(WIREGUARD_LOCAL_ADDRESS_V4, WIREGUARD_LOCAL_ADDRESS_V6)
} else {
outbound.settings?.address as List<*>
}
if (settingsStorage?.decodeBool(AppConfig.PREF_PREFER_IPV6) != true) {
localTunAddr = listOf(localTunAddr.first())
}
outbound.settings?.address = localTunAddr
}
if (outbound.streamSettings?.network == DEFAULT_NETWORK
&& outbound.streamSettings?.tcpSettings?.header?.type == HTTP
) {

View File

@@ -2,6 +2,7 @@ package com.v2ray.ang.viewmodel
import android.app.Application
import android.content.*
import android.os.Build
import android.util.Log
import android.view.LayoutInflater
import android.widget.ArrayAdapter
@@ -24,12 +25,28 @@ import kotlinx.coroutines.*
import java.util.*
class MainViewModel(application: Application) : AndroidViewModel(application) {
private val mainStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_MAIN, MMKV.MULTI_PROCESS_MODE) }
private val serverRawStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SERVER_RAW, MMKV.MULTI_PROCESS_MODE) }
private val mainStorage by lazy {
MMKV.mmkvWithID(
MmkvManager.ID_MAIN,
MMKV.MULTI_PROCESS_MODE
)
}
private val serverRawStorage by lazy {
MMKV.mmkvWithID(
MmkvManager.ID_SERVER_RAW,
MMKV.MULTI_PROCESS_MODE
)
}
private val settingsStorage by lazy {
MMKV.mmkvWithID(
MmkvManager.ID_SETTING,
MMKV.MULTI_PROCESS_MODE
)
}
var serverList = MmkvManager.decodeServerList()
var subscriptionId: String = ""
var keywordFilter: String = ""
var subscriptionId: String = settingsStorage.decodeString(AppConfig.CACHE_SUBSCRIPTION_ID, "")!!
var keywordFilter: String = settingsStorage.decodeString(AppConfig.CACHE_KEYWORD_FILTER, "")!!
private set
val serversCache = mutableListOf<ServersCache>()
val isRunning by lazy { MutableLiveData<Boolean>() }
@@ -40,7 +57,18 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
fun startListenBroadcast() {
isRunning.value = false
getApplication<AngApplication>().registerReceiver(mMsgReceiver, IntentFilter(AppConfig.BROADCAST_ACTION_ACTIVITY))
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
getApplication<AngApplication>().registerReceiver(
mMsgReceiver,
IntentFilter(AppConfig.BROADCAST_ACTION_ACTIVITY),
Context.RECEIVER_EXPORTED
)
} else {
getApplication<AngApplication>().registerReceiver(
mMsgReceiver,
IntentFilter(AppConfig.BROADCAST_ACTION_ACTIVITY)
)
}
MessageUtil.sendMsg2Service(getApplication(), AppConfig.MSG_REGISTER_CLIENT, "")
}
@@ -62,7 +90,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
serverList.remove(guid)
MmkvManager.removeServer(guid)
val index = getPosition(guid)
if(index >= 0){
if (index >= 0) {
serversCache.removeAt(index)
}
}
@@ -75,7 +103,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
val key = MmkvManager.encodeServerConfig("", config)
serverRawStorage?.encode(key, server)
serverList.add(0, key)
serversCache.add(0, ServersCache(key,config))
serversCache.add(0, ServersCache(key, config))
}
fun swapServer(fromPosition: Int, toPosition: Int) {
@@ -115,7 +143,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
val testResult = SpeedtestUtil.tcping(serverAddress, serverPort)
launch(Dispatchers.Main) {
MmkvManager.encodeServerTestDelayMillis(item.guid, testResult)
updateListAction.value = getPosition(item.guid)
updateListAction.value = getPosition(item.guid)
}
}
}
@@ -128,12 +156,18 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
MmkvManager.clearAllTestDelayResults()
updateListAction.value = -1 // update all
val serversCopy = serversCache.toList() // Create a copy of the list
getApplication<AngApplication>().toast(R.string.connection_test_testing)
viewModelScope.launch(Dispatchers.Default) { // without Dispatchers.Default viewModelScope will launch in main thread
for (item in serversCache) {
for (item in serversCopy) {
val config = V2rayConfigUtil.getV2rayConfig(getApplication(), item.guid)
if (config.status) {
MessageUtil.sendMsg2TestService(getApplication(), AppConfig.MSG_MEASURE_CONFIG, Pair(item.guid, config.content))
MessageUtil.sendMsg2TestService(
getApplication(),
AppConfig.MSG_MEASURE_CONFIG,
Pair(item.guid, config.content)
)
}
}
}
@@ -143,7 +177,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
MessageUtil.sendMsg2Service(getApplication(), AppConfig.MSG_MEASURE_DELAY, "")
}
fun filterConfig(context :Context) {
fun filterConfig(context: Context) {
val subscriptions = MmkvManager.decodeSubscriptions()
val listId = subscriptions.map { it.first }.toList().toMutableList()
val listRemarks = subscriptions.map { it.second.remarks }.toList().toMutableList()
@@ -155,7 +189,11 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
}
val ivBinding = DialogConfigFilterBinding.inflate(LayoutInflater.from(context))
ivBinding.spSubscriptionId.adapter = ArrayAdapter<String>( context, android.R.layout.simple_spinner_dropdown_item, listRemarks)
ivBinding.spSubscriptionId.adapter = ArrayAdapter<String>(
context,
android.R.layout.simple_spinner_dropdown_item,
listRemarks
)
ivBinding.spSubscriptionId.setSelection(checkedItem)
ivBinding.etKeyword.text = Utils.getEditable(keywordFilter)
val builder = AlertDialog.Builder(context).setView(ivBinding.root)
@@ -169,6 +207,8 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
subscriptions[position].first
}
keywordFilter = ivBinding.etKeyword.text.toString()
settingsStorage?.encode(AppConfig.CACHE_SUBSCRIPTION_ID, subscriptionId)
settingsStorage?.encode(AppConfig.CACHE_KEYWORD_FILTER, keywordFilter)
reloadServerList()
dialogInterface?.dismiss()
@@ -193,7 +233,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
// }.show()
}
fun getPosition(guid: String) : Int {
fun getPosition(guid: String): Int {
serversCache.forEachIndexed { index, it ->
if (it.guid == guid)
return index
@@ -206,20 +246,24 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
serversCache.forEachIndexed { index, it ->
val outbound = it.config.getProxyOutbound()
serversCache.forEachIndexed { index2, it2 ->
if(index2 > index){
if (index2 > index) {
val outbound2 = it2.config.getProxyOutbound()
if( outbound == outbound2 && !deleteServer.contains(it2.guid))
{
if (outbound == outbound2 && !deleteServer.contains(it2.guid)) {
deleteServer.add(it2.guid)
}
}
}
}
for(it in deleteServer){
for (it in deleteServer) {
MmkvManager.removeServer(it)
}
reloadServerList()
getApplication<AngApplication>().toast(getApplication<AngApplication>().getString(R.string.title_del_duplicate_config_count, deleteServer.count()))
getApplication<AngApplication>().toast(
getApplication<AngApplication>().getString(
R.string.title_del_duplicate_config_count,
deleteServer.count()
)
)
}
private val mMsgReceiver = object : BroadcastReceiver() {
@@ -228,23 +272,29 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
AppConfig.MSG_STATE_RUNNING -> {
isRunning.value = true
}
AppConfig.MSG_STATE_NOT_RUNNING -> {
isRunning.value = false
}
AppConfig.MSG_STATE_START_SUCCESS -> {
getApplication<AngApplication>().toast(R.string.toast_services_success)
isRunning.value = true
}
AppConfig.MSG_STATE_START_FAILURE -> {
getApplication<AngApplication>().toast(R.string.toast_services_failure)
isRunning.value = false
}
AppConfig.MSG_STATE_STOP_SUCCESS -> {
isRunning.value = false
}
AppConfig.MSG_MEASURE_DELAY_SUCCESS -> {
updateTestResultAction.value = intent.getStringExtra("content")
}
AppConfig.MSG_MEASURE_CONFIG_SUCCESS -> {
val resultPair = intent.getSerializableExtra("content") as Pair<String, Long>
MmkvManager.encodeServerTestDelayMillis(resultPair.first, resultPair.second)

View File

@@ -61,6 +61,7 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
AppConfig.PREF_SNIFFING_ENABLED -> {
settingsStorage?.encode(key, sharedPreferences.getBoolean(key, true))
}
AppConfig.PREF_MUX_CONCURRENCY,
AppConfig.PREF_MUX_XUDP_CONCURRENCY -> {
settingsStorage?.encode(key, sharedPreferences.getString(key, "8")?.toIntOrNull() ?: 8)
}

View File

@@ -48,7 +48,7 @@
android:id="@+id/layout_test"
android:layout_width="match_parent"
android:layout_height="@dimen/connection_test_height"
android:background="?attr/colorPrimary"
android:background="@color/colorMainTestBg"
android:gravity="center|start"
android:nextFocusRight="@+id/fab"
android:clickable="true"

View File

@@ -0,0 +1,199 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.ServerActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="@dimen/layout_margin_top_height">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/title_server"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_margin_top_height"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/server_lab_remarks" />
<EditText
android:id="@+id/et_remarks"
android:layout_width="match_parent"
android:layout_height="@dimen/edit_height"
android:inputType="text" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_margin_top_height"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/server_lab_address3" />
<EditText
android:id="@+id/et_address"
android:layout_width="match_parent"
android:layout_height="@dimen/edit_height"
android:inputType="text" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_margin_top_height"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/server_lab_port3" />
<EditText
android:id="@+id/et_port"
android:layout_width="match_parent"
android:layout_height="@dimen/edit_height"
android:inputType="number" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_margin_top_height"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/server_lab_secret_key" />
<EditText
android:id="@+id/et_id"
android:layout_width="match_parent"
android:layout_height="@dimen/edit_height"
android:inputType="text" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_margin_top_height"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/server_lab_public_key" />
<EditText
android:id="@+id/et_public_key"
android:layout_width="match_parent"
android:layout_height="@dimen/edit_height"
android:inputType="text" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_margin_top_height"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/server_lab_reserved" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:id="@+id/et_reserved1"
android:layout_width="60dp"
android:layout_height="@dimen/edit_height"
android:inputType="number" />
<EditText
android:id="@+id/et_reserved2"
android:layout_width="60dp"
android:layout_height="@dimen/edit_height"
android:inputType="number" />
<EditText
android:id="@+id/et_reserved3"
android:layout_width="60dp"
android:layout_height="@dimen/edit_height"
android:inputType="number" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_margin_top_height"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/server_lab_local_address" />
<EditText
android:id="@+id/et_local_address"
android:layout_width="match_parent"
android:layout_height="@dimen/edit_height"
android:inputType="text" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_margin_top_height"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/server_lab_local_mtu" />
<EditText
android:id="@+id/et_local_mtu"
android:layout_width="match_parent"
android:layout_height="@dimen/edit_height"
android:inputType="number" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/layout_margin_top_height"
android:layout_marginTop="@dimen/layout_margin_top_height"
android:orientation="vertical" />
</LinearLayout>
</ScrollView>

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
@@ -11,5 +12,5 @@
android:layout_width="330dp"
android:layout_height="330dp"
android:scaleType="fitXY"
android:src="@drawable/ic_fab_check" />
app:srcCompat="@drawable/ic_fab_check" />
</LinearLayout>

View File

@@ -4,7 +4,7 @@
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:background="@color/colorPrimary"
android:foreground="?android:attr/selectableItemBackground"
android:foreground="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true">

View File

@@ -20,7 +20,7 @@
android:focusable="true"
android:nextFocusRight="@+id/layout_share"
android:background="@color/colorPrimary"
android:foreground="?android:attr/selectableItemBackground"
android:foreground="?attr/selectableItemBackground"
android:layout_width="match_parent"
android:layout_height="@dimen/server_height"
android:layout_gravity="center"
@@ -116,7 +116,7 @@
android:id="@+id/layout_share"
android:layout_width="wrap_content"
android:layout_height="@dimen/server_height"
android:background="?android:attr/selectableItemBackground"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:gravity="center"
@@ -127,7 +127,7 @@
<ImageView
android:layout_width="wrap_content"
android:layout_height="@dimen/png_height"
android:src="@drawable/ic_share_24dp"
app:srcCompat="@drawable/ic_share_24dp"
app:tint="?attr/colorMainText" />
</LinearLayout>
@@ -136,7 +136,7 @@
android:id="@+id/layout_edit"
android:layout_width="wrap_content"
android:layout_height="@dimen/server_height"
android:background="?android:attr/selectableItemBackground"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:gravity="center"
@@ -146,7 +146,7 @@
<ImageView
android:layout_width="@dimen/png_height"
android:layout_height="@dimen/png_height"
android:src="@drawable/ic_edit_24dp"
app:srcCompat="@drawable/ic_edit_24dp"
app:tint="?attr/colorMainText" />
</LinearLayout>
@@ -155,7 +155,7 @@
android:id="@+id/layout_remove"
android:layout_width="wrap_content"
android:layout_height="@dimen/server_height"
android:background="?android:attr/selectableItemBackground"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:gravity="center"
@@ -165,7 +165,7 @@
<ImageView
android:layout_width="@dimen/png_height"
android:layout_height="@dimen/png_height"
android:src="@drawable/ic_delete_24dp"
app:srcCompat="@drawable/ic_delete_24dp"
app:tint="?attr/colorMainText" />
</LinearLayout>

View File

@@ -19,7 +19,7 @@
android:clickable="true"
android:focusable="true"
android:background="@color/colorPrimary"
android:foreground="?android:attr/selectableItemBackground"
android:foreground="?attr/selectableItemBackground"
android:nextFocusRight="@+id/layout_edit"
android:layout_width="match_parent"
android:layout_height="@dimen/server_height"
@@ -68,7 +68,7 @@
android:id="@+id/layout_share"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:gravity="center"
@@ -79,7 +79,7 @@
<ImageView
android:layout_width="@dimen/png_height"
android:layout_height="@dimen/png_height"
android:src="@drawable/ic_share_24dp"
app:srcCompat="@drawable/ic_share_24dp"
app:tint="?attr/colorMainText"/>
</LinearLayout>
@@ -88,7 +88,7 @@
android:id="@+id/layout_edit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:gravity="center"
@@ -99,7 +99,7 @@
<ImageView
android:layout_width="@dimen/png_height"
android:layout_height="@dimen/png_height"
android:src="@drawable/ic_edit_24dp"
app:srcCompat="@drawable/ic_edit_24dp"
app:tint="?attr/colorMainText" />
</LinearLayout>

View File

@@ -17,7 +17,7 @@
android:orientation="horizontal"
android:gravity="center"
android:background="@color/colorPrimary"
android:foreground="?android:attr/selectableItemBackground"
android:foreground="?attr/selectableItemBackground"
android:padding="@dimen/nav_header_vertical_spacing">
<LinearLayout
@@ -46,7 +46,7 @@
android:id="@+id/layout_remove"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:padding="@dimen/nav_header_vertical_spacing">
@@ -54,7 +54,7 @@
<ImageView
android:layout_width="@dimen/png_height"
android:layout_height="@dimen/png_height"
android:src="@drawable/ic_delete_24dp"
app:srcCompat="@drawable/ic_delete_24dp"
app:tint="?attr/colorMainText" />
</LinearLayout>
</LinearLayout>

View File

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layout_switch"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/layout_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
@@ -12,5 +13,5 @@
android:layout_width="40dp"
android:layout_height="40dp"
android:padding="10dp"
android:src="@drawable/ic_stat_name" />
app:srcCompat="@drawable/ic_stat_name" />
</LinearLayout>

View File

@@ -32,6 +32,10 @@
android:id="@+id/feedback"
android:icon="@drawable/ic_feedback_24dp"
android:title="@string/title_pref_feedback" />
<item
android:id="@+id/privacy_policy"
android:icon="@drawable/ic_info_24dp"
android:title="@string/title_privacy_policy" />
<!-- place holder for version text at the bottom -->
<item
android:id="@+id/placeholder"

View File

@@ -34,6 +34,10 @@
android:id="@+id/import_manually_trojan"
android:title="@string/menu_item_import_config_manually_trojan"
app:showAsAction="never" />
<item
android:id="@+id/import_manually_wireguard"
android:title="@string/menu_item_import_config_manually_wireguard"
app:showAsAction="never" />
<item
android:title="@string/menu_item_import_config_custom"

View File

@@ -28,6 +28,7 @@
<string name="menu_item_import_config_manually_ss">الكتابة يدويا [Shadowsocks]</string>
<string name="menu_item_import_config_manually_socks">الكتابة يدويا [Socks]</string>
<string name="menu_item_import_config_manually_trojan">الكتابة يدويا [Trojan]</string>
<string name="menu_item_import_config_manually_wireguard">[Wireguard] الكتابة يدويا</string>
<string name="menu_item_import_config_custom">تكوين مخصص</string>
<string name="menu_item_import_config_custom_clipboard">استيراد التكوين المخصص من الحافظة</string>
<string name="menu_item_import_config_custom_local">استيراد التكوين المخصص من ملف محلي</string>
@@ -62,6 +63,9 @@
<string name="server_lab_public_key" translatable="false">مفتاح عام</string>
<string name="server_lab_short_id" translatable="false">ShortId</string>
<string name="server_lab_spider_x" translatable="false">SpiderX</string>
<string name="server_lab_reserved">Reserved (اختياري)</string>
<string name="server_lab_local_address">العنوان المحلي IPv4(اختياري)</string>
<string name="server_lab_local_mtu">Mtu(optional, default 1420)</string>
<string name="toast_success">نجاح</string>
<string name="toast_failure">فشل</string>
<string name="toast_none_data">لا يوجد شيء</string>

View File

@@ -30,6 +30,7 @@
<string name="menu_item_import_config_manually_ss">تایپ دستی[Shadowsocks]</string>
<string name="menu_item_import_config_manually_socks">تایپ دستی[Socks]</string>
<string name="menu_item_import_config_manually_trojan">تایپ دستی[Trojan]</string>
<string name="menu_item_import_config_manually_wireguard">[Wireguard]تایپ دستی</string>
<string name="menu_item_import_config_custom">پیکربندی سفارشی</string>
<string name="menu_item_import_config_custom_clipboard">پیکربندی سفارشی را از کلیپ‌بورد وارد کنید</string>
<string name="menu_item_import_config_custom_local">پیکربندی سفارشی را به صورت محلی وارد کنید</string>
@@ -59,6 +60,9 @@
<string name="server_lab_security4">نام‌کاربری (اختیاری)</string>
<string name="server_lab_encryption">رمزگذاری</string>
<string name="server_lab_flow">جریان</string>
<string name="server_lab_reserved">Reserved (اختیاری)</string>
<string name="server_lab_local_address">آدرس محلی IPv4(اختیاری)</string>
<string name="server_lab_local_mtu">Mtu(optional, default 1420)</string>
<string name="toast_success">موفقیت</string>
<string name="toast_failure">شکست</string>
<string name="toast_none_data">چیزی نیست</string>

View File

@@ -11,4 +11,5 @@
<color name="colorAccent">#FFFFFF</color>
<color name="colorBg">#000000</color>
<color name="colorText">#FFFFFF</color>
<color name="colorMainTestBg">#222222</color>
</resources>

View File

@@ -30,6 +30,7 @@
<string name="menu_item_import_config_manually_ss">Ручной ввод профиля Shadowsocks</string>
<string name="menu_item_import_config_manually_socks">Ручной ввод профиля Socks</string>
<string name="menu_item_import_config_manually_trojan">Ручной ввод профиля Trojan</string>
<string name="menu_item_import_config_manually_wireguard">Ручной ввод профиля Wireguard</string>
<string name="menu_item_import_config_custom">Пользовательский профиль</string>
<string name="menu_item_import_config_custom_clipboard">Импорт из буфера обмена</string>
<string name="menu_item_import_config_custom_local">Импорт из файла</string>
@@ -64,6 +65,10 @@
<string name="server_lab_public_key" translatable="false">PublicKey</string>
<string name="server_lab_short_id" translatable="false">ShortId</string>
<string name="server_lab_spider_x" translatable="false">SpiderX</string>
<string name="server_lab_secret_key" translatable="false">SecretKey</string>
<string name="server_lab_reserved">Reserved (необязательно)</string>
<string name="server_lab_local_address">Локальный адрес (необязательно, IPv4/IPv6 через запятую)</string>
<string name="server_lab_local_mtu">MTU (необязательно, по умолчанию 1420)</string>
<string name="toast_success">Успешно</string>
<string name="toast_failure">Ошибка</string>
<string name="toast_none_data">Ничего нет</string>
@@ -103,7 +108,8 @@
<string name="summary_pref_per_app_proxy">Основной: выделенное приложение соединяется через прокси, не выделенное — напрямую; \n\nРежим обхода: выделенное приложение соединяется напрямую, не выделенное — через прокси.\n\nЕсть возможность автоматического выбора проксируемых приложений в меню.</string>
<string name="title_pref_mux_enabled">Использовать мультиплексирование</string>
<string name="summary_pref_mux_enabled">Быстрее, но это может привести к нестабильному соединению.\nМультиплексирование TCP-трафика с 8 соединениями по умолчанию, ниже можно настроить обработку UDP и QUIC.</string>
<string name="summary_pref_mux_enabled">Быстрее, но это может привести к нестабильному соединению.\nНиже можно настроить обработку TCP, UDP и QUIC.</string>
<string name="title_pref_mux_concurency">TCP-соединения (диапазон от -1 до 1024)</string>
<string name="title_pref_mux_xudp_concurency">XUDP-соединения (диапазон от -1 до 1024)</string>
<string name="title_pref_mux_xudp_quic">Обработка QUIC в мультиплексном туннеле</string>
<string-array name="mux_xudp_quic_entries">
@@ -166,13 +172,14 @@
<string name="summary_pref_feedback">Предложить улучшение или сообщить об ошибке на GitHub</string>
<string name="summary_pref_tg_group">Присоединиться к группе в Telegram</string>
<string name="toast_tg_app_not_found">Приложение Telegram не найдено</string>
<string name="title_privacy_policy">Конфиденциальность</string>
<string name="title_pref_promotion">Содействие</string>
<string name="summary_pref_promotion">Содействие, нажмите для получения подробной информации (пожертвование может быть удалено)</string>
<string name="title_pref_auto_update_subscription">Автоматическое обновление подписок</string>
<string name="summary_pref_auto_update_subscription">Автоматическое обновление подписок в фоновом режиме с указанным интервалом. В зависимости от устройства эта функция может работать не всегда.</string>
<string name="title_pref_auto_update_interval">Интервал автообновления (мин.)</string>
<string name="title_pref_auto_update_interval">Интервал автообновления (мин., не менее 15)</string>
<string name="title_core_loglevel">Подробность ведения журнала</string>
<string name="title_mode">Режим</string>

View File

@@ -12,10 +12,10 @@
<string name="notification_action_stop_v2ray">Ngắt kết nối v2rayNG</string>
<string name="toast_permission_denied">Vui lòng cấp quyền cần thiết cho v2rayNG! Bạn đã từ chối các quyền cần thiết như Camera hay Bộ nhớ?</string>
<string name="notification_action_more">Nhấn để biết thêm</string>
<string name="toast_services_start">Đang bắt đầu v2rayNG...</string>
<string name="toast_services_start">Đang khởi động v2rayNG...</string>
<string name="toast_services_stop">Đã dừng v2rayNG!</string>
<string name="toast_services_success">Đã bắt đầu v2rayNG!</string>
<string name="toast_services_failure">Không thể bắt đầu v2rayNG, kiểm tra lại cấu hình hoặc khởi động lại thiết bị.</string>
<string name="toast_services_success">Đã khởi động v2rayNG!</string>
<string name="toast_services_failure">Không thể khởi động v2rayNG, kiểm tra lại cấu hình.</string>
<!--ServerActivity-->
<string name="title_server">v2rayNG</string>
@@ -29,35 +29,39 @@
<string name="menu_item_import_config_manually_ss">Nhập thủ công [Shadowsocks]</string>
<string name="menu_item_import_config_manually_socks">Nhập thủ công [Socks]</string>
<string name="menu_item_import_config_manually_trojan">Nhập thủ công [Trojan]</string>
<string name="menu_item_import_config_manually_wireguard">Nhập thủ công [Wireguard]</string>
<string name="menu_item_import_config_custom">Nâng cao / Cấu hình tùy chỉnh</string>
<string name="menu_item_import_config_custom_clipboard">Nhập cấu hình tùy chỉnh từ Clipboard</string>
<string name="menu_item_import_config_custom_local">Nhập cấu hình tùy chỉnh từ Tệp</string>
<string name="menu_item_import_config_custom_url">Nhập cấu hình tùy chỉnh từ URL</string>
<string name="menu_item_import_config_custom_url_scan">Nhập cấu hình tùy chỉnh quét URL</string>
<string name="del_config_comfirm">Bạn có muốn xóa cấu hình không?</string>
<string name="del_config_comfirm">Xác nhận xóa?</string>
<string name="server_lab_remarks">Tên cấu hình</string>
<string name="server_lab_address">Địa chỉ</string>
<string name="server_lab_port">Cổng</string>
<string name="server_lab_id">ID</string>
<string name="server_lab_alterid">ID thay thế</string>
<string name="server_lab_security">Bảo mật</string>
<string name="server_lab_network">Kiểu kết nối</string>
<string name="server_lab_alterid">alterId</string>
<string name="server_lab_security">Thuật toán mã hóa</string>
<string name="server_lab_network">Giao thức truyền tải (network)</string>
<string name="server_lab_more_function">Nâng cao</string>
<string name="server_lab_head_type">Loại Head</string>
<string name="server_lab_head_type">Kiểu ngụy trang (type)</string>
<string name="server_lab_mode_type">Chế độ gRPC</string>
<string name="server_lab_request_host">Yêu cầu host (Host/WS/H2) / Bảo mật QUIC</string>
<string name="server_lab_path">Đường dẫn (WS/H2) / Khóa QUIC / KCP seed / Dịch vụ gRPC</string>
<string name="server_lab_request_host">Yêu cầu host(host/ws host/h2 host)/QUIC security</string>
<string name="server_lab_path">path(ws path/h2 path)/QUIC key/kcp seed/gRPC serviceName</string>
<string name="server_lab_stream_security">TLS</string>
<string name="server_lab_allow_insecure">Bỏ qua xác minh chứng chỉ</string>
<string name="server_lab_sni">Địa chỉ SNI</string>
<string name="server_lab_allow_insecure">allowInsecure</string>
<string name="server_lab_sni">SNI</string>
<string name="server_lab_address3">Địa chỉ</string>
<string name="server_lab_port3">Cổng</string>
<string name="server_lab_id3">Mật khẩu</string>
<string name="server_lab_security3">Bảo mật</string>
<string name="server_lab_id4">Mật khẩu (Bổ sung)</string>
<string name="server_lab_security4">Tên người dùng (Bổ sung)</string>
<string name="server_lab_encryption">Mã h</string>
<string name="server_lab_flow">Kiểm soát lưu lượng</string>
<string name="server_lab_security3">Thuật toán mã hóa</string>
<string name="server_lab_id4">Mật khẩu (không bắt buộc)</string>
<string name="server_lab_security4">Tên người dùng (không bắt buộc)</string>
<string name="server_lab_encryption">Mã hóa</string>
<string name="server_lab_flow">Kiểm soát lưu lượng (flow)</string>
<string name="server_lab_reserved">Reserved (không bắt buộc)</string>
<string name="server_lab_local_address">Địa chỉ cục bộ (IPv4/IPv6, phân cách bằng dấu phẩy)</string>
<string name="server_lab_local_mtu">MTU (không bắt buộc, mặc định 1420)</string>
<string name="toast_success">Thành công!</string>
<string name="toast_failure">Đã xảy ra lỗi, vui lòng thử lại!</string>
<string name="toast_none_data">Không có gì ở đây</string>
@@ -72,7 +76,7 @@
<string name="toast_invalid_url">URL không hợp lệ hoặc trống!</string>
<string name="server_lab_need_inbound">Vui lòng đảm bảo cấu hình tùy chỉnh này không bị lỗi trước khi sử dụng!</string>
<string name="toast_malformed_josn">Cấu hình không hợp lệ!</string>
<string name="server_lab_request_host6">Host (SNI) (Bổ sung)</string>
<string name="server_lab_request_host6">Host (SNI) (không bắt buộc)</string>
<string name="toast_asset_copy_failed">Không thể sao chép tệp tin, hãy dùng trình quản lý tệp!</string>
<string name="menu_item_add_file">Thêm tệp</string>
<string name="menu_item_download_file">Tải xuống tệp tin</string>
@@ -82,7 +86,7 @@
<string name="menu_item_search">Tìm kiếm</string>
<string name="menu_item_select_all">Chọn tất cả</string>
<string name="msg_enter_keywords">Nhập từ khoá</string>
<string name="switch_bypass_apps_mode">Bỏ qua kết nối VPN</string>
<string name="switch_bypass_apps_mode">Chế độ Bypass</string>
<string name="menu_item_select_proxy_app">Tự động chọn ứng dụng Proxy</string>
<string name="msg_downloading_content">Đang tải xuống nội dung...</string>
<string name="menu_item_export_proxy_app">Xuất và sao chép</string>
@@ -93,54 +97,55 @@
<string name="title_settings">Cài đặt</string>
<string name="title_advanced">Cài đặt nâng cao</string>
<string name="title_vpn_settings">Cài đặt VPN</string>
<string name="title_pref_per_app_proxy">Proxy ng dụng</string>
<string name="summary_pref_per_app_proxy">- Chung: Ứng dụng đã chọn sẽ kết nối Proxy, chưa lựa chọn sẽ kết nối trực tiếp. \n- Bỏ qua kết nối: Ứng dụng được chọn sẽ trực tiếp kết nối, không lựa chọn sẽ kết nối qua Proxy. \n- Lựa chọn để tự động chọn ứng dụng Proxy trong Menu.</string>
<string name="title_pref_per_app_proxy">Proxy Theo Ứng Dụng</string>
<string name="summary_pref_per_app_proxy">- Bình thường: Ứng dụng đã chọn sẽ kết nối thông qua Proxy, chưa chọn sẽ kết nối trực tiếp. \n- Chế độ Bypass: Ứng dụng đã chọn sẽ kết nối trực tiếp, chưa chọn sẽ kết nối qua Proxy. \n- Nếu bạn đang ở Trung Quốc thì vào Menu, chọn Tự động chọn ứng dụng Proxy.</string>
<string name="title_pref_mux_enabled">Cho phép Mux</string>
<string name="summary_pref_mux_enabled">Nhanh hơn nhưng có thể khiến kết nối không ổn định\nMux lưu lượng TCP với 8 kết nối mặc định, tùy chỉnh cách xử lý UDP và QUIC bên dưới</string>
<string name="title_pref_mux_xudp_concurency">Kết nối XUDP (phạm vi -1 đến 1024)</string>
<string name="title_pref_mux_xudp_quic">Xử lý QUIC trong đường hầm mux</string>
<string name="title_pref_mux_enabled">Bật Mux</string>
<string name="summary_pref_mux_enabled">Giảm độ trễ trong bước bắt tay của kết nối TCP. Mux phân phối dữ liệu từ nhiều kết nối TCP trên một kết nối TCP duy nhất. Không nên sử dụng Mux để xem video, download file hoặc chạy speedtest vì thường không hiệu quả.</string>
<string name="title_pref_mux_concurency">TCP connections (từ 1 đến 1024)</string>
<string name="title_pref_mux_xudp_concurency">XUDP connections (từ 1 đến 1024)</string>
<string name="title_pref_mux_xudp_quic">Handling of QUIC traffic in mux tunnel.</string>
<string-array name="mux_xudp_quic_entries">
<item>từ chối</item>
<item>cho phép</item>
<item>nhảy</item>
<item>Từ chối</item>
<item>Cho phép</item>
<item>Bỏ qua</item>
</string-array>
<string name="title_pref_speed_enabled">Cho phép hiển thị tốc độ mạng</string>
<string name="summary_pref_speed_enabled">Hiển thị tốc độ mạng hiện tại trên thanh thông báo.\nBiểu tượng trên thanh trạng thái có thể thay đổi tùy vào mức sử dụng.</string>
<string name="title_pref_speed_enabled">Bật Hiển thị tốc độ mạng</string>
<string name="summary_pref_speed_enabled">Hiển thị tốc độ mạng hiện tại trên thanh thông báo. \nBiểu tượng trên thanh trạng thái có thể thay đổi tùy vào mức sử dụng.</string>
<string name="title_pref_sniffing_enabled">Bật tính năng phát hiện luồng</string>
<string name="summary_pref_sniffing_enabled">Thử chuyển kết nối hiện tại của bạn qua trung gian để trung gian xử lý kết nối về lại cho bạn. (Mặc định là bật, hãy tắt nó nếu kết nối không ổn định)</string>
<string name="title_pref_sniffing_enabled">Bật Sniffing</string>
<string name="summary_pref_sniffing_enabled">Nhận diện tên miền từ gói tin để phục vụ định tuyến. \n(phải tắt để xài Zalo)</string>
<string name="title_pref_local_dns_enabled">Cho phép DNS cục bộ</string>
<string name="summary_pref_local_dns_enabled">DNS được xử lý bởi mô đun của lõi DNS. (Khuyến nghị, nếu cần định tuyến Bỏ qua mạng LAN và địa chỉ đất liền)</string>
<string name="title_pref_local_dns_enabled">Bật Local DNS</string>
<string name="summary_pref_local_dns_enabled">DNS được xử lý bởi mô-đun DNS của xray-core (dùng nếu cần định tuyến bypass cho mạng LAN và địa chỉ nội địa)</string>
<string name="title_pref_fake_dns_enabled">Cho phép DNS giả</string>
<string name="summary_pref_fake_dns_enabled">DNS cục bộ trả về địa chỉ IP giả. (Nhanh hơn, nhưng có thể không hoạt động với một số ứng dụng)</string>
<string name="title_pref_fake_dns_enabled">Bật FakeDNS</string>
<string name="summary_pref_fake_dns_enabled">FakeDNS lấy tên miền mục tiêu bằng cách giả mạo DNS (nhanh hơn, nhưng có thể không hoạt động cho một số ứng dụng)</string>
<string name="title_pref_prefer_ipv6">Ưu tiên IPv6</string>
<string name="summary_pref_prefer_ipv6">Ưu tiên sử dụng địa chỉ IPv6 cho kết nối và lộ trình.</string>
<string name="summary_pref_prefer_ipv6">Ưu tiên sử dụng địa chỉ IPv6 cho kết nối và định tuyến.</string>
<string name="title_pref_routing">Lộ trình</string>
<string name="title_pref_routing_domain_strategy">Tùy chọn tên miền</string>
<string name="title_pref_routing_mode">Tùy chỉnh quy tắc lộ trình</string>
<string name="title_pref_routing_custom">Tùy chỉnh lộ trình</string>
<string name="title_pref_routing">Định tuyến</string>
<string name="title_pref_routing_domain_strategy">Chiến lược tên miền (domainStrategy)</string>
<string name="title_pref_routing_mode">Quy tắc được định nghĩa trước</string>
<string name="title_pref_routing_custom">Quy tắc tùy chỉnh</string>
<string name="title_pref_remote_dns">Điều khiển DNS (Bổ sung)</string>
<string name="title_pref_remote_dns">DNS ngoại quốc (không bắt buộc)</string>
<string name="summary_pref_remote_dns">DNS</string>
<string name="title_pref_vpn_dns">VPN DNS (Chỉ IPv4/IPv6)</string>
<string name="title_pref_domestic_dns">DNS trong nước (Bổ sung)</string>
<string name="title_pref_domestic_dns">DNS nội địa (không bắt buộc)</string>
<string name="summary_pref_domestic_dns">DNS</string>
<string name="title_pref_proxy_sharing_enabled">Cho phép kết nối từ mạng LAN</string>
<string name="summary_pref_proxy_sharing_enabled">Các thiết bị khác có thể kết nối đến Proxy bởi địa chỉ IP thông qua Socks/HTTP, chỉ bật khi bạn tin tưởng kết nối đó.</string>
<string name="toast_warning_pref_proxysharing_short">Cho phép kết nối từ mạng LAN, đảm bảo rằng bạn tin tưởng kết nối hiện tại!</string>
<string name="summary_pref_proxy_sharing_enabled">Các thiết bị khác trong cùng mạng LAN có thể kết nối đến SOCKS/HTTP proxy trên thiết bị của bạn. \nChỉ bật tính năng này trong các mạng đáng tin cậy để tránh kết nối trái phép.</string>
<string name="toast_warning_pref_proxysharing_short">Đang bật cho phép kết nối từ mạng LAN</string>
<string name="title_pref_allow_insecure">Cho phép đặt lại Bỏ qua xác minh chứng chỉ</string>
<string name="summary_pref_allow_insecure">Khi kết nối TLS, đặt cài đặt Bỏ qua xác minh chứng chỉ thành mặc định.</string>
<string name="title_pref_allow_insecure">allowInsecure</string>
<string name="summary_pref_allow_insecure">Khi nhập những cấu hình có bảo mật TLS, mặc định sẽ không xác minh chứng chỉ (allowInsecure: true).</string>
<string name="title_pref_socks_port">Cổng Proxy SOCKS5</string>
<string name="summary_pref_socks_port">Cổng Proxy SOCKS5</string>
@@ -148,13 +153,13 @@
<string name="title_pref_http_port">Cổng Proxy HTTP</string>
<string name="summary_pref_http_port">Cổng Proxy HTTP</string>
<string name="title_pref_local_dns_port">Cổng DNS cục bộ</string>
<string name="summary_pref_local_dns_port">Cổng DNS cục bộ</string>
<string name="title_pref_local_dns_port">Cổng Local DNS</string>
<string name="summary_pref_local_dns_port">Cổng Local DNS</string>
<string name="title_pref_confirm_remove">Hiển thị thông báo xác nhận x cấu hình</string>
<string name="summary_pref_confirm_remove">Hiển thị thông báo xác nhận xoá cấu hình khi bạn xoá một cấu hình.</string>
<string name="title_pref_confirm_remove">Xác nhận xóa tệp cấu hình</string>
<string name="summary_pref_confirm_remove">Yêu cầu xác nhận từ người dùng khi thực hiện xóa tệp cấu hình.</string>
<string name="title_pref_start_scan_immediate">Bắt đầu quét mã QR ngay lập tức</string>
<string name="title_pref_start_scan_immediate">Quét mã QR ngay lập tức</string>
<string name="summary_pref_start_scan_immediate">Mở camera để quét QR ngay khi khởi động, nếu không bạn có thể chọn quét mã hoặc chọn ảnh trên thanh công cụ.</string>
<string name="title_pref_feedback">Phản hồi lỗi</string>
@@ -162,81 +167,86 @@
<string name="summary_pref_tg_group">Tham gia nhóm Telegram</string>
<string name="toast_tg_app_not_found">Không tìm thấy ứng dụng Telegram</string>
<string name="title_pref_promotion">Quảng cáo Server</string>
<string name="title_pref_promotion">Quảng bá server</string>
<string name="summary_pref_promotion">Quảng cáo, nhấn để biết thêm (Ủng hộ có thể được gỡ bỏ)</string>
<string name="title_core_loglevel">Mức độ nhật</string>
<string name="title_pref_auto_update_subscription">Tự động cập nhật các gói đăng</string>
<string name="summary_pref_auto_update_subscription">Tự động cập nhật các gói đăng ký của bạn ở trong nền với khoảng thời gian cố định. Tùy thiết bị, tính năng này có thể không luôn hoạt động đúng như mong đợi.</string>
<string name="title_pref_auto_update_interval">Thời gian Cập nhật tự động (Phút, Giá trị tối thiểu 15)</string>
<string name="title_core_loglevel">Log level</string>
<string name="title_mode">Chế độ kết nối</string>
<string name="title_mode_help">Nhấn vào đây nếu bạn cần trợ giúp!</string>
<string name="title_language">Ngôn ngữ</string>
<string name="title_ui_settings">Cài đặt UI</string>
<string name="title_ui_settings">Cài đặt giao diện</string>
<string name="title_logcat">Nhật ký hoạt động</string>
<string name="logcat_copy">Sao chép nhật ký</string>
<string name="logcat_clear">Xoá nhật ký</string>
<string name="title_service_restart">Kết nối lại v2rayNG</string>
<string name="title_logcat">Logcat</string>
<string name="logcat_copy">Sao chép</string>
<string name="logcat_clear">Xoá</string>
<string name="title_service_restart">Khởi động lại dịch vụ lõi</string>
<string name="title_del_all_config">Xoá tất cả cấu hình</string>
<string name="title_del_duplicate_config">Xoá cấu hình trùng lặp</string>
<string name="title_del_invalid_config">Xoá cấu hình lỗi (Kiểm tra trước)</string>
<string name="title_del_invalid_config">Xoá cấu hình lỗi</string>
<string name="title_export_all">Xuất và sao chép tất cả cấu hình</string>
<string name="title_sub_setting">Các gói đăng ký</string>
<string name="sub_setting_remarks">Tên các gói đăng ký</string>
<string name="sub_setting_remarks">Tên gói đăng ký</string>
<string name="sub_setting_url">URL gói đăng ký</string>
<string name="sub_setting_enable">Sử dụng gói đăng ký này</string>
<string name="sub_auto_update">Bật tự động cập nhật</string>
<string name="title_sub_update">Cập nhật các gói đăng ký</string>
<string name="title_ping_all_server">Ping tất cả máy chủ</string>
<string name="title_real_ping_all_server">Kiểm tra máy chủ</string>
<string name="title_real_ping_all_server">Test HTTP tất cả máy chủ</string>
<string name="title_user_asset_setting">Tệp Geo asset</string>
<string name="title_sort_by_test_results">Sắp xếp lại theo lần kiểm tra cuối cùng</string>
<string name="title_filter_config">Lọc cấu hình theo các gói đăng ký</string>
<string name="filter_config_all">Hiển thị tất cả các gói đăng ký</string>
<string name="title_del_duplicate_config_count">Xoá %d cấu hình trùng lặp</string>
<string name="tasker_start_service">Bắt đầu v2rayNG</string>
<string name="tasker_start_service">Khởi động v2rayNG</string>
<string name="tasker_setting_confirm">Xác nhận</string>
<string name="routing_settings_title">Cài đặt lộ trình</string>
<string name="routing_settings_tips">Được phân cách bằng dấu phẩy (,). Hãy nhớ nó để lưu lại!</string>
<string name="routing_settings_title">Cài đặt định tuyến</string>
<string name="routing_settings_tips">Phân cách bằng dấu phẩy (,). Có thể tải xuống rules mặc định để tham khảo ở menu ba chấm</string>
<string name="routing_settings_save">Lưu lại</string>
<string name="routing_settings_delete">Xoá</string>
<string name="routing_settings_scan_replace">Quét và thay thế</string>
<string name="routing_settings_scan_append">Quét và nối</string>
<string name="routing_settings_default_rules">Đặt luật cho lộ trình mặc định</string>
<string name="routing_settings_scan_replace">Quét QR và thay thế</string>
<string name="routing_settings_scan_append">Quét QR và nối thêm</string>
<string name="routing_settings_default_rules">Tải xuống rules mặc định cho China</string>
<string name="connection_test_pending">Kiểm tra kết nối</string>
<string name="connection_test_testing">Đang kiểm tra kết nối mạng...</string>
<string name="connection_test_available">Kiểm tra kết nối mạng thành công! Ping hiện tại là %d</string>
<string name="connection_test_available">Test thành công: truy cập www.Google.com mất %d ms</string>
<string name="connection_test_error">Lỗi kết nối mạng, hãy thử đổi cấu hình hoặc kiểm tra lại! Mã lỗi: %s</string>
<string name="connection_test_fail">Không có kết nối mạng!</string>
<string name="connection_test_error_status_code">Mã lỗi: #%d</string>
<string name="connection_connected">Đã kết nối, hãy nhấn vào đây để kiểm tra kết nối mạng!</string>
<string name="connection_not_connected">Chưa kết nối, hãy chọn một cấu hình để kết nối!</string>
<string name="connection_connected">Đã kết nối, nhấn vào đây để kiểm tra kết nối mạng!</string>
<string name="connection_not_connected">Chưa kết nối</string>
<string name="import_subscription_success">Đăng ký đã nhập thành công</string>
<string name="import_subscription_failure">Nhập đăng ký không thành công</string>
<string name="import_subscription_success">Nhập gói đăng ký thành công!</string>
<string name="import_subscription_failure">Nhập gói đăng ký không thành công!</string>
<string-array name="share_method">
<item>Xuất ra mã QR (Chụp màn hình để lưu)</item>
<item>Sao chép cấu hình này</item>
<item>Sao chép vào bảng nhớ tạm</item>
<item>Sao chép thành cấu hình tùy chỉnh</item>
</string-array>
<string-array name="share_sub_method">
<item>Xuất ra mã QR (Chụp màn hình để lưu)</item>
<item>Sao chép cấu hình này</item>
<item>Xuất gói ra mã QR (Chụp màn hình để lưu)</item>
<item>Xuất gói đăng ký vào bảng nhớ tạm</item>
</string-array>
<string-array name="routing_tag">
<item>Proxy URL hoặc IP</item>
<item>Direct URL hoặc IP</item>
<item>URL đã chặn hoặc IP</item>
<item>Proxy</item>
<item>Direct</item>
<item>Blocked</item>
</string-array>
<string-array name="routing_mode">
<item>Proxy toàn cầu</item>
<item>Bỏ qua địa chỉ LAN rồi Proxy</item>
<item>Bỏ qua địa chỉ đất liền rồi Proxy</item>
<item>Bỏ qua LAN và địa chỉ đất liền rồi Proxy</item>
<item>Bỏ qua địa chỉ nội địa rồi Proxy</item>
<item>Bỏ qua LAN và địa chỉ nội địa rồi Proxy</item>
<item>Kết nối trực tiếp toàn cầu</item>
</string-array>

View File

@@ -29,6 +29,7 @@
<string name="menu_item_import_config_manually_ss">手动输入[Shadowsocks]</string>
<string name="menu_item_import_config_manually_socks">手动输入[Socks]</string>
<string name="menu_item_import_config_manually_trojan">手动输入[Trojan]</string>
<string name="menu_item_import_config_manually_wireguard">手动输入[Wireguard]</string>
<string name="menu_item_import_config_custom">自定义配置</string>
<string name="menu_item_import_config_custom_clipboard">从剪贴板导入自定义配置</string>
<string name="menu_item_import_config_custom_local">从本地导入自定义配置</string>
@@ -58,6 +59,9 @@
<string name="server_lab_security4">用户名(可选)</string>
<string name="server_lab_encryption">加密方式(encryption)</string>
<string name="server_lab_flow">流控(flow)</string>
<string name="server_lab_reserved">Reserved(可选)</string>
<string name="server_lab_local_address">本地地址(可选IPv4/IPv6逗号隔开)</string>
<string name="server_lab_local_mtu">Mtu(可选, 默认1420)</string>
<string name="toast_success">成功</string>
<string name="toast_failure">失败</string>
<string name="toast_none_data">没有数据</string>
@@ -97,7 +101,8 @@
<string name="summary_pref_per_app_proxy">常规:勾选的App被代理,未勾选的直连;\n绕行模式:勾选的App直连,未勾选的被代理.\n不明白者在菜单中选择自动选中需代理应用</string>
<string name="title_pref_mux_enabled">启用 Mux 多路复用</string>
<string name="summary_pref_mux_enabled">减低延时,但可能会断流,建议不要启用。\nTCP 默认复用 8 个子链接UDP 及 QUIC 流量处理方式下方可选。</string>
<string name="summary_pref_mux_enabled">减低延时,但可能会断流,建议不要启用。\nTCPUDP 及 QUIC 流量处理方式下方可选。</string>
<string name="title_pref_mux_concurency">TCP 复用子链接数(可填 -1 至 1024</string>
<string name="title_pref_mux_xudp_concurency">XUDP 复用子链接数(可填 -1 至 1024</string>
<string name="title_pref_mux_xudp_quic">QUIC 流量处理方式</string>
<string-array name="mux_xudp_quic_entries">
@@ -160,13 +165,14 @@
<string name="summary_pref_feedback">反馈改进或漏洞至 GitHub</string>
<string name="summary_pref_tg_group">加入Telegram Group</string>
<string name="toast_tg_app_not_found">未找到Telegram app</string>
<string name="title_privacy_policy">隐私权政策</string>
<string name="title_pref_promotion">推广</string>
<string name="summary_pref_promotion">一些推广,点击查看详情(捐赠可去除)</string>
<string name="title_pref_auto_update_subscription">自动更新订阅</string>
<string name="summary_pref_auto_update_subscription">在后台按一定时间间隔自动更新您的订阅。受设备影响,此功能不一定总是有效</string>
<string name="title_pref_auto_update_interval">订阅自动更新间隔(分钟)</string>
<string name="title_pref_auto_update_interval">自动更新间隔(分钟最小值15</string>
<string name="title_core_loglevel">日志级别</string>
<string name="title_mode">模式</string>

View File

@@ -29,6 +29,7 @@
<string name="menu_item_import_config_manually_ss">手動鍵入 [Shadowsocks]</string>
<string name="menu_item_import_config_manually_socks">手動鍵入 [Socks]</string>
<string name="menu_item_import_config_manually_trojan">手動鍵入 [Trojan]</string>
<string name="menu_item_import_config_manually_wireguard">手動鍵入 [Wireguard]</string>
<string name="menu_item_import_config_custom">自訂組態</string>
<string name="menu_item_import_config_custom_clipboard">從剪貼簿匯入自訂組態</string>
<string name="menu_item_import_config_custom_local">從本地匯入自訂組態</string>
@@ -58,6 +59,9 @@
<string name="server_lab_security4">使用者名稱 (可選)</string>
<string name="server_lab_encryption">加密 (encryption)</string>
<string name="server_lab_flow">流程 (flow)</string>
<string name="server_lab_reserved">Reserved (可選)</string>
<string name="server_lab_local_address">本機位址(可選IPv4/IPv6逗號隔開)</string>
<string name="server_lab_local_mtu">Mtu(可選, 預設1420)</string>
<string name="toast_success">成功</string>
<string name="toast_failure">失敗</string>
<string name="toast_none_data">無資料</string>
@@ -97,7 +101,8 @@
<string name="summary_pref_per_app_proxy">常規:勾選的 App 啟用 Proxy未勾選的直接連線\n繞行模式勾選的 App 直接連線,未勾選的啟用 Proxy。\n可在選單中選擇自動選中需 Proxy 應用</string>
<string name="title_pref_mux_enabled">啟用 Mux 多路復用</string>
<string name="summary_pref_mux_enabled">減低延時 但可能會斷流\nTCP 默認復用 8 個子鏈接UDP 及 QUIC 流量處理方式下方可選</string>
<string name="summary_pref_mux_enabled">減低延時 但可能會斷流\nTCPUDP 及 QUIC 流量處理方式下方可選</string>
<string name="title_pref_mux_concurency">TCP 復用子鏈接數(可填 -1 至 1024</string>
<string name="title_pref_mux_xudp_concurency">XUDP 復用子鏈接數(可填 -1 至 1024</string>
<string name="title_pref_mux_xudp_quic">QUIC 流量處理方式</string>
<string-array name="mux_xudp_quic_entries">
@@ -160,13 +165,14 @@
<string name="summary_pref_feedback">前往 GitHub 回報錯誤</string>
<string name="summary_pref_tg_group">加入 Telegram 群組</string>
<string name="toast_tg_app_not_found">未找到 Telegram 應用程式</string>
<string name="title_privacy_policy">隱私權政策</string>
<string name="title_pref_promotion">推廣</string>
<string name="summary_pref_promotion">一些推廣,輕觸以檢視 (捐贈可去除)</string>
<string name="title_pref_auto_update_subscription">自動更新訂閱</string>
<string name="summary_pref_auto_update_subscription">在後台以一定時間間隔自動更新您的訂閱。受設備影響,此功能不一定總是有效</string>
<string name="title_pref_auto_update_interval">訂閱自動更新間隔(分鐘)</string>
<string name="title_pref_auto_update_interval">自動更新間隔(分鐘最小值15</string>
<string name="title_core_loglevel">記錄層級</string>
<string name="title_mode">模式</string>

View File

@@ -76,8 +76,11 @@
<string-array name="streamsecurity_alpn" translatable="false">
<item></item>
<item>h3</item>
<item>h2</item>
<item>http/1.1</item>
<item>h3,h2,http/1.1</item>
<item>h3,h2</item>
<item>h2,http/1.1</item>
</string-array>

View File

@@ -15,4 +15,5 @@
<color name="colorAccent">#000000</color>
<color name="colorBg">#FFFFFF</color>
<color name="colorText">#000000</color>
<color name="colorMainTestBg">#F0F1F6</color>
</resources>

View File

@@ -30,6 +30,7 @@
<string name="menu_item_import_config_manually_ss">Type manually[Shadowsocks]</string>
<string name="menu_item_import_config_manually_socks">Type manually[Socks]</string>
<string name="menu_item_import_config_manually_trojan">Type manually[Trojan]</string>
<string name="menu_item_import_config_manually_wireguard">Type manually[Wireguard]</string>
<string name="menu_item_import_config_custom">Custom config</string>
<string name="menu_item_import_config_custom_clipboard">Import custom config from Clipboard</string>
<string name="menu_item_import_config_custom_local">Import custom config from locally</string>
@@ -64,6 +65,10 @@
<string name="server_lab_public_key" translatable="false">PublicKey</string>
<string name="server_lab_short_id" translatable="false">ShortId</string>
<string name="server_lab_spider_x" translatable="false">SpiderX</string>
<string name="server_lab_secret_key" translatable="false">SecretKey</string>
<string name="server_lab_reserved">Reserved(Optional)</string>
<string name="server_lab_local_address">Local address (optional IPv4/IPv6, separated by commas)</string>
<string name="server_lab_local_mtu">Mtu(optional, default 1420)</string>
<string name="toast_success">Success</string>
<string name="toast_failure">Failure</string>
<string name="toast_none_data">There is nothing</string>
@@ -103,7 +108,8 @@
<string name="summary_pref_per_app_proxy">General: Checked App is proxy, unchecked direct connection; \nbypass mode: checked app directly connected, unchecked proxy. \nThe option to automatically select the proxy application in the menu</string>
<string name="title_pref_mux_enabled">Enable Mux</string>
<string name="summary_pref_mux_enabled">Faster, but it may cause unstable connectivity\nTCP traffic mux with default 8 connectionscustomize how to handle UDP and QUIC below</string>
<string name="summary_pref_mux_enabled">Faster, but it may cause unstable connectivity\ncustomize how to handle TCP, UDP and QUIC below</string>
<string name="title_pref_mux_concurency">TCP connectionsrange -1 to 1024</string>
<string name="title_pref_mux_xudp_concurency">XUDP connectionsrange -1 to 1024</string>
<string name="title_pref_mux_xudp_quic">Handling of QUIC in mux tunnel</string>
<string-array name="mux_xudp_quic_entries">
@@ -168,13 +174,14 @@
<string name="summary_pref_feedback">Feedback enhancements or bugs to GitHub</string>
<string name="summary_pref_tg_group">Join Telegram Group</string>
<string name="toast_tg_app_not_found">Telegram app not found</string>
<string name="title_privacy_policy">Privacy policy</string>
<string name="title_pref_promotion">Promotion</string>
<string name="summary_pref_promotion">Promotion,click for details(Donation can be removed)</string>
<string name="title_pref_auto_update_subscription">Automatic update subscriptions</string>
<string name="summary_pref_auto_update_subscription">Update your subscriptions automatically with an interval in background. Depending on the device, this feature may not always work</string>
<string name="title_pref_auto_update_interval">Subscription Auto Update Interval (Minutes)</string>
<string name="title_pref_auto_update_interval">Auto Update Interval (Minutes, Min value 15)</string>
<string name="title_core_loglevel">Log Level</string>
<string name="title_mode">Mode</string>

View File

@@ -12,6 +12,12 @@
android:summary="@string/summary_pref_mux_enabled"
android:title="@string/title_pref_mux_enabled" />
<EditTextPreference
android:key="pref_mux_concurency"
android:summary="8"
android:inputType="number"
android:title="@string/title_pref_mux_concurency" />
<EditTextPreference
android:key="pref_mux_xudp_concurency"
android:summary="8"

View File

@@ -1,7 +1,7 @@
buildToolsVer=33.0.2
compileSdkVer=33
targetSdkVer=33
buildToolsVer=34.0.0
compileSdkVer=34
targetSdkVer=34
kotlin.incremental=true
android.useAndroidX=true
android.enableJetifier=true