Compare commits
215 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b9728dc84 | ||
|
|
1ce9b7c0c8 | ||
|
|
ff75d3fdc2 | ||
|
|
8154812570 | ||
|
|
1dcd2478fc | ||
|
|
0515806e92 | ||
|
|
f286506ba4 | ||
|
|
48be736275 | ||
|
|
af0faeab16 | ||
|
|
fb6edee842 | ||
|
|
1209c4b92a | ||
|
|
e4e668b492 | ||
|
|
92d3136a23 | ||
|
|
f6e74ddb45 | ||
|
|
c34f332771 | ||
|
|
eb1d71bda6 | ||
|
|
3cdd5822cb | ||
|
|
8f924b5ce1 | ||
|
|
2ae645dce5 | ||
|
|
f848a7119f | ||
|
|
2fa41db32f | ||
|
|
9af4516761 | ||
|
|
189b07124d | ||
|
|
e6e5cdcdcd | ||
|
|
a31e4dab85 | ||
|
|
b2a9397dc7 | ||
|
|
a536df1a02 | ||
|
|
1a2751563d | ||
|
|
2dc82b7e83 | ||
|
|
b02d81ae53 | ||
|
|
773bcc5658 | ||
|
|
9142b9cfb4 | ||
|
|
376882d975 | ||
|
|
f330c32ccc | ||
|
|
58dbb71b53 | ||
|
|
ebd0257f6e | ||
|
|
945c584fc6 | ||
|
|
19cd24c37a | ||
|
|
3994810c4e | ||
|
|
d86e68c77a | ||
|
|
cc7bdefe54 | ||
|
|
51b32a030a | ||
|
|
6c76ddd145 | ||
|
|
0d12cc5dc8 | ||
|
|
cfb756723c | ||
|
|
0e9f198341 | ||
|
|
1fa1325630 | ||
|
|
dc79d3a897 | ||
|
|
486f3ffc96 | ||
|
|
bf030e12f5 | ||
|
|
e80cce9696 | ||
|
|
5bb9ecce47 | ||
|
|
5eb09aa54e | ||
|
|
ff261d9939 | ||
|
|
1624ec87b2 | ||
|
|
f1062f2f45 | ||
|
|
39341c27bc | ||
|
|
31a90cec2b | ||
|
|
617fc63393 | ||
|
|
802f2cf3eb | ||
|
|
c7efcde868 | ||
|
|
e858179204 | ||
|
|
6871b0b950 | ||
|
|
e9a27a1585 | ||
|
|
976b765629 | ||
|
|
126b9b6516 | ||
|
|
94dab02b54 | ||
|
|
a893b87730 | ||
|
|
7db2ddd1f7 | ||
|
|
748980aa1a | ||
|
|
96d416066e | ||
|
|
3955bb16bc | ||
|
|
de9bbf842f | ||
|
|
336b673746 | ||
|
|
f1b6b1e871 | ||
|
|
fba4c03bb5 | ||
|
|
a32ae5b53f | ||
|
|
589e0f38fd | ||
|
|
e21116680e | ||
|
|
e1960f5aff | ||
|
|
327ba57088 | ||
|
|
04e1b024e2 | ||
|
|
b59fe9b57b | ||
|
|
fab0b756de | ||
|
|
aa2727d0d0 | ||
|
|
ce3dd73a81 | ||
|
|
2eab209fea | ||
|
|
02476657bf | ||
|
|
35602120e8 | ||
|
|
9e800e08ab | ||
|
|
09fc20794e | ||
|
|
3cd95fbfdb | ||
|
|
c243fadacf | ||
|
|
9c06412ceb | ||
|
|
8d1f0d5df9 | ||
|
|
a035f42008 | ||
|
|
838fb041aa | ||
|
|
7fbab63227 | ||
|
|
ad871723fe | ||
|
|
8887b44bf7 | ||
|
|
176beced3a | ||
|
|
a3561ddc6c | ||
|
|
097bd06021 | ||
|
|
dfdd1efcc8 | ||
|
|
1ac5a410d4 | ||
|
|
8c44e849c9 | ||
|
|
2684bd2af4 | ||
|
|
01a860aab5 | ||
|
|
e1faf4e54e | ||
|
|
14dd4d6b99 | ||
|
|
bab21bc8a5 | ||
|
|
8393b3ce86 | ||
|
|
e15eec9cff | ||
|
|
32741ed7ab | ||
|
|
0d77a65bbb | ||
|
|
6eaac2d7e9 | ||
|
|
496a0ec92c | ||
|
|
826329e996 | ||
|
|
4f57da4a38 | ||
|
|
b3f49d0a34 | ||
|
|
b78b370408 | ||
|
|
82afcdddd0 | ||
|
|
a33a698f38 | ||
|
|
3aff1800cd | ||
|
|
ea816ca981 | ||
|
|
a2bace4ede | ||
|
|
5589d1058b | ||
|
|
a12fc32ff0 | ||
|
|
ce2d1c5e0d | ||
|
|
eb75666c85 | ||
|
|
4b970cedcc | ||
|
|
fbd9d92f5e | ||
|
|
cdaff4da06 | ||
|
|
fbb17390f2 | ||
|
|
52273db482 | ||
|
|
a6af25ae88 | ||
|
|
3ac89d68fb | ||
|
|
267a43fd97 | ||
|
|
6d0384b6f1 | ||
|
|
7ae4be402f | ||
|
|
46f0b7be5b | ||
|
|
b253f2d947 | ||
|
|
444ade8afe | ||
|
|
522dbdd170 | ||
|
|
d89238e1aa | ||
|
|
52710020ea | ||
|
|
a18b8f4f2b | ||
|
|
01e0a6570d | ||
|
|
cd6ef8f062 | ||
|
|
ea383c43bd | ||
|
|
68e08e3866 | ||
|
|
329ed26e85 | ||
|
|
c830a4cea4 | ||
|
|
e7e088ec83 | ||
|
|
cf6c814eb4 | ||
|
|
8f62e42ae8 | ||
|
|
89b4060ba2 | ||
|
|
e4d4b329a0 | ||
|
|
09c5f41995 | ||
|
|
02b25787c1 | ||
|
|
cdb050b6c5 | ||
|
|
21f47b536d | ||
|
|
f14484c986 | ||
|
|
358dd78dc3 | ||
|
|
d79e072316 | ||
|
|
c24dee567a | ||
|
|
9f75bafcea | ||
|
|
2150452629 | ||
|
|
507a32a08e | ||
|
|
44dee8e850 | ||
|
|
5e4d9246c2 | ||
|
|
9fe7419467 | ||
|
|
41b2251dfe | ||
|
|
73fad43573 | ||
|
|
4676717582 | ||
|
|
834766e6e7 | ||
|
|
e304dce347 | ||
|
|
61654aefeb | ||
|
|
1664aaa25b | ||
|
|
68f1f64f3d | ||
|
|
7bad57ca52 | ||
|
|
1c8e1f0993 | ||
|
|
6037ae6fc4 | ||
|
|
dc1c5400b8 | ||
|
|
9ae4688171 | ||
|
|
e3f39234b2 | ||
|
|
0df0b2d6ac | ||
|
|
9ce96d0591 | ||
|
|
a7ef0618ba | ||
|
|
cc9b083e5d | ||
|
|
5af322552c | ||
|
|
fc852281dd | ||
|
|
0b2f036a22 | ||
|
|
cf2becb5e9 | ||
|
|
82d8eba1b9 | ||
|
|
286ad34d94 | ||
|
|
ff0bc6594d | ||
|
|
7b8113aef1 | ||
|
|
81d2ef5db5 | ||
|
|
bdea3ef88c | ||
|
|
c62c86fc29 | ||
|
|
59f698f755 | ||
|
|
9ac979006e | ||
|
|
da6291a965 | ||
|
|
bf6555e57c | ||
|
|
35b114220e | ||
|
|
800bb6a4e9 | ||
|
|
c93edd8875 | ||
|
|
caa2edcf05 | ||
|
|
71bd684b46 | ||
|
|
02ae19f0c7 | ||
|
|
775fa5ea62 | ||
|
|
a5bb39ac8a | ||
|
|
073c7c0410 | ||
|
|
7e88e3ba4f |
64
.github/workflows/build.yml
vendored
Normal file
64
.github/workflows/build.yml
vendored
Normal 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@v4
|
||||
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '17'
|
||||
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@v5
|
||||
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@v3
|
||||
|
||||
|
||||
- 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@v4
|
||||
with:
|
||||
name: apk
|
||||
path: ${{ github.workspace }}/V2rayNG/app/build/outputs/apk/debug/
|
||||
41
CR.md
41
CR.md
@@ -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 天通过我们的可用渠道和应用内提示来通知用户。**在新条款生效后继续使用软件即表示您同意修改后的隐私条款。**
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'org.jetbrains.kotlin.android'
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion Integer.parseInt("$compileSdkVer")
|
||||
buildToolsVersion "$buildToolsVer"
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.v2ray.ang"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion Integer.parseInt("$targetSdkVer")
|
||||
multiDexEnabled true
|
||||
versionCode 526
|
||||
versionName "1.8.10"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
zipAlignEnabled false
|
||||
shrinkResources false
|
||||
ndk.abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
|
||||
}
|
||||
debug {
|
||||
minifyEnabled false
|
||||
zipAlignEnabled false
|
||||
shrinkResources false
|
||||
ndk.abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
|
||||
}
|
||||
}
|
||||
|
||||
// flavorDimensions "versions"
|
||||
//
|
||||
// productFlavors {
|
||||
// dev {
|
||||
// applicationIdSuffix = ".dev"
|
||||
// versionNameSuffix = "-dev"
|
||||
// }
|
||||
// pre_release {
|
||||
// applicationIdSuffix = ".pre"
|
||||
// versionNameSuffix = "-pre-release"
|
||||
// }
|
||||
// prod {
|
||||
// }
|
||||
// }
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
jniLibs.srcDirs = ['libs']
|
||||
java.srcDirs += 'src/main/kotlin'
|
||||
}
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
splits {
|
||||
abi {
|
||||
enable true
|
||||
reset()
|
||||
include 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a' //select ABIs to build APKs for
|
||||
universalApk true //generate an additional APK that contains all the ABIs
|
||||
}
|
||||
}
|
||||
|
||||
// map for the version code
|
||||
project.ext.versionCodes = ['armeabi-v7a': 1, 'arm64-v8a': 2, 'x86': 3, 'x86_64': 4]
|
||||
|
||||
android.applicationVariants.all { variant ->
|
||||
// assign different version code for each output
|
||||
variant.outputs.each { output ->
|
||||
output.outputFileName = "v2rayNG_" + variant.versionName + "_" + output.getFilter(com.android.build.OutputFile.ABI) + ".apk"
|
||||
|
||||
output.versionCodeOverride =
|
||||
project.ext.versionCodes.get(output.getFilter(com.android.build.OutputFile.ABI), 0) *
|
||||
1000000 + android.defaultConfig.versionCode
|
||||
}
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
viewBinding true
|
||||
buildConfig true
|
||||
}
|
||||
namespace 'com.v2ray.ang'
|
||||
testNamespace 'com.v2ray.angTest'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.aar', '*.jar'], exclude: [])
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
|
||||
// Androidx
|
||||
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 'androidx.cardview:cardview:1.0.0'
|
||||
implementation 'androidx.preference:preference-ktx:1.2.0'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.3.0'
|
||||
implementation 'androidx.fragment:fragment-ktx:1.5.7'
|
||||
implementation 'androidx.multidex:multidex:2.0.1'
|
||||
implementation 'androidx.viewpager2:viewpager2:1.1.0-beta01'
|
||||
|
||||
// Androidx ktx
|
||||
implementation 'androidx.activity:activity-ktx:1.7.1'
|
||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1'
|
||||
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.6.1'
|
||||
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1'
|
||||
|
||||
//kotlin
|
||||
implementation "org.jetbrains.kotlin:kotlin-reflect:1.8.0"
|
||||
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.google.code.gson:gson:2.10.1'
|
||||
implementation 'io.reactivex:rxjava:1.3.8'
|
||||
implementation 'io.reactivex:rxandroid:1.2.1'
|
||||
implementation 'com.tbruyelle.rxpermissions:rxpermissions:0.9.4@aar'
|
||||
implementation 'com.github.jorgecastilloprz:fabprogresscircle:1.01@aar'
|
||||
implementation 'me.drakeet.support:toastcompat:1.1.0'
|
||||
implementation 'com.blacksquircle.ui:editorkit:2.8.0'
|
||||
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'
|
||||
|
||||
def work_version = "2.8.1"
|
||||
|
||||
implementation "androidx.work:work-runtime-ktx:$work_version"
|
||||
implementation "androidx.work:work-multiprocess:$work_version"
|
||||
}
|
||||
124
V2rayNG/app/build.gradle.kts
Normal file
124
V2rayNG/app/build.gradle.kts
Normal file
@@ -0,0 +1,124 @@
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.v2ray.ang"
|
||||
compileSdk = 34
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "com.v2ray.ang"
|
||||
minSdk = 21
|
||||
targetSdk = 34
|
||||
versionCode = 552
|
||||
versionName = "1.8.19"
|
||||
multiDexEnabled = true
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
|
||||
}
|
||||
debug {
|
||||
isMinifyEnabled = false
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
getByName("main") {
|
||||
jniLibs.srcDirs("libs")
|
||||
}
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_1_8.toString()
|
||||
}
|
||||
|
||||
splits {
|
||||
abi {
|
||||
isEnable = true
|
||||
isUniversalApk = true
|
||||
}
|
||||
}
|
||||
|
||||
applicationVariants.all {
|
||||
val variant = this
|
||||
val versionCodes =
|
||||
mapOf("armeabi-v7a" to 1, "arm64-v8a" to 2, "x86" to 3, "x86_64" to 4)
|
||||
|
||||
variant.outputs
|
||||
.map { it as com.android.build.gradle.internal.api.ApkVariantOutputImpl }
|
||||
.forEach { output ->
|
||||
val abi = if (output.getFilter("ABI") != null)
|
||||
output.getFilter("ABI")
|
||||
else
|
||||
"all"
|
||||
|
||||
output.outputFileName = "v2rayNG_${variant.versionName}_${abi}.apk"
|
||||
if(versionCodes.containsKey(abi))
|
||||
{
|
||||
output.versionCodeOverride = (1000000 * versionCodes[abi]!!).plus(variant.versionCode)
|
||||
}
|
||||
else
|
||||
{
|
||||
return@forEach
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
viewBinding = true
|
||||
buildConfig = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.aar","*.jar"))))
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
|
||||
// Androidx
|
||||
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.11.0")
|
||||
implementation("androidx.cardview:cardview:1.0.0")
|
||||
implementation("androidx.preference:preference-ktx:1.2.1")
|
||||
implementation("androidx.recyclerview:recyclerview:1.3.2")
|
||||
implementation("androidx.fragment:fragment-ktx:1.6.2")
|
||||
implementation("androidx.multidex:multidex:2.0.1")
|
||||
implementation("androidx.viewpager2:viewpager2:1.1.0-beta02")
|
||||
|
||||
// Androidx ktx
|
||||
implementation("androidx.activity:activity-ktx:1.8.2")
|
||||
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0")
|
||||
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.7.0")
|
||||
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.7.0")
|
||||
|
||||
//kotlin
|
||||
implementation("org.jetbrains.kotlin:kotlin-reflect:1.9.23")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0")
|
||||
|
||||
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")
|
||||
implementation("com.tbruyelle.rxpermissions:rxpermissions:0.9.4@aar")
|
||||
implementation("com.github.jorgecastilloprz:fabprogresscircle:1.01@aar")
|
||||
implementation("me.drakeet.support:toastcompat:1.1.0")
|
||||
implementation("com.blacksquircle.ui:editorkit:2.9.0")
|
||||
implementation("com.blacksquircle.ui:language-base:2.9.0")
|
||||
implementation("com.blacksquircle.ui:language-json:2.9.0")
|
||||
implementation("io.github.g00fy2.quickie:quickie-bundled:1.9.0")
|
||||
implementation("com.google.zxing:core:3.5.3")
|
||||
|
||||
implementation("androidx.work:work-runtime-ktx:2.8.1")
|
||||
implementation("androidx.work:work-multiprocess:2.8.1")
|
||||
}
|
||||
@@ -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" />
|
||||
@@ -89,6 +93,9 @@
|
||||
<activity
|
||||
android:exported="false"
|
||||
android:name=".ui.UserAssetActivity" />
|
||||
<activity
|
||||
android:exported="false"
|
||||
android:name=".ui.UserAssetUrlActivity" />
|
||||
|
||||
<activity
|
||||
android:exported="false"
|
||||
@@ -127,6 +134,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 +142,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 +181,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
|
||||
|
||||
@@ -41,11 +41,17 @@ 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"
|
||||
const val PREF_FRAGMENT_ENABLED = "pref_fragment_enabled"
|
||||
const val PREF_FRAGMENT_PACKETS = "pref_fragment_packets"
|
||||
const val PREF_FRAGMENT_LENGTH = "pref_fragment_length"
|
||||
const val PREF_FRAGMENT_INTERVAL = "pref_fragment_interval"
|
||||
|
||||
const val HTTP_PROTOCOL: String = "http://"
|
||||
const val HTTPS_PROTOCOL: String = "https://"
|
||||
const val PROTOCOL_HTTP: String = "http://"
|
||||
const val PROTOCOL_HTTPS: String = "https://"
|
||||
const val PROTOCOL_FREEDOM: String = "freedom"
|
||||
|
||||
const val BROADCAST_ACTION_SERVICE = "com.v2ray.ang.action.service"
|
||||
const val BROADCAST_ACTION_ACTIVITY = "com.v2ray.ang.action.activity"
|
||||
@@ -60,13 +66,17 @@ object AppConfig {
|
||||
const val TAG_AGENT = "proxy"
|
||||
const val TAG_DIRECT = "direct"
|
||||
const val TAG_BLOCKED = "block"
|
||||
const val TAG_FRAGMENT = "fragment"
|
||||
|
||||
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 +84,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 +110,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"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.v2ray.ang.dto
|
||||
|
||||
data class AssetUrlItem(
|
||||
var remarks: String = "",
|
||||
var url: String = "",
|
||||
val addedTime: Long = System.currentTimeMillis(),
|
||||
var lastUpdated: Long = -1
|
||||
)
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,4 +8,5 @@ data class SubscriptionItem(
|
||||
var lastUpdated: Long = -1,
|
||||
var autoUpdate: Boolean = false,
|
||||
val updateInterval: Int? = null,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import com.google.gson.reflect.TypeToken
|
||||
import java.lang.reflect.Type
|
||||
|
||||
data class V2rayConfig(
|
||||
var remarks: String? = null,
|
||||
var stats: Any? = null,
|
||||
val log: LogBean,
|
||||
var policy: PolicyBean?,
|
||||
@@ -21,7 +22,9 @@ data class V2rayConfig(
|
||||
val transport: Any? = null,
|
||||
val reverse: Any? = null,
|
||||
var fakedns: Any? = null,
|
||||
val browserForwarder: Any? = null) {
|
||||
val browserForwarder: Any? = null,
|
||||
var observatory: Any? = null,
|
||||
var burstObservatory: Any? = null) {
|
||||
companion object {
|
||||
const val DEFAULT_PORT = 443
|
||||
const val DEFAULT_SECURITY = "auto"
|
||||
@@ -60,21 +63,22 @@ data class V2rayConfig(
|
||||
val metadataOnly: Boolean? = null)
|
||||
}
|
||||
|
||||
data class OutboundBean(val tag: String = "proxy",
|
||||
data class OutboundBean(var tag: String = "proxy",
|
||||
var protocol: String,
|
||||
var settings: OutSettingsBean? = null,
|
||||
var streamSettings: StreamSettingsBean? = null,
|
||||
val proxySettings: Any? = null,
|
||||
val sendThrough: String? = null,
|
||||
val mux: MuxBean? = MuxBean(false)) {
|
||||
var mux: MuxBean? = MuxBean(false)) {
|
||||
|
||||
data class OutSettingsBean(var vnext: List<VnextBean>? = null,
|
||||
var fragment: FragmentBean? = null,
|
||||
var servers: List<ServersBean>? = null,
|
||||
/*Blackhole*/
|
||||
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 +87,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 = "",
|
||||
@@ -99,6 +105,10 @@ data class V2rayConfig(
|
||||
var flow: String = "")
|
||||
}
|
||||
|
||||
data class FragmentBean(var packets: String? = null,
|
||||
var length: String? = null,
|
||||
var interval: String? = null)
|
||||
|
||||
data class ServersBean(var address: String = "",
|
||||
var method: String = "chacha20-poly1305",
|
||||
var ota: Boolean = false,
|
||||
@@ -127,13 +137,14 @@ data class V2rayConfig(
|
||||
var tcpSettings: TcpSettingsBean? = null,
|
||||
var kcpSettings: KcpSettingsBean? = null,
|
||||
var wsSettings: WsSettingsBean? = null,
|
||||
var httpupgradeSettings: HttpupgradeSettingsBean? = null,
|
||||
var httpSettings: HttpSettingsBean? = null,
|
||||
var tlsSettings: TlsSettingsBean? = null,
|
||||
var quicSettings: QuicSettingBean? = null,
|
||||
var realitySettings: TlsSettingsBean? = null,
|
||||
var grpcSettings: GrpcSettingsBean? = null,
|
||||
val dsSettings: Any? = null,
|
||||
val sockopt: Any? = null
|
||||
var sockopt: SockoptBean? = null
|
||||
) {
|
||||
|
||||
data class TcpSettingsBean(var header: HeaderBean = HeaderBean(),
|
||||
@@ -176,9 +187,20 @@ data class V2rayConfig(
|
||||
data class HeadersBean(var Host: String = "")
|
||||
}
|
||||
|
||||
data class HttpupgradeSettingsBean(var path: String = "",
|
||||
var host: String = "",
|
||||
val acceptProxyProtocol: Boolean? = null)
|
||||
|
||||
data class HttpSettingsBean(var host: List<String> = ArrayList(),
|
||||
var path: String = "")
|
||||
|
||||
data class SockoptBean(var TcpNoDelay: Boolean? = null,
|
||||
var tcpKeepAliveIdle: Int? = null,
|
||||
var tcpFastOpen: Boolean? = null,
|
||||
var tproxy: String? = null,
|
||||
var mark: Int? = null,
|
||||
var dialerProxy: String? = null)
|
||||
|
||||
data class TlsSettingsBean(var allowInsecure: Boolean = false,
|
||||
var serverName: String = "",
|
||||
val alpn: List<String>? = null,
|
||||
@@ -203,10 +225,12 @@ data class V2rayConfig(
|
||||
}
|
||||
|
||||
data class GrpcSettingsBean(var serviceName: String = "",
|
||||
var authority: String? = null,
|
||||
var multiMode: Boolean? = null)
|
||||
|
||||
fun populateTransportSettings(transport: String, headerType: String?, host: String?, path: String?, seed: String?,
|
||||
quicSecurity: String?, key: String?, mode: String?, serviceName: String?): String {
|
||||
quicSecurity: String?, key: String?, mode: String?, serviceName: String?,
|
||||
authority: String?): String {
|
||||
var sni = ""
|
||||
network = transport
|
||||
when (network) {
|
||||
@@ -244,6 +268,13 @@ data class V2rayConfig(
|
||||
wssetting.path = path ?: "/"
|
||||
wsSettings = wssetting
|
||||
}
|
||||
"httpupgrade" -> {
|
||||
val httpupgradeSetting = HttpupgradeSettingsBean()
|
||||
httpupgradeSetting.host = host ?: ""
|
||||
sni = httpupgradeSetting.host
|
||||
httpupgradeSetting.path = path ?: "/"
|
||||
httpupgradeSettings = httpupgradeSetting
|
||||
}
|
||||
"h2", "http" -> {
|
||||
network = "h2"
|
||||
val h2Setting = HttpSettingsBean()
|
||||
@@ -263,7 +294,8 @@ data class V2rayConfig(
|
||||
val grpcSetting = GrpcSettingsBean()
|
||||
grpcSetting.multiMode = mode == "multi"
|
||||
grpcSetting.serviceName = serviceName ?: ""
|
||||
sni = host ?: ""
|
||||
grpcSetting.authority = authority ?: ""
|
||||
sni = authority ?: ""
|
||||
grpcSettings = grpcSetting
|
||||
}
|
||||
}
|
||||
@@ -352,7 +384,8 @@ data class V2rayConfig(
|
||||
fun getTransportSettingDetails(): List<String>? {
|
||||
if (protocol.equals(EConfigType.VMESS.name, true)
|
||||
|| protocol.equals(EConfigType.VLESS.name, true)
|
||||
|| protocol.equals(EConfigType.TROJAN.name, true)) {
|
||||
|| protocol.equals(EConfigType.TROJAN.name, true)
|
||||
|| protocol.equals(EConfigType.SHADOWSOCKS.name, true)) {
|
||||
val transport = streamSettings?.network ?: return null
|
||||
return when (transport) {
|
||||
"tcp" -> {
|
||||
@@ -373,6 +406,12 @@ data class V2rayConfig(
|
||||
wsSetting.headers.Host,
|
||||
wsSetting.path)
|
||||
}
|
||||
"httpupgrade" -> {
|
||||
val httpupgradeSetting = streamSettings?.httpupgradeSettings ?: return null
|
||||
listOf("",
|
||||
httpupgradeSetting.host,
|
||||
httpupgradeSetting.path)
|
||||
}
|
||||
"h2" -> {
|
||||
val h2Setting = streamSettings?.httpSettings ?: return null
|
||||
listOf("",
|
||||
@@ -388,7 +427,7 @@ data class V2rayConfig(
|
||||
"grpc" -> {
|
||||
val grpcSetting = streamSettings?.grpcSettings ?: return null
|
||||
listOf(if (grpcSetting.multiMode == true) "multi" else "gun",
|
||||
"",
|
||||
grpcSetting.authority ?: "",
|
||||
grpcSetting.serviceName)
|
||||
}
|
||||
else -> null
|
||||
|
||||
@@ -9,72 +9,61 @@ import org.json.JSONObject
|
||||
import java.net.URI
|
||||
import java.net.URLConnection
|
||||
|
||||
/**
|
||||
* Some extensions
|
||||
*/
|
||||
|
||||
val Context.v2RayApplication: AngApplication
|
||||
get() = applicationContext as AngApplication
|
||||
|
||||
fun Context.toast(message: Int): Toast = ToastCompat
|
||||
.makeText(this, message, Toast.LENGTH_SHORT)
|
||||
.apply {
|
||||
show()
|
||||
}
|
||||
|
||||
fun Context.toast(message: CharSequence): Toast = ToastCompat
|
||||
.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) }
|
||||
|
||||
const val threshold = 1000
|
||||
const val divisor = 1024F
|
||||
|
||||
fun Long.toSpeedString() = toTrafficString() + "/s"
|
||||
|
||||
fun Long.toTrafficString(): String {
|
||||
if (this == 0L)
|
||||
return "\t\t\t0\t B"
|
||||
|
||||
if (this < threshold)
|
||||
return "${this.toFloat().toShortString()}\t B"
|
||||
|
||||
val kib = this / divisor
|
||||
if (kib < threshold)
|
||||
return "${kib.toShortString()}\t KB"
|
||||
|
||||
val mib = kib / divisor
|
||||
if (mib < threshold)
|
||||
return "${mib.toShortString()}\t MB"
|
||||
|
||||
val gib = mib / divisor
|
||||
if (gib < threshold)
|
||||
return "${gib.toShortString()}\t GB"
|
||||
|
||||
val tib = gib / divisor
|
||||
if (tib < threshold)
|
||||
return "${tib.toShortString()}\t TB"
|
||||
|
||||
val pib = tib / divisor
|
||||
if (pib < threshold)
|
||||
return "${pib.toShortString()}\t PB"
|
||||
|
||||
return "∞"
|
||||
fun Context.toast(message: Int) {
|
||||
ToastCompat.makeText(this, message, Toast.LENGTH_SHORT).apply { show() }
|
||||
}
|
||||
|
||||
private fun Float.toShortString(): String {
|
||||
val s = "%.2f".format(this)
|
||||
if (s.length <= 4)
|
||||
return s
|
||||
return s.substring(0, 4).removeSuffix(".")
|
||||
fun Context.toast(message: CharSequence) {
|
||||
ToastCompat.makeText(this, message, Toast.LENGTH_SHORT).apply { show() }
|
||||
}
|
||||
|
||||
fun JSONObject.putOpt(pair: Pair<String, Any?>) {
|
||||
put(pair.first, pair.second)
|
||||
}
|
||||
|
||||
fun JSONObject.putOpt(pairs: Map<String, Any?>) {
|
||||
pairs.forEach { put(it.key, it.value) }
|
||||
}
|
||||
|
||||
const val THRESHOLD = 1000L
|
||||
const val DIVISOR = 1024.0
|
||||
|
||||
fun Long.toSpeedString(): String = this.toTrafficString() + "/s"
|
||||
|
||||
fun Long.toTrafficString(): String {
|
||||
if (this < THRESHOLD) {
|
||||
return "$this B"
|
||||
}
|
||||
val kb = this / DIVISOR
|
||||
if (kb < THRESHOLD) {
|
||||
return "${String.format("%.1f KB", kb)}"
|
||||
}
|
||||
val mb = kb / DIVISOR
|
||||
if (mb < THRESHOLD) {
|
||||
return "${String.format("%.1f MB", mb)}"
|
||||
}
|
||||
val gb = mb / DIVISOR
|
||||
if (gb < THRESHOLD) {
|
||||
return "${String.format("%.1f GB", gb)}"
|
||||
}
|
||||
val tb = gb / DIVISOR
|
||||
if (tb < THRESHOLD) {
|
||||
return "${String.format("%.1f TB", tb)}"
|
||||
}
|
||||
return String.format("%.1f PB", tb / DIVISOR)
|
||||
}
|
||||
|
||||
val URLConnection.responseLength: Long
|
||||
get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) contentLengthLong else contentLength.toLong()
|
||||
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 = replace("\\s+".toRegex(), "")
|
||||
|
||||
@@ -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, "")
|
||||
}
|
||||
|
||||
|
||||
@@ -73,5 +73,8 @@ object SubscriptionUpdater {
|
||||
if (count <= 0) {
|
||||
AngConfigManager.importBatchConfig(Utils.decode(server!!), subid, append)
|
||||
}
|
||||
if (count <= 0) {
|
||||
AngConfigManager.appendCustomConfigServer(server, subid)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
@@ -364,7 +368,7 @@ object V2RayServiceManager {
|
||||
}
|
||||
val directUplink = v2rayPoint.queryStats(TAG_DIRECT, "uplink")
|
||||
val directDownlink = v2rayPoint.queryStats(TAG_DIRECT, "downlink")
|
||||
val zeroSpeed = (proxyTotal == 0L && directUplink == 0L && directDownlink == 0L)
|
||||
val zeroSpeed = proxyTotal == 0L && directUplink == 0L && directDownlink == 0L
|
||||
if (!zeroSpeed || !lastZeroSpeed) {
|
||||
if (proxyTotal == 0L) {
|
||||
appendSpeedString(text, outboundTags?.firstOrNull(), 0.0, 0.0)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -22,6 +22,7 @@ import androidx.appcompat.app.ActionBarDrawerToggle
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
@@ -69,7 +70,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
||||
binding.fab.setOnClickListener {
|
||||
if (mainViewModel.isRunning.value == true) {
|
||||
Utils.stopVService(this)
|
||||
} else if (settingsStorage?.decodeString(AppConfig.PREF_MODE) ?: "VPN" == "VPN") {
|
||||
} else if ((settingsStorage?.decodeString(AppConfig.PREF_MODE) ?: "VPN") == "VPN") {
|
||||
val intent = VpnService.prepare(this)
|
||||
if (intent == null) {
|
||||
startV2Ray()
|
||||
@@ -103,7 +104,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()
|
||||
@@ -117,6 +118,17 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
||||
toast(R.string.toast_permission_denied)
|
||||
}
|
||||
}
|
||||
|
||||
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
if (binding.drawerLayout.isDrawerOpen(GravityCompat.START)) {
|
||||
binding.drawerLayout.closeDrawer(GravityCompat.START)
|
||||
} else {
|
||||
//super.onBackPressed()
|
||||
onBackPressedDispatcher.onBackPressed()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun setupViewModel() {
|
||||
@@ -253,6 +265,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 +431,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()
|
||||
@@ -649,16 +668,6 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun onBackPressed() {
|
||||
if (binding.drawerLayout.isDrawerOpen(GravityCompat.START)) {
|
||||
binding.drawerLayout.closeDrawer(GravityCompat.START)
|
||||
} else {
|
||||
//super.onBackPressed()
|
||||
onBackPressedDispatcher.onBackPressed()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNavigationItemSelected(item: MenuItem): Boolean {
|
||||
// Handle navigation view item clicks here.
|
||||
when (item.itemId) {
|
||||
@@ -682,6 +691,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
|
||||
|
||||
@@ -66,7 +66,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
|
||||
holder.itemMainBinding.tvName.text = config.remarks
|
||||
holder.itemView.setBackgroundColor(Color.TRANSPARENT)
|
||||
holder.itemMainBinding.tvTestResult.text = aff?.getTestDelayString() ?: ""
|
||||
if ((aff?.testDelayMillis ?: 0L) < 0L) {
|
||||
if (aff?.testDelayMillis ?: 0L < 0L) {
|
||||
holder.itemMainBinding.tvTestResult.setTextColor(ContextCompat.getColor(mActivity, R.color.colorPingRed))
|
||||
} else {
|
||||
holder.itemMainBinding.tvTestResult.setTextColor(ContextCompat.getColor(mActivity, R.color.colorPing))
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
@@ -51,7 +50,7 @@ class PerAppProxyActivity : BaseActivity() {
|
||||
.map {
|
||||
if (blacklist != null) {
|
||||
it.forEach { one ->
|
||||
if ((blacklist.contains(one.packageName))) {
|
||||
if (blacklist.contains(one.packageName)) {
|
||||
one.isSelected = 1
|
||||
} else {
|
||||
one.isSelected = 0
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
@@ -45,7 +45,7 @@ class ScannerActivity : BaseActivity(){
|
||||
|
||||
private fun handleResult(result: QRResult) {
|
||||
if (result is QRResult.QRSuccess ) {
|
||||
finished(result.content.rawValue)
|
||||
finished(result.content.rawValue!!)
|
||||
} else {
|
||||
finish()
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -401,7 +504,8 @@ class ServerActivity : BaseActivity() {
|
||||
quicSecurity = requestHost,
|
||||
key = path,
|
||||
mode = transportTypes(networks[network])[type],
|
||||
serviceName = path
|
||||
serviceName = path,
|
||||
authority = requestHost,
|
||||
)
|
||||
if (sniField.isNotBlank()) {
|
||||
sni = sniField
|
||||
@@ -413,14 +517,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 +533,15 @@ class ServerActivity : BaseActivity() {
|
||||
"tcp" -> {
|
||||
tcpTypes
|
||||
}
|
||||
|
||||
"kcp", "quic" -> {
|
||||
kcpAndQuicTypes
|
||||
}
|
||||
|
||||
"grpc" -> {
|
||||
grpcModes
|
||||
}
|
||||
|
||||
else -> {
|
||||
arrayOf("---")
|
||||
}
|
||||
@@ -485,10 +592,12 @@ class ServerActivity : BaseActivity() {
|
||||
deleteServer()
|
||||
true
|
||||
}
|
||||
|
||||
R.id.save_config -> {
|
||||
saveServer()
|
||||
true
|
||||
}
|
||||
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ class ServerCustomConfigActivity : BaseActivity() {
|
||||
}
|
||||
|
||||
val config = MmkvManager.decodeServerConfig(editGuid) ?: ServerConfig.create(EConfigType.CUSTOM)
|
||||
config.remarks = binding.etRemarks.text.toString().trim()
|
||||
config.remarks = v2rayConfig.remarks ?: binding.etRemarks.text.toString().trim()
|
||||
config.fullConfig = v2rayConfig
|
||||
|
||||
MmkvManager.encodeServerConfig(editGuid, config)
|
||||
|
||||
@@ -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,9 +37,14 @@ 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) }
|
||||
|
||||
private val fragment by lazy { findPreference<CheckBoxPreference>(AppConfig.PREF_FRAGMENT_ENABLED) }
|
||||
private val fragmentPackets by lazy { findPreference<ListPreference>(AppConfig.PREF_FRAGMENT_PACKETS) }
|
||||
private val fragmentLength by lazy { findPreference<EditTextPreference>(AppConfig.PREF_FRAGMENT_LENGTH) }
|
||||
private val fragmentInterval by lazy { findPreference<EditTextPreference>(AppConfig.PREF_FRAGMENT_INTERVAL) }
|
||||
|
||||
// val autoRestart by lazy { findPreference(PREF_AUTO_RESTART) as CheckBoxPreference }
|
||||
private val remoteDns by lazy { findPreference<EditTextPreference>(AppConfig.PREF_REMOTE_DNS) }
|
||||
@@ -76,9 +79,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
|
||||
|
||||
// It must be greater than 15 minutes because WorkManager couldn't run tasks under 15 minutes intervals
|
||||
nval =
|
||||
if (TextUtils.isEmpty(nval) || nval.toLong() < 15) AppConfig.SUBSCRIPTION_DEFAULT_UPDATE_INTERVAL else nval
|
||||
autoUpdateInterval?.summary = nval
|
||||
configureUpdateTask(nval.toLong())
|
||||
true
|
||||
}
|
||||
@@ -158,10 +164,31 @@ 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
|
||||
}
|
||||
|
||||
fragment?.setOnPreferenceChangeListener { _, newValue ->
|
||||
updateFragment(newValue as Boolean)
|
||||
true
|
||||
}
|
||||
fragmentPackets?.setOnPreferenceChangeListener { _, newValue ->
|
||||
updateFragmentPackets(newValue as String)
|
||||
true
|
||||
}
|
||||
fragmentLength?.setOnPreferenceChangeListener { _, newValue ->
|
||||
updateFragmentLength(newValue as String)
|
||||
true
|
||||
}
|
||||
fragmentInterval?.setOnPreferenceChangeListener { _, newValue ->
|
||||
updateFragmentInterval(newValue as String)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
@@ -176,7 +203,12 @@ 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")
|
||||
updateFragment(defaultSharedPreferences.getBoolean(AppConfig.PREF_FRAGMENT_ENABLED, false))
|
||||
fragmentPackets?.summary = defaultSharedPreferences.getString(AppConfig.PREF_FRAGMENT_PACKETS, "tlshello")
|
||||
fragmentLength?.summary = defaultSharedPreferences.getString(AppConfig.PREF_FRAGMENT_LENGTH, "50-100")
|
||||
fragmentInterval?.summary = defaultSharedPreferences.getString(AppConfig.PREF_FRAGMENT_INTERVAL, "10-20")
|
||||
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 +286,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 {
|
||||
@@ -270,6 +312,27 @@ class SettingsActivity : BaseActivity() {
|
||||
muxXudpQuic?.isEnabled = concurrency >= 0
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateFragment(enabled: Boolean) {
|
||||
val defaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity())
|
||||
fragmentPackets?.isEnabled = enabled
|
||||
fragmentLength?.isEnabled = enabled
|
||||
fragmentInterval?.isEnabled = enabled
|
||||
if (enabled) {
|
||||
updateFragmentPackets(defaultSharedPreferences.getString(AppConfig.PREF_FRAGMENT_PACKETS, "tlshello"))
|
||||
updateFragmentLength(defaultSharedPreferences.getString(AppConfig.PREF_FRAGMENT_LENGTH, "50-100"))
|
||||
updateFragmentInterval(defaultSharedPreferences.getString(AppConfig.PREF_FRAGMENT_INTERVAL, "10-20"))
|
||||
}
|
||||
}
|
||||
private fun updateFragmentPackets(value: String?) {
|
||||
fragmentPackets?.summary = value.toString()
|
||||
}
|
||||
private fun updateFragmentLength(value: String?) {
|
||||
fragmentLength?.summary = value.toString()
|
||||
}
|
||||
private fun updateFragmentInterval(value: String?) {
|
||||
fragmentInterval?.summary = value.toString()
|
||||
}
|
||||
}
|
||||
|
||||
fun onModeHelpClicked(view: View) {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -25,8 +25,8 @@ class UrlSchemeActivity : BaseActivity() {
|
||||
if ("text/plain" == type) {
|
||||
intent.getStringExtra(Intent.EXTRA_TEXT)?.let {
|
||||
val uri = Uri.parse(it)
|
||||
if (uri.scheme?.startsWith(AppConfig.HTTPS_PROTOCOL) == true || uri.scheme?.startsWith(
|
||||
AppConfig.HTTP_PROTOCOL
|
||||
if (uri.scheme?.startsWith(AppConfig.PROTOCOL_HTTPS) == true || uri.scheme?.startsWith(
|
||||
AppConfig.PROTOCOL_HTTP
|
||||
) == true
|
||||
) {
|
||||
val name = uri.getQueryParameter("name") ?: "Subscription"
|
||||
|
||||
@@ -15,12 +15,14 @@ import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.gson.Gson
|
||||
import com.tbruyelle.rxpermissions.RxPermissions
|
||||
import com.tencent.mmkv.MMKV
|
||||
import com.v2ray.ang.AppConfig
|
||||
import com.v2ray.ang.R
|
||||
import com.v2ray.ang.databinding.ActivitySubSettingBinding
|
||||
import com.v2ray.ang.databinding.ItemRecyclerUserAssetBinding
|
||||
import com.v2ray.ang.dto.AssetUrlItem
|
||||
import com.v2ray.ang.extension.toTrafficString
|
||||
import com.v2ray.ang.extension.toast
|
||||
import com.v2ray.ang.util.MmkvManager
|
||||
@@ -39,9 +41,11 @@ import java.util.*
|
||||
class UserAssetActivity : BaseActivity() {
|
||||
private lateinit var binding: ActivitySubSettingBinding
|
||||
private val settingsStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SETTING, MMKV.MULTI_PROCESS_MODE) }
|
||||
private val assetStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_ASSET, MMKV.MULTI_PROCESS_MODE) }
|
||||
|
||||
val extDir by lazy { File(Utils.userAssetPath(this)) }
|
||||
val geofiles = arrayOf("geosite.dat", "geoip.dat")
|
||||
val builtInGeoFiles = arrayOf("geosite.dat", "geoip.dat")
|
||||
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@@ -55,6 +59,11 @@ class UserAssetActivity : BaseActivity() {
|
||||
binding.recyclerView.adapter = UserAssetAdapter()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
binding.recyclerView.adapter?.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
menuInflater.inflate(R.menu.menu_asset, menu)
|
||||
return super.onCreateOptionsMenu(menu)
|
||||
@@ -66,6 +75,11 @@ class UserAssetActivity : BaseActivity() {
|
||||
true
|
||||
}
|
||||
|
||||
R.id.add_url -> {
|
||||
val intent = Intent(this, UserAssetUrlActivity::class.java)
|
||||
startActivity(intent)
|
||||
true
|
||||
}
|
||||
R.id.download_file -> {
|
||||
downloadGeoFiles()
|
||||
true
|
||||
@@ -104,13 +118,27 @@ class UserAssetActivity : BaseActivity() {
|
||||
}
|
||||
|
||||
private val chooseFile =
|
||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { it ->
|
||||
val uri = it.data?.data
|
||||
if (it.resultCode == RESULT_OK && uri != null) {
|
||||
val assetId = Utils.getUuid()
|
||||
try {
|
||||
val assetItem = AssetUrlItem(
|
||||
getCursorName(uri) ?: uri.toString(),
|
||||
"file"
|
||||
)
|
||||
|
||||
// check remarks unique
|
||||
val assetList = MmkvManager.decodeAssetUrls()
|
||||
if (assetList.any { it.second.remarks == assetItem.remarks && it.first != assetId }) {
|
||||
toast(R.string.msg_remark_is_duplicate)
|
||||
return@registerForActivityResult
|
||||
}
|
||||
assetStorage?.encode(assetId, Gson().toJson(assetItem))
|
||||
copyFile(uri)
|
||||
} catch (e: Exception) {
|
||||
toast(R.string.toast_asset_copy_failed)
|
||||
MmkvManager.removeAssetUrl(assetId)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -143,31 +171,33 @@ class UserAssetActivity : BaseActivity() {
|
||||
val httpPort = Utils.parseInt(settingsStorage?.decodeString(AppConfig.PREF_HTTP_PORT), AppConfig.PORT_HTTP.toInt())
|
||||
|
||||
toast(R.string.msg_downloading_content)
|
||||
geofiles.forEach {
|
||||
var assets = MmkvManager.decodeAssetUrls()
|
||||
assets = addBuiltInGeoItems(assets)
|
||||
|
||||
assets.forEach {
|
||||
//toast(getString(R.string.msg_downloading_content) + it)
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val result = downloadGeo(it, 60000, httpPort)
|
||||
val result = downloadGeo(it.second, 60000, httpPort)
|
||||
launch(Dispatchers.Main) {
|
||||
if (result) {
|
||||
toast(getString(R.string.toast_success) + " " + it)
|
||||
toast(getString(R.string.toast_success) + " " + it.second.remarks)
|
||||
binding.recyclerView.adapter?.notifyDataSetChanged()
|
||||
} else {
|
||||
toast(getString(R.string.toast_failure) + " " + it)
|
||||
toast(getString(R.string.toast_failure) + " " + it.second.remarks)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun downloadGeo(name: String, timeout: Int, httpPort: Int): Boolean {
|
||||
val url = AppConfig.geoUrl + name
|
||||
val targetTemp = File(extDir, name + "_temp")
|
||||
val target = File(extDir, name)
|
||||
private fun downloadGeo(item: AssetUrlItem, timeout: Int, httpPort: Int): Boolean {
|
||||
val targetTemp = File(extDir, item.remarks + "_temp")
|
||||
val target = File(extDir, item.remarks)
|
||||
var conn: HttpURLConnection? = null
|
||||
//Log.d(AppConfig.ANG_PACKAGE, url)
|
||||
|
||||
try {
|
||||
conn = URL(url).openConnection(
|
||||
conn = URL(item.url).openConnection(
|
||||
Proxy(
|
||||
Proxy.Type.HTTP,
|
||||
InetSocketAddress("127.0.0.1", httpPort)
|
||||
@@ -192,33 +222,74 @@ class UserAssetActivity : BaseActivity() {
|
||||
conn?.disconnect()
|
||||
}
|
||||
}
|
||||
private fun addBuiltInGeoItems(assets: List<Pair<String, AssetUrlItem>>): List<Pair<String, AssetUrlItem>> {
|
||||
val list = mutableListOf<Pair<String, AssetUrlItem>>()
|
||||
builtInGeoFiles.forEach {
|
||||
list.add(Utils.getUuid() to AssetUrlItem(
|
||||
it,
|
||||
AppConfig.geoUrl + it
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return list + assets
|
||||
}
|
||||
|
||||
inner class UserAssetAdapter : RecyclerView.Adapter<UserAssetViewHolder>() {
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserAssetViewHolder {
|
||||
return UserAssetViewHolder(ItemRecyclerUserAssetBinding.inflate(LayoutInflater.from(parent.context), parent, false))
|
||||
return UserAssetViewHolder(
|
||||
ItemRecyclerUserAssetBinding.inflate(
|
||||
LayoutInflater.from(parent.context),
|
||||
parent,
|
||||
false)
|
||||
)
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onBindViewHolder(holder: UserAssetViewHolder, position: Int) {
|
||||
val file = extDir.listFiles()?.getOrNull(position) ?: return
|
||||
holder.itemUserAssetBinding.assetName.text = file.name
|
||||
val dateFormat = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM)
|
||||
holder.itemUserAssetBinding.assetProperties.text = "${file.length().toTrafficString()} • ${dateFormat.format(Date(file.lastModified()))}"
|
||||
if (file.name in geofiles) {
|
||||
var assets = MmkvManager.decodeAssetUrls();
|
||||
assets = addBuiltInGeoItems(assets);
|
||||
val item = assets.getOrNull(position) ?: return
|
||||
// file with name == item.second.remarks
|
||||
val file = extDir.listFiles()?.find { it.name == item.second.remarks }
|
||||
|
||||
holder.itemUserAssetBinding.assetName.text = item.second.remarks
|
||||
|
||||
if (file != null) {
|
||||
val dateFormat = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM)
|
||||
holder.itemUserAssetBinding.assetProperties.text =
|
||||
"${file.length().toTrafficString()} • ${dateFormat.format(Date(file.lastModified()))}"
|
||||
} else {
|
||||
holder.itemUserAssetBinding.assetProperties.text = getString(R.string.msg_file_not_found)
|
||||
}
|
||||
|
||||
if (item.second.remarks in builtInGeoFiles) {
|
||||
holder.itemUserAssetBinding.layoutEdit.visibility = GONE
|
||||
holder.itemUserAssetBinding.layoutRemove.visibility = GONE
|
||||
} else {
|
||||
holder.itemUserAssetBinding.layoutEdit.visibility = item.second.url.let { if (it == "file") GONE else VISIBLE }
|
||||
holder.itemUserAssetBinding.layoutRemove.visibility = VISIBLE
|
||||
}
|
||||
|
||||
holder.itemUserAssetBinding.layoutEdit.setOnClickListener {
|
||||
val intent = Intent(this@UserAssetActivity, UserAssetUrlActivity::class.java)
|
||||
intent.putExtra("assetId", item.first)
|
||||
startActivity(intent)
|
||||
}
|
||||
holder.itemUserAssetBinding.layoutRemove.setOnClickListener {
|
||||
file.delete()
|
||||
file?.delete()
|
||||
MmkvManager.removeAssetUrl(item.first)
|
||||
binding.recyclerView.adapter?.notifyItemRemoved(position)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return extDir.listFiles()?.size ?: 0
|
||||
var assets = MmkvManager.decodeAssetUrls();
|
||||
assets = addBuiltInGeoItems(assets);
|
||||
return assets.size
|
||||
}
|
||||
}
|
||||
|
||||
class UserAssetViewHolder(val itemUserAssetBinding: ItemRecyclerUserAssetBinding) : RecyclerView.ViewHolder(itemUserAssetBinding.root)
|
||||
class UserAssetViewHolder(val itemUserAssetBinding: ItemRecyclerUserAssetBinding) :
|
||||
RecyclerView.ViewHolder(itemUserAssetBinding.root)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
package com.v2ray.ang.ui
|
||||
|
||||
import android.os.Bundle
|
||||
import android.text.TextUtils
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.google.gson.Gson
|
||||
import com.tencent.mmkv.MMKV
|
||||
import com.v2ray.ang.R
|
||||
import com.v2ray.ang.databinding.ActivityUserAssetUrlBinding
|
||||
import com.v2ray.ang.dto.AssetUrlItem
|
||||
import com.v2ray.ang.extension.toast
|
||||
import com.v2ray.ang.util.MmkvManager
|
||||
import com.v2ray.ang.util.Utils
|
||||
import java.io.File
|
||||
|
||||
class UserAssetUrlActivity : BaseActivity() {
|
||||
private lateinit var binding: ActivityUserAssetUrlBinding
|
||||
|
||||
var del_config: MenuItem? = null
|
||||
var save_config: MenuItem? = null
|
||||
|
||||
val extDir by lazy { File(Utils.userAssetPath(this)) }
|
||||
private val assetStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_ASSET, MMKV.MULTI_PROCESS_MODE) }
|
||||
private val editAssetId by lazy { intent.getStringExtra("assetId").orEmpty() }
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityUserAssetUrlBinding.inflate(layoutInflater)
|
||||
val view = binding.root
|
||||
setContentView(view)
|
||||
title = getString(R.string.title_user_asset_add_url)
|
||||
|
||||
val json = assetStorage?.decodeString(editAssetId)
|
||||
if (!json.isNullOrBlank()) {
|
||||
bindingAsset(Gson().fromJson(json, AssetUrlItem::class.java))
|
||||
} else {
|
||||
clearAsset()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* bingding seleced asset config
|
||||
*/
|
||||
private fun bindingAsset(assetItem: AssetUrlItem): Boolean {
|
||||
binding.etRemarks.text = Utils.getEditable(assetItem.remarks)
|
||||
binding.etUrl.text = Utils.getEditable(assetItem.url)
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* clear or init asset config
|
||||
*/
|
||||
private fun clearAsset(): Boolean {
|
||||
binding.etRemarks.text = null
|
||||
binding.etUrl.text = null
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* save asset config
|
||||
*/
|
||||
private fun saveServer(): Boolean {
|
||||
val assetItem: AssetUrlItem
|
||||
val json = assetStorage?.decodeString(editAssetId)
|
||||
var assetId = editAssetId
|
||||
if (!json.isNullOrBlank()) {
|
||||
assetItem = Gson().fromJson(json, AssetUrlItem::class.java)
|
||||
|
||||
// remove file associated with the asset
|
||||
val file = extDir.resolve(assetItem.remarks)
|
||||
if (file.exists()) {
|
||||
file.delete()
|
||||
}
|
||||
} else {
|
||||
assetId = Utils.getUuid()
|
||||
assetItem = AssetUrlItem()
|
||||
}
|
||||
|
||||
assetItem.remarks = binding.etRemarks.text.toString()
|
||||
assetItem.url = binding.etUrl.text.toString()
|
||||
|
||||
// check remarks unique
|
||||
val assetList = MmkvManager.decodeAssetUrls()
|
||||
if (assetList.any { it.second.remarks == assetItem.remarks && it.first != assetId }) {
|
||||
toast(R.string.msg_remark_is_duplicate)
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
if (TextUtils.isEmpty(assetItem.remarks)) {
|
||||
toast(R.string.sub_setting_remarks)
|
||||
return false
|
||||
}
|
||||
if (TextUtils.isEmpty(assetItem.url)) {
|
||||
toast(R.string.title_url)
|
||||
return false
|
||||
}
|
||||
|
||||
assetStorage?.encode(assetId, Gson().toJson(assetItem))
|
||||
toast(R.string.toast_success)
|
||||
finish()
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* save server config
|
||||
*/
|
||||
private fun deleteServer(): Boolean {
|
||||
if (editAssetId.isNotEmpty()) {
|
||||
AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
MmkvManager.removeAssetUrl(editAssetId)
|
||||
finish()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
menuInflater.inflate(R.menu.action_server, menu)
|
||||
del_config = menu.findItem(R.id.del_config)
|
||||
save_config = menu.findItem(R.id.save_config)
|
||||
|
||||
if (editAssetId.isEmpty()) {
|
||||
del_config?.isVisible = false
|
||||
}
|
||||
|
||||
return super.onCreateOptionsMenu(menu)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
|
||||
R.id.del_config -> {
|
||||
deleteServer()
|
||||
true
|
||||
}
|
||||
R.id.save_config -> {
|
||||
saveServer()
|
||||
true
|
||||
}
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
@@ -7,11 +7,19 @@ import android.text.TextUtils
|
||||
import android.util.Log
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.JsonPrimitive
|
||||
import com.google.gson.JsonSerializationContext
|
||||
import com.google.gson.JsonSerializer
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.tencent.mmkv.MMKV
|
||||
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.PROTOCOL_HTTPS
|
||||
import com.v2ray.ang.AppConfig.PROTOCOL_HTTP
|
||||
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
|
||||
@@ -19,8 +27,9 @@ import com.v2ray.ang.dto.V2rayConfig.Companion.TLS
|
||||
import com.v2ray.ang.util.MmkvManager.KEY_SELECTED_SERVER
|
||||
import java.net.URI
|
||||
import java.util.*
|
||||
import java.lang.reflect.Type
|
||||
import com.v2ray.ang.extension.idnHost
|
||||
import com.v2ray.ang.extension.toast
|
||||
import com.v2ray.ang.extension.removeWhiteSpace
|
||||
|
||||
object AngConfigManager {
|
||||
private val mainStorage by lazy {
|
||||
@@ -167,7 +176,8 @@ object AngConfigManager {
|
||||
vmessBean.requestHost,
|
||||
vmessBean.path,
|
||||
vmessBean.headerType,
|
||||
vmessBean.path
|
||||
vmessBean.path,
|
||||
vmessBean.requestHost,
|
||||
)
|
||||
val allowInsecure = if (vmessBean.allowInsecure.isBlank()) {
|
||||
settingsStorage?.decodeBool(AppConfig.PREF_ALLOW_INSECURE) ?: false
|
||||
@@ -212,8 +222,8 @@ object AngConfigManager {
|
||||
}
|
||||
|
||||
//maybe sub
|
||||
if (TextUtils.isEmpty(subid) && (str.startsWith(HTTP_PROTOCOL) || str.startsWith(
|
||||
HTTPS_PROTOCOL
|
||||
if (TextUtils.isEmpty(subid) && (str.startsWith(PROTOCOL_HTTP) || str.startsWith(
|
||||
PROTOCOL_HTTPS
|
||||
))
|
||||
) {
|
||||
MmkvManager.importUrlAsSubscription(str)
|
||||
@@ -266,7 +276,8 @@ object AngConfigManager {
|
||||
vmessQRCode.host,
|
||||
vmessQRCode.path,
|
||||
vmessQRCode.type,
|
||||
vmessQRCode.path
|
||||
vmessQRCode.path,
|
||||
vmessQRCode.host
|
||||
)
|
||||
|
||||
val fingerprint = vmessQRCode.fp ?: streamSetting.tlsSettings?.fingerprint
|
||||
@@ -374,7 +385,8 @@ object AngConfigManager {
|
||||
queryParam["quicSecurity"],
|
||||
queryParam["key"],
|
||||
queryParam["mode"],
|
||||
queryParam["serviceName"]
|
||||
queryParam["serviceName"],
|
||||
queryParam["authority"]
|
||||
)
|
||||
fingerprint = queryParam["fp"] ?: ""
|
||||
config.outboundBean?.streamSettings?.populateTlsSettings(
|
||||
@@ -402,7 +414,6 @@ object AngConfigManager {
|
||||
.associate { it.split("=").let { (k, v) -> k to Utils.urlDecode(v) } }
|
||||
config = ServerConfig.create(EConfigType.VLESS)
|
||||
val streamSetting = config.outboundBean?.streamSettings ?: return -1
|
||||
var fingerprint = streamSetting.tlsSettings?.fingerprint
|
||||
|
||||
config.remarks = Utils.urlDecode(uri.fragment ?: "")
|
||||
config.outboundBean?.settings?.vnext?.get(0)?.let { vnext ->
|
||||
@@ -422,16 +433,41 @@ object AngConfigManager {
|
||||
queryParam["quicSecurity"],
|
||||
queryParam["key"],
|
||||
queryParam["mode"],
|
||||
queryParam["serviceName"]
|
||||
queryParam["serviceName"],
|
||||
queryParam["authority"]
|
||||
)
|
||||
fingerprint = queryParam["fp"] ?: ""
|
||||
val pbk = queryParam["pbk"] ?: ""
|
||||
val sid = queryParam["sid"] ?: ""
|
||||
val spx = Utils.urlDecode(queryParam["spx"] ?: "")
|
||||
streamSetting.populateTlsSettings(
|
||||
queryParam["security"] ?: "", allowInsecure,
|
||||
queryParam["sni"] ?: sni, fingerprint, queryParam["alpn"], pbk, sid, spx
|
||||
queryParam["security"] ?: "",
|
||||
allowInsecure,
|
||||
queryParam["sni"] ?: sni,
|
||||
queryParam["fp"] ?: "",
|
||||
queryParam["alpn"],
|
||||
queryParam["pbk"] ?: "",
|
||||
queryParam["sid"] ?: "",
|
||||
queryParam["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 +479,8 @@ object AngConfigManager {
|
||||
?.getServerAddress() == removedSelectedServer.getProxyOutbound()
|
||||
?.getServerAddress() &&
|
||||
config.getProxyOutbound()
|
||||
?.getServerPort() == removedSelectedServer.getProxyOutbound()?.getServerPort()
|
||||
?.getServerPort() == removedSelectedServer.getProxyOutbound()
|
||||
?.getServerPort()
|
||||
) {
|
||||
mainStorage?.encode(KEY_SELECTED_SERVER, guid)
|
||||
}
|
||||
@@ -460,7 +497,7 @@ object AngConfigManager {
|
||||
allowInsecure: Boolean
|
||||
): Boolean {
|
||||
return runCatching {
|
||||
val uri = URI(uriString)
|
||||
val uri = URI(Utils.fixIllegalUrl(uriString))
|
||||
check(uri.scheme == "vmess")
|
||||
val (_, protocol, tlsStr, uuid, alterId) =
|
||||
Regex("(tcp|http|ws|kcp|quic|grpc)(\\+tls)?:([0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12})")
|
||||
@@ -488,7 +525,8 @@ object AngConfigManager {
|
||||
queryParam["security"],
|
||||
queryParam["key"],
|
||||
queryParam["mode"],
|
||||
queryParam["serviceName"])
|
||||
queryParam["serviceName"],
|
||||
queryParam["authority"])
|
||||
streamSetting.populateTlsSettings(
|
||||
if (tls) TLS else "", allowInsecure, sni, fingerprint, null,
|
||||
null, null, null
|
||||
@@ -551,6 +589,42 @@ object AngConfigManager {
|
||||
password = base64Decode.substringAfter(":")
|
||||
}
|
||||
|
||||
val query = Utils.urlDecode(uri.query ?: "")
|
||||
if (query != "") {
|
||||
val queryPairs = HashMap<String, String>()
|
||||
val pairs = query.split(";")
|
||||
Log.d(AppConfig.ANG_PACKAGE, pairs.toString())
|
||||
for (pair in pairs) {
|
||||
val idx = pair.indexOf("=")
|
||||
if (idx == -1) {
|
||||
queryPairs[Utils.urlDecode(pair)] = "";
|
||||
} else {
|
||||
queryPairs[Utils.urlDecode(pair.substring(0, idx))] = Utils.urlDecode(pair.substring(idx + 1))
|
||||
}
|
||||
}
|
||||
Log.d(AppConfig.ANG_PACKAGE, queryPairs.toString())
|
||||
var sni: String? = ""
|
||||
if (queryPairs["plugin"] == "obfs-local" && queryPairs["obfs"] == "http") {
|
||||
sni = config.outboundBean?.streamSettings?.populateTransportSettings(
|
||||
"tcp", "http", queryPairs["obfs-host"], queryPairs["path"], null, null, null, null, null, null
|
||||
)
|
||||
} else if (queryPairs["plugin"] == "v2ray-plugin") {
|
||||
var network = "ws";
|
||||
if (queryPairs["mode"] == "quic") {
|
||||
network = "quic";
|
||||
}
|
||||
sni = config.outboundBean?.streamSettings?.populateTransportSettings(
|
||||
network, null, queryPairs["host"], queryPairs["path"], null, null, null, null, null, null
|
||||
)
|
||||
}
|
||||
if ("tls" in queryPairs) {
|
||||
config.outboundBean?.streamSettings?.populateTlsSettings(
|
||||
"tls", false, sni ?: "", null, null, null, null, null
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
config.outboundBean?.settings?.servers?.get(0)?.let { server ->
|
||||
server.address = uri.idnHost
|
||||
server.port = uri.port
|
||||
@@ -571,7 +645,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 +678,8 @@ object AngConfigManager {
|
||||
Utils.encode(json)
|
||||
}
|
||||
|
||||
EConfigType.CUSTOM, EConfigType.WIREGUARD -> ""
|
||||
EConfigType.CUSTOM -> ""
|
||||
|
||||
EConfigType.SHADOWSOCKS -> {
|
||||
val remark = "#" + Utils.urlEncode(config.remarks)
|
||||
val pw =
|
||||
@@ -617,10 +696,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 +754,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) {
|
||||
@@ -690,7 +773,7 @@ object AngConfigManager {
|
||||
}
|
||||
}
|
||||
|
||||
"ws" -> {
|
||||
"ws", "httpupgrade" -> {
|
||||
if (!TextUtils.isEmpty(transportDetails[1])) {
|
||||
dicQuery["host"] = Utils.urlEncode(transportDetails[1])
|
||||
}
|
||||
@@ -717,7 +800,8 @@ object AngConfigManager {
|
||||
|
||||
"grpc" -> {
|
||||
dicQuery["mode"] = transportDetails[0]
|
||||
dicQuery["serviceName"] = transportDetails[2]
|
||||
dicQuery["authority"] = Utils.urlEncode(transportDetails[1])
|
||||
dicQuery["serviceName"] = Utils.urlEncode(transportDetails[2])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -733,6 +817,38 @@ 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 +974,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 +1026,57 @@ 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")
|
||||
) {
|
||||
try {
|
||||
//val gson = GsonBuilder().setPrettyPrinting().create()
|
||||
val gson = GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.disableHtmlEscaping()
|
||||
.registerTypeAdapter( // custom serialiser is needed here since JSON by default parse number as Double, core will fail to start
|
||||
object : TypeToken<Double>() {}.type,
|
||||
JsonSerializer { src: Double?, _: Type?, _: JsonSerializationContext? -> JsonPrimitive(src?.toInt()) }
|
||||
)
|
||||
.create()
|
||||
val serverList: Array<V2rayConfig> =
|
||||
Gson().fromJson(server, Array<V2rayConfig>::class.java)
|
||||
|
||||
if (serverList.isNotEmpty()) {
|
||||
var count = 0
|
||||
for (srv in serverList) {
|
||||
val config = ServerConfig.create(EConfigType.CUSTOM)
|
||||
config.remarks = srv.remarks
|
||||
?: ("%04d-".format(count + 1) + System.currentTimeMillis()
|
||||
.toString())
|
||||
config.subscriptionId = subid
|
||||
config.fullConfig = srv
|
||||
val key = MmkvManager.encodeServerConfig("", config)
|
||||
serverRawStorage?.encode(key, gson.toJson(srv))
|
||||
count += 1
|
||||
}
|
||||
return count
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
// For compatibility
|
||||
val config = ServerConfig.create(EConfigType.CUSTOM)
|
||||
config.subscriptionId = subid
|
||||
config.fullConfig = Gson().fromJson(server, V2rayConfig::class.java)
|
||||
config.remarks = config.fullConfig?.remarks ?: System.currentTimeMillis().toString()
|
||||
val key = MmkvManager.encodeServerConfig("", config)
|
||||
serverRawStorage?.encode(key, server)
|
||||
return 1
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ object AppManagerUtil {
|
||||
|
||||
val appName = applicationInfo.loadLabel(packageManager).toString()
|
||||
val appIcon = applicationInfo.loadIcon(packageManager)
|
||||
val isSystemApp = (applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM) > 0
|
||||
val isSystemApp = applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM > 0
|
||||
|
||||
val appInfo = AppInfo(appName, pkg.packageName, appIcon, isSystemApp, 0)
|
||||
apps.add(appInfo)
|
||||
|
||||
@@ -2,9 +2,11 @@ package com.v2ray.ang.util
|
||||
|
||||
import com.google.gson.Gson
|
||||
import com.tencent.mmkv.MMKV
|
||||
import com.v2ray.ang.dto.AssetUrlItem
|
||||
import com.v2ray.ang.dto.ServerAffiliationInfo
|
||||
import com.v2ray.ang.dto.ServerConfig
|
||||
import com.v2ray.ang.dto.SubscriptionItem
|
||||
import java.net.URI
|
||||
|
||||
object MmkvManager {
|
||||
const val ID_MAIN = "MAIN"
|
||||
@@ -12,6 +14,7 @@ object MmkvManager {
|
||||
const val ID_SERVER_RAW = "SERVER_RAW"
|
||||
const val ID_SERVER_AFF = "SERVER_AFF"
|
||||
const val ID_SUB = "SUB"
|
||||
const val ID_ASSET = "ASSET"
|
||||
const val ID_SETTING = "SETTING"
|
||||
const val KEY_SELECTED_SERVER = "SELECTED_SERVER"
|
||||
const val KEY_ANG_CONFIGS = "ANG_CONFIGS"
|
||||
@@ -20,6 +23,7 @@ object MmkvManager {
|
||||
private val serverStorage by lazy { MMKV.mmkvWithID(ID_SERVER_CONFIG, MMKV.MULTI_PROCESS_MODE) }
|
||||
private val serverAffStorage by lazy { MMKV.mmkvWithID(ID_SERVER_AFF, MMKV.MULTI_PROCESS_MODE) }
|
||||
private val subStorage by lazy { MMKV.mmkvWithID(ID_SUB, MMKV.MULTI_PROCESS_MODE) }
|
||||
private val assetStorage by lazy { MMKV.mmkvWithID(ID_ASSET, MMKV.MULTI_PROCESS_MODE) }
|
||||
|
||||
fun decodeServerList(): MutableList<String> {
|
||||
val json = mainStorage?.decodeString(KEY_ANG_CONFIGS)
|
||||
@@ -118,8 +122,9 @@ object MmkvManager {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
val uri = URI(Utils.fixIllegalUrl(url))
|
||||
val subItem = SubscriptionItem()
|
||||
subItem.remarks = "import sub"
|
||||
subItem.remarks = Utils.urlDecode(uri.fragment ?: "import sub")
|
||||
subItem.url = url
|
||||
subStorage?.encode(Utils.getUuid(), Gson().toJson(subItem))
|
||||
return 1
|
||||
@@ -142,6 +147,22 @@ object MmkvManager {
|
||||
removeServerViaSubid(subid)
|
||||
}
|
||||
|
||||
fun decodeAssetUrls(): List<Pair<String, AssetUrlItem>> {
|
||||
val assetUrlItems = mutableListOf<Pair<String, AssetUrlItem>>()
|
||||
assetStorage?.allKeys()?.forEach { key ->
|
||||
val json = assetStorage?.decodeString(key)
|
||||
if (!json.isNullOrBlank()) {
|
||||
assetUrlItems.add(Pair(key, Gson().fromJson(json, AssetUrlItem::class.java)))
|
||||
}
|
||||
}
|
||||
assetUrlItems.sortedBy { (_, value) -> value.addedTime }
|
||||
return assetUrlItems
|
||||
}
|
||||
|
||||
fun removeAssetUrl(assetid: String) {
|
||||
assetStorage?.remove(assetid)
|
||||
}
|
||||
|
||||
fun removeAllServer() {
|
||||
mainStorage?.clearAll()
|
||||
serverStorage?.clearAll()
|
||||
|
||||
@@ -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
|
||||
@@ -209,7 +210,7 @@ object Utils {
|
||||
}
|
||||
|
||||
fun isPureIpAddress(value: String): Boolean {
|
||||
return (isIpv4Address(value) || isIpv6Address(value))
|
||||
return isIpv4Address(value) || isIpv6Address(value)
|
||||
}
|
||||
|
||||
fun isIpv4Address(value: String): Boolean {
|
||||
@@ -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
|
||||
|
||||
@@ -2,11 +2,14 @@ package com.v2ray.ang.util
|
||||
|
||||
import android.content.Context
|
||||
import android.text.TextUtils
|
||||
import android.util.Log
|
||||
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.PROTOCOL_FREEDOM
|
||||
import com.v2ray.ang.AppConfig.TAG_DIRECT
|
||||
import com.v2ray.ang.AppConfig.TAG_FRAGMENT
|
||||
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
|
||||
@@ -14,8 +17,18 @@ import com.v2ray.ang.dto.V2rayConfig.Companion.DEFAULT_NETWORK
|
||||
import com.v2ray.ang.dto.V2rayConfig.Companion.HTTP
|
||||
|
||||
object V2rayConfigUtil {
|
||||
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) }
|
||||
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
|
||||
)
|
||||
}
|
||||
|
||||
data class Result(var status: Boolean, var content: String)
|
||||
|
||||
@@ -32,12 +45,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)
|
||||
val result = getV2rayNonCustomConfig(context, outbound, config.remarks)
|
||||
//Log.d(ANG_PACKAGE, result.content)
|
||||
return result
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
@@ -48,7 +61,11 @@ object V2rayConfigUtil {
|
||||
/**
|
||||
* 生成v2ray的客户端配置文件
|
||||
*/
|
||||
private fun getV2rayNonCustomConfig(context: Context, outbound: V2rayConfig.OutboundBean): Result {
|
||||
private fun getV2rayNonCustomConfig(
|
||||
context: Context,
|
||||
outbound: V2rayConfig.OutboundBean,
|
||||
remarks: String,
|
||||
): Result {
|
||||
val result = Result(false, "")
|
||||
//取得默认配置
|
||||
val assets = Utils.readTextFromAssets(context, "v2ray_config.json")
|
||||
@@ -60,14 +77,15 @@ object V2rayConfigUtil {
|
||||
val v2rayConfig = Gson().fromJson(assets, V2rayConfig::class.java) ?: return result
|
||||
|
||||
v2rayConfig.log.loglevel = settingsStorage?.decodeString(AppConfig.PREF_LOGLEVEL)
|
||||
?: "warning"
|
||||
?: "warning"
|
||||
|
||||
inbounds(v2rayConfig)
|
||||
|
||||
updateOutboundWithGlobalSettings(outbound)
|
||||
|
||||
v2rayConfig.outbounds[0] = outbound
|
||||
|
||||
updateOutboundFragment(v2rayConfig)
|
||||
|
||||
routing(v2rayConfig)
|
||||
|
||||
fakedns(v2rayConfig)
|
||||
@@ -81,6 +99,9 @@ object V2rayConfigUtil {
|
||||
v2rayConfig.stats = null
|
||||
v2rayConfig.policy = null
|
||||
}
|
||||
|
||||
v2rayConfig.remarks = remarks
|
||||
|
||||
result.status = true
|
||||
result.content = v2rayConfig.toPrettyPrinting()
|
||||
return result
|
||||
@@ -91,8 +112,14 @@ object V2rayConfigUtil {
|
||||
*/
|
||||
private fun inbounds(v2rayConfig: V2rayConfig): Boolean {
|
||||
try {
|
||||
val socksPort = Utils.parseInt(settingsStorage?.decodeString(AppConfig.PREF_SOCKS_PORT), AppConfig.PORT_SOCKS.toInt())
|
||||
val httpPort = Utils.parseInt(settingsStorage?.decodeString(AppConfig.PREF_HTTP_PORT), AppConfig.PORT_HTTP.toInt())
|
||||
val socksPort = Utils.parseInt(
|
||||
settingsStorage?.decodeString(AppConfig.PREF_SOCKS_PORT),
|
||||
AppConfig.PORT_SOCKS.toInt()
|
||||
)
|
||||
val httpPort = Utils.parseInt(
|
||||
settingsStorage?.decodeString(AppConfig.PREF_HTTP_PORT),
|
||||
AppConfig.PORT_HTTP.toInt()
|
||||
)
|
||||
|
||||
v2rayConfig.inbounds.forEach { curInbound ->
|
||||
if (settingsStorage?.decodeBool(AppConfig.PREF_PROXY_SHARING) != true) {
|
||||
@@ -102,8 +129,9 @@ object V2rayConfigUtil {
|
||||
}
|
||||
v2rayConfig.inbounds[0].port = socksPort
|
||||
val fakedns = settingsStorage?.decodeBool(AppConfig.PREF_FAKE_DNS_ENABLED)
|
||||
?: false
|
||||
val sniffAllTlsAndHttp = settingsStorage?.decodeBool(AppConfig.PREF_SNIFFING_ENABLED, true)
|
||||
?: false
|
||||
val sniffAllTlsAndHttp =
|
||||
settingsStorage?.decodeBool(AppConfig.PREF_SNIFFING_ENABLED, true)
|
||||
?: true
|
||||
v2rayConfig.inbounds[0].sniffing?.enabled = fakedns || sniffAllTlsAndHttp
|
||||
if (!sniffAllTlsAndHttp) {
|
||||
@@ -129,11 +157,14 @@ object V2rayConfigUtil {
|
||||
}
|
||||
|
||||
private fun fakedns(v2rayConfig: V2rayConfig) {
|
||||
if (settingsStorage?.decodeBool(AppConfig.PREF_FAKE_DNS_ENABLED) == true) {
|
||||
if (settingsStorage?.decodeBool(AppConfig.PREF_LOCAL_DNS_ENABLED) == true
|
||||
|| settingsStorage?.decodeBool(AppConfig.PREF_FAKE_DNS_ENABLED) == true
|
||||
) {
|
||||
v2rayConfig.fakedns = listOf(V2rayConfig.FakednsBean())
|
||||
v2rayConfig.outbounds.filter { it.protocol == "freedom" }.forEach {
|
||||
it.settings?.domainStrategy = "UseIP"
|
||||
}
|
||||
v2rayConfig.outbounds.filter { it.protocol == PROTOCOL_FREEDOM && it.tag == TAG_DIRECT }
|
||||
.forEach {
|
||||
it.settings?.domainStrategy = "UseIP"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,38 +173,49 @@ object V2rayConfigUtil {
|
||||
*/
|
||||
private fun routing(v2rayConfig: V2rayConfig): Boolean {
|
||||
try {
|
||||
routingUserRule(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_AGENT)
|
||||
?: "", AppConfig.TAG_AGENT, v2rayConfig)
|
||||
routingUserRule(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_DIRECT)
|
||||
?: "", AppConfig.TAG_DIRECT, v2rayConfig)
|
||||
routingUserRule(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_BLOCKED)
|
||||
?: "", AppConfig.TAG_BLOCKED, v2rayConfig)
|
||||
routingUserRule(
|
||||
settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_AGENT)
|
||||
?: "", AppConfig.TAG_AGENT, v2rayConfig
|
||||
)
|
||||
routingUserRule(
|
||||
settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_DIRECT)
|
||||
?: "", AppConfig.TAG_DIRECT, v2rayConfig
|
||||
)
|
||||
routingUserRule(
|
||||
settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_BLOCKED)
|
||||
?: "", AppConfig.TAG_BLOCKED, v2rayConfig
|
||||
)
|
||||
|
||||
v2rayConfig.routing.domainStrategy = settingsStorage?.decodeString(AppConfig.PREF_ROUTING_DOMAIN_STRATEGY)
|
||||
v2rayConfig.routing.domainStrategy =
|
||||
settingsStorage?.decodeString(AppConfig.PREF_ROUTING_DOMAIN_STRATEGY)
|
||||
?: "IPIfNonMatch"
|
||||
// v2rayConfig.routing.domainMatcher = "mph"
|
||||
val routingMode = settingsStorage?.decodeString(AppConfig.PREF_ROUTING_MODE) ?: ERoutingMode.GLOBAL_PROXY.value
|
||||
val routingMode = settingsStorage?.decodeString(AppConfig.PREF_ROUTING_MODE)
|
||||
?: ERoutingMode.GLOBAL_PROXY.value
|
||||
|
||||
// Hardcode googleapis.cn
|
||||
val googleapisRoute = V2rayConfig.RoutingBean.RulesBean(
|
||||
type = "field",
|
||||
outboundTag = AppConfig.TAG_AGENT,
|
||||
domain = arrayListOf("domain:googleapis.cn")
|
||||
type = "field",
|
||||
outboundTag = AppConfig.TAG_AGENT,
|
||||
domain = arrayListOf("domain:googleapis.cn")
|
||||
)
|
||||
|
||||
when (routingMode) {
|
||||
ERoutingMode.BYPASS_LAN.value -> {
|
||||
routingGeo("ip", "private", AppConfig.TAG_DIRECT, v2rayConfig)
|
||||
}
|
||||
|
||||
ERoutingMode.BYPASS_MAINLAND.value -> {
|
||||
routingGeo("", "cn", AppConfig.TAG_DIRECT, v2rayConfig)
|
||||
v2rayConfig.routing.rules.add(0, googleapisRoute)
|
||||
}
|
||||
|
||||
ERoutingMode.BYPASS_LAN_MAINLAND.value -> {
|
||||
routingGeo("ip", "private", AppConfig.TAG_DIRECT, v2rayConfig)
|
||||
routingGeo("", "cn", AppConfig.TAG_DIRECT, v2rayConfig)
|
||||
v2rayConfig.routing.rules.add(0, googleapisRoute)
|
||||
}
|
||||
|
||||
ERoutingMode.GLOBAL_DIRECT.value -> {
|
||||
val globalDirect = V2rayConfig.RoutingBean.RulesBean(
|
||||
type = "field",
|
||||
@@ -190,7 +232,12 @@ object V2rayConfigUtil {
|
||||
return true
|
||||
}
|
||||
|
||||
private fun routingGeo(ipOrDomain: String, code: String, tag: String, v2rayConfig: V2rayConfig) {
|
||||
private fun routingGeo(
|
||||
ipOrDomain: String,
|
||||
code: String,
|
||||
tag: String,
|
||||
v2rayConfig: V2rayConfig
|
||||
) {
|
||||
try {
|
||||
if (!TextUtils.isEmpty(code)) {
|
||||
//IP
|
||||
@@ -275,51 +322,70 @@ object V2rayConfigUtil {
|
||||
try {
|
||||
if (settingsStorage?.decodeBool(AppConfig.PREF_FAKE_DNS_ENABLED) == true) {
|
||||
val geositeCn = arrayListOf("geosite:cn")
|
||||
val proxyDomain = userRule2Domian(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_AGENT)
|
||||
?: "")
|
||||
val directDomain = userRule2Domian(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_DIRECT)
|
||||
?: "")
|
||||
val proxyDomain = userRule2Domian(
|
||||
settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_AGENT)
|
||||
?: ""
|
||||
)
|
||||
val directDomain = userRule2Domian(
|
||||
settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_DIRECT)
|
||||
?: ""
|
||||
)
|
||||
// fakedns with all domains to make it always top priority
|
||||
v2rayConfig.dns.servers?.add(0,
|
||||
V2rayConfig.DnsBean.ServersBean(address = "fakedns", domains = geositeCn.plus(proxyDomain).plus(directDomain)))
|
||||
v2rayConfig.dns.servers?.add(
|
||||
0,
|
||||
V2rayConfig.DnsBean.ServersBean(
|
||||
address = "fakedns",
|
||||
domains = geositeCn.plus(proxyDomain).plus(directDomain)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// DNS inbound对象
|
||||
val remoteDns = Utils.getRemoteDnsServers()
|
||||
if (v2rayConfig.inbounds.none { e -> e.protocol == "dokodemo-door" && e.tag == "dns-in" }) {
|
||||
val dnsInboundSettings = V2rayConfig.InboundBean.InSettingsBean(
|
||||
address = if (Utils.isPureIpAddress(remoteDns.first())) remoteDns.first() else "1.1.1.1",
|
||||
port = 53,
|
||||
network = "tcp,udp")
|
||||
address = if (Utils.isPureIpAddress(remoteDns.first())) remoteDns.first() else "1.1.1.1",
|
||||
port = 53,
|
||||
network = "tcp,udp"
|
||||
)
|
||||
|
||||
val localDnsPort = Utils.parseInt(settingsStorage?.decodeString(AppConfig.PREF_LOCAL_DNS_PORT), AppConfig.PORT_LOCAL_DNS.toInt())
|
||||
val localDnsPort = Utils.parseInt(
|
||||
settingsStorage?.decodeString(AppConfig.PREF_LOCAL_DNS_PORT),
|
||||
AppConfig.PORT_LOCAL_DNS.toInt()
|
||||
)
|
||||
v2rayConfig.inbounds.add(
|
||||
V2rayConfig.InboundBean(
|
||||
tag = "dns-in",
|
||||
port = localDnsPort,
|
||||
listen = "127.0.0.1",
|
||||
protocol = "dokodemo-door",
|
||||
settings = dnsInboundSettings,
|
||||
sniffing = null))
|
||||
V2rayConfig.InboundBean(
|
||||
tag = "dns-in",
|
||||
port = localDnsPort,
|
||||
listen = "127.0.0.1",
|
||||
protocol = "dokodemo-door",
|
||||
settings = dnsInboundSettings,
|
||||
sniffing = null
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// DNS outbound对象
|
||||
if (v2rayConfig.outbounds.none { e -> e.protocol == "dns" && e.tag == "dns-out" }) {
|
||||
v2rayConfig.outbounds.add(
|
||||
V2rayConfig.OutboundBean(
|
||||
protocol = "dns",
|
||||
tag = "dns-out",
|
||||
settings = null,
|
||||
streamSettings = null,
|
||||
mux = null))
|
||||
V2rayConfig.OutboundBean(
|
||||
protocol = "dns",
|
||||
tag = "dns-out",
|
||||
settings = null,
|
||||
streamSettings = null,
|
||||
mux = null
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// DNS routing tag
|
||||
v2rayConfig.routing.rules.add(0, V2rayConfig.RoutingBean.RulesBean(
|
||||
v2rayConfig.routing.rules.add(
|
||||
0, V2rayConfig.RoutingBean.RulesBean(
|
||||
type = "field",
|
||||
inboundTag = arrayListOf("dns-in"),
|
||||
outboundTag = "dns-out",
|
||||
domain = null)
|
||||
domain = null
|
||||
)
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
@@ -333,43 +399,73 @@ object V2rayConfigUtil {
|
||||
val hosts = mutableMapOf<String, String>()
|
||||
val servers = ArrayList<Any>()
|
||||
val remoteDns = Utils.getRemoteDnsServers()
|
||||
val proxyDomain = userRule2Domian(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_AGENT)
|
||||
?: "")
|
||||
val proxyDomain = userRule2Domian(
|
||||
settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_AGENT)
|
||||
?: ""
|
||||
)
|
||||
|
||||
remoteDns.forEach {
|
||||
servers.add(it)
|
||||
}
|
||||
if (proxyDomain.size > 0) {
|
||||
servers.add(V2rayConfig.DnsBean.ServersBean(remoteDns.first(), 53, proxyDomain, null))
|
||||
servers.add(
|
||||
V2rayConfig.DnsBean.ServersBean(
|
||||
remoteDns.first(),
|
||||
53,
|
||||
proxyDomain,
|
||||
null
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// domestic DNS
|
||||
val directDomain = userRule2Domian(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_DIRECT)
|
||||
?: "")
|
||||
val routingMode = settingsStorage?.decodeString(AppConfig.PREF_ROUTING_MODE) ?: ERoutingMode.GLOBAL_PROXY.value
|
||||
val directDomain = userRule2Domian(
|
||||
settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_DIRECT)
|
||||
?: ""
|
||||
)
|
||||
val routingMode = settingsStorage?.decodeString(AppConfig.PREF_ROUTING_MODE)
|
||||
?: ERoutingMode.GLOBAL_PROXY.value
|
||||
if (directDomain.size > 0 || routingMode == ERoutingMode.BYPASS_MAINLAND.value || routingMode == ERoutingMode.BYPASS_LAN_MAINLAND.value) {
|
||||
val domesticDns = Utils.getDomesticDnsServers()
|
||||
val geositeCn = arrayListOf("geosite:cn")
|
||||
val geoipCn = arrayListOf("geoip:cn")
|
||||
if (directDomain.size > 0) {
|
||||
servers.add(V2rayConfig.DnsBean.ServersBean(domesticDns.first(), 53, directDomain, geoipCn))
|
||||
servers.add(
|
||||
V2rayConfig.DnsBean.ServersBean(
|
||||
domesticDns.first(),
|
||||
53,
|
||||
directDomain,
|
||||
geoipCn
|
||||
)
|
||||
)
|
||||
}
|
||||
if (routingMode == ERoutingMode.BYPASS_MAINLAND.value || routingMode == ERoutingMode.BYPASS_LAN_MAINLAND.value) {
|
||||
servers.add(V2rayConfig.DnsBean.ServersBean(domesticDns.first(), 53, geositeCn, geoipCn))
|
||||
servers.add(
|
||||
V2rayConfig.DnsBean.ServersBean(
|
||||
domesticDns.first(),
|
||||
53,
|
||||
geositeCn,
|
||||
geoipCn
|
||||
)
|
||||
)
|
||||
}
|
||||
if (Utils.isPureIpAddress(domesticDns.first())) {
|
||||
v2rayConfig.routing.rules.add(0, V2rayConfig.RoutingBean.RulesBean(
|
||||
v2rayConfig.routing.rules.add(
|
||||
0, V2rayConfig.RoutingBean.RulesBean(
|
||||
type = "field",
|
||||
outboundTag = AppConfig.TAG_DIRECT,
|
||||
port = "53",
|
||||
ip = arrayListOf(domesticDns.first()),
|
||||
domain = null)
|
||||
domain = null
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val blkDomain = userRule2Domian(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_BLOCKED)
|
||||
?: "")
|
||||
val blkDomain = userRule2Domian(
|
||||
settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_BLOCKED)
|
||||
?: ""
|
||||
)
|
||||
if (blkDomain.size > 0) {
|
||||
hosts.putAll(blkDomain.map { it to "127.0.0.1" })
|
||||
}
|
||||
@@ -379,17 +475,20 @@ object V2rayConfigUtil {
|
||||
|
||||
// DNS dns对象
|
||||
v2rayConfig.dns = V2rayConfig.DnsBean(
|
||||
servers = servers,
|
||||
hosts = hosts)
|
||||
servers = servers,
|
||||
hosts = hosts
|
||||
)
|
||||
|
||||
// DNS routing
|
||||
if (Utils.isPureIpAddress(remoteDns.first())) {
|
||||
v2rayConfig.routing.rules.add(0, V2rayConfig.RoutingBean.RulesBean(
|
||||
v2rayConfig.routing.rules.add(
|
||||
0, V2rayConfig.RoutingBean.RulesBean(
|
||||
type = "field",
|
||||
outboundTag = AppConfig.TAG_AGENT,
|
||||
port = "53",
|
||||
ip = arrayListOf(remoteDns.first()),
|
||||
domain = null)
|
||||
domain = null
|
||||
)
|
||||
)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
@@ -402,11 +501,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 +513,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 +526,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
|
||||
) {
|
||||
@@ -449,6 +560,7 @@ object V2rayConfigUtil {
|
||||
outbound.streamSettings?.tcpSettings?.header?.request?.headers?.Host = host!!
|
||||
}
|
||||
|
||||
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
return false
|
||||
@@ -456,4 +568,61 @@ object V2rayConfigUtil {
|
||||
return true
|
||||
}
|
||||
|
||||
private fun updateOutboundFragment(v2rayConfig: V2rayConfig): Boolean {
|
||||
try {
|
||||
if (settingsStorage?.decodeBool(AppConfig.PREF_FRAGMENT_ENABLED, false) == false) {
|
||||
return true
|
||||
}
|
||||
if (v2rayConfig.outbounds[0].streamSettings?.security != V2rayConfig.TLS
|
||||
&& v2rayConfig.outbounds[0].streamSettings?.security != V2rayConfig.REALITY
|
||||
) {
|
||||
return true
|
||||
}
|
||||
|
||||
val fragmentOutbound =
|
||||
V2rayConfig.OutboundBean(
|
||||
protocol = PROTOCOL_FREEDOM,
|
||||
tag = TAG_FRAGMENT,
|
||||
mux = null
|
||||
)
|
||||
|
||||
var packets = settingsStorage?.decodeString(AppConfig.PREF_FRAGMENT_PACKETS) ?: "tlshello"
|
||||
if (v2rayConfig.outbounds[0].streamSettings?.security == V2rayConfig.REALITY
|
||||
&& packets == "tlshello"
|
||||
) {
|
||||
packets = "1-3"
|
||||
} else if (v2rayConfig.outbounds[0].streamSettings?.security == V2rayConfig.TLS
|
||||
&& packets != "tlshello"
|
||||
) {
|
||||
packets = "tlshello"
|
||||
}
|
||||
|
||||
fragmentOutbound.settings = V2rayConfig.OutboundBean.OutSettingsBean(
|
||||
fragment = V2rayConfig.OutboundBean.OutSettingsBean.FragmentBean(
|
||||
packets = packets,
|
||||
length = settingsStorage?.decodeString(AppConfig.PREF_FRAGMENT_LENGTH)
|
||||
?: "50-100",
|
||||
interval = settingsStorage?.decodeString(AppConfig.PREF_FRAGMENT_INTERVAL)
|
||||
?: "10-20"
|
||||
)
|
||||
)
|
||||
fragmentOutbound.streamSettings = V2rayConfig.OutboundBean.StreamSettingsBean(
|
||||
sockopt = V2rayConfig.OutboundBean.StreamSettingsBean.SockoptBean(
|
||||
TcpNoDelay = true,
|
||||
mark = 255
|
||||
)
|
||||
)
|
||||
v2rayConfig.outbounds.add(fragmentOutbound)
|
||||
|
||||
//proxy chain
|
||||
v2rayConfig.outbounds[0].streamSettings?.sockopt =
|
||||
V2rayConfig.OutboundBean.StreamSettingsBean.SockoptBean(
|
||||
dialerProxy = TAG_FRAGMENT
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,20 +90,20 @@ 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)
|
||||
}
|
||||
}
|
||||
|
||||
fun appendCustomConfigServer(server: String) {
|
||||
val config = ServerConfig.create(EConfigType.CUSTOM)
|
||||
config.remarks = System.currentTimeMillis().toString()
|
||||
config.subscriptionId = subscriptionId
|
||||
config.fullConfig = Gson().fromJson(server, V2rayConfig::class.java)
|
||||
config.remarks = config.fullConfig?.remarks ?: System.currentTimeMillis().toString()
|
||||
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)
|
||||
|
||||
@@ -41,6 +41,9 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
|
||||
AppConfig.PREF_V2RAY_ROUTING_BLOCKED,
|
||||
AppConfig.PREF_V2RAY_ROUTING_DIRECT,
|
||||
AppConfig.SUBSCRIPTION_AUTO_UPDATE_INTERVAL,
|
||||
AppConfig.PREF_FRAGMENT_PACKETS,
|
||||
AppConfig.PREF_FRAGMENT_LENGTH,
|
||||
AppConfig.PREF_FRAGMENT_INTERVAL,
|
||||
AppConfig.PREF_MUX_XUDP_QUIC, -> {
|
||||
settingsStorage?.encode(key, sharedPreferences.getString(key, ""))
|
||||
}
|
||||
@@ -55,12 +58,14 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
|
||||
AppConfig.PREF_CONFIRM_REMOVE,
|
||||
AppConfig.PREF_START_SCAN_IMMEDIATE,
|
||||
AppConfig.SUBSCRIPTION_AUTO_UPDATE,
|
||||
AppConfig.PREF_FRAGMENT_ENABLED,
|
||||
AppConfig.PREF_MUX_ENABLED, -> {
|
||||
settingsStorage?.encode(key, sharedPreferences.getBoolean(key, false))
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -117,7 +117,95 @@
|
||||
android:entries="@array/ss_securitys" />
|
||||
</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_more_function"
|
||||
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_network" />
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/sp_network"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/edit_height"
|
||||
android:entries="@array/networks" />
|
||||
</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:id="@+id/sp_header_type_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/server_lab_head_type" />
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/sp_header_type"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/edit_height" />
|
||||
</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_request_host" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_request_host"
|
||||
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_path" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_path"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/edit_height"
|
||||
android:inputType="text" />
|
||||
</LinearLayout>
|
||||
|
||||
<include layout="@layout/tls_layout" />
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -126,4 +214,4 @@
|
||||
android:orientation="vertical" />
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</ScrollView>
|
||||
|
||||
199
V2rayNG/app/src/main/res/layout/activity_server_wireguard.xml
Normal file
199
V2rayNG/app/src/main/res/layout/activity_server_wireguard.xml
Normal 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>
|
||||
86
V2rayNG/app/src/main/res/layout/activity_user_asset_url.xml
Normal file
86
V2rayNG/app/src/main/res/layout/activity_user_asset_url.xml
Normal file
@@ -0,0 +1,86 @@
|
||||
<?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.UserAssetUrlActivity">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<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/menu_item_add_asset"
|
||||
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/sub_setting_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/title_url" />
|
||||
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_url"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:gravity="top"
|
||||
android:inputType="textMultiLine"
|
||||
android:maxLines="10"
|
||||
android:minLines="5"
|
||||
android:scrollbars="vertical" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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">
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -17,36 +17,70 @@
|
||||
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
|
||||
android:layout_width="0dp"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="9dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
<TextView
|
||||
android:id="@+id/asset_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="2"
|
||||
android:minLines="1"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
|
||||
tools:text="Placeholder.dat" />
|
||||
|
||||
<TextView
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/asset_properties"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAlignment="textEnd"
|
||||
android:lines="1"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||
android:textSize="12sp"
|
||||
tools:text="1MB . 2020.01.01" />
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_edit"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:padding="@dimen/nav_header_vertical_spacing">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="@dimen/png_height"
|
||||
android:layout_height="@dimen/png_height"
|
||||
app:srcCompat="@drawable/ic_edit_24dp"
|
||||
app:tint="?attr/colorMainText" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
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 +88,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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -2,10 +2,20 @@
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/add_file"
|
||||
android:icon="@drawable/ic_add_24dp"
|
||||
android:title="@string/menu_item_add_file"
|
||||
app:showAsAction="ifRoom" />
|
||||
android:title="@string/menu_item_add_asset"
|
||||
app:showAsAction="ifRoom" >
|
||||
<menu>
|
||||
<item
|
||||
android:id="@+id/add_file"
|
||||
android:title="@string/menu_item_add_file"
|
||||
app:showAsAction="never" />
|
||||
<item
|
||||
android:id="@+id/add_url"
|
||||
android:title="@string/menu_item_add_url"
|
||||
app:showAsAction="never" />
|
||||
</menu>
|
||||
</item>
|
||||
<item
|
||||
android:id="@+id/download_file"
|
||||
android:icon="@drawable/ic_cloud_download_24dp"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources>
|
||||
<string name="app_name" translatable="false">v2rayNG</string>
|
||||
<string name="app_widget_name">التبديل</string>
|
||||
<string name="app_tile_name">التبديل</string>
|
||||
<string name="app_tile_first_use">أول استخدام لهذه الميزة، يرجى استخدام التطبيق لإضافة خادم</string>
|
||||
@@ -28,6 +27,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>
|
||||
@@ -44,11 +44,9 @@
|
||||
<string name="server_lab_more_function">النقل</string>
|
||||
<string name="server_lab_head_type">نوع الرأس</string>
|
||||
<string name="server_lab_mode_type">وضع gRPC</string>
|
||||
<string name="server_lab_request_host">طلب الاستضافة (host/ws host/h2 host)/QUIC security</string>
|
||||
<string name="server_lab_request_host">طلب الاستضافة (host/ws host/h2 host)/QUIC security/gRPC Authority</string>
|
||||
<string name="server_lab_path">المسار (ws path/h2 path)/QUIC key/kcp seed/gRPC serviceName</string>
|
||||
<string name="server_lab_stream_security">TLS</string>
|
||||
<string name="server_lab_stream_fingerprint" translatable="false">Fingerprint</string>
|
||||
<string name="server_lab_stream_alpn" translatable="false">Alpn</string>
|
||||
<string name="server_lab_allow_insecure">allowInsecure</string>
|
||||
<string name="server_lab_sni">SNI</string>
|
||||
<string name="server_lab_address3">العنوان</string>
|
||||
@@ -59,9 +57,9 @@
|
||||
<string name="server_lab_security4">المستخدم (اختياري)</string>
|
||||
<string name="server_lab_encryption">التشفير</string>
|
||||
<string name="server_lab_flow">التدفق</string>
|
||||
<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>
|
||||
@@ -81,6 +79,9 @@
|
||||
<string name="menu_item_add_file">إضافة ملفات</string>
|
||||
<string name="menu_item_download_file">تحميل الملفات</string>
|
||||
<!-- PerAppProxyActivity -->
|
||||
<string name="title_user_asset_add_url">أضف عنوان URL للأصل</string>
|
||||
<string name="msg_file_not_found">لم يتم العثور على الملف</string>
|
||||
<string name="msg_remark_is_duplicate">الملاحظات موجودة بالفعل</string>
|
||||
<string name="msg_dialog_progress">جار التحميل</string>
|
||||
<string name="menu_item_search">بحث</string>
|
||||
<string name="menu_item_select_all">تحديد الكل</string>
|
||||
@@ -98,6 +99,7 @@
|
||||
<string name="summary_pref_per_app_proxy">عام: التطبيق المحدد هو الوكيل، الاتصال غير المحدد مباشر؛ \nوضع التجاوز: التطبيق المحدد متصل مباشرة، الوكيل غير المحدد. \nالخيار لتحديد التطبيق الوكيل تلقائيا في القائمة</string>
|
||||
<string name="title_pref_mux_enabled">تمكين Mux</string>
|
||||
<string name="summary_pref_mux_enabled">حركة مرور TCP مع 8 اتصالات افتراضية، قم بتخصيص كيفية التعامل مع UDP وQUIC أدناهn\أسرع، لكنه قد يسبب اتصالاً غير مستقر</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">
|
||||
@@ -143,8 +145,12 @@
|
||||
<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">الفاصل الزمني للتحديث التلقائي (الدقائق، القيمة الدنيا 15)</string>
|
||||
<string name="title_core_loglevel">مستوى السجل</string>
|
||||
<string name="title_mode">الوضع</string>
|
||||
<string name="title_mode_help">انقر علي للمزيد من المساعدة</string>
|
||||
@@ -162,6 +168,7 @@
|
||||
<string name="sub_setting_remarks">ملاحظات</string>
|
||||
<string name="sub_setting_url">URL اختياري</string>
|
||||
<string name="sub_setting_enable">تمكين التحديث</string>
|
||||
<string name="sub_auto_update">تمكين التحديث التلقائي</string>
|
||||
<string name="title_sub_update">تحديث الاشتراك (1)</string>
|
||||
<string name="title_ping_all_server">Tcping كل التكوين</string>
|
||||
<string name="title_real_ping_all_server">اختبر كل العناوين (3)</string>
|
||||
@@ -214,4 +221,6 @@
|
||||
<item>VPN</item>
|
||||
<item>الوكيل فقط</item>
|
||||
</string-array>
|
||||
<string name="menu_item_add_asset">يضيف</string>
|
||||
<string name="menu_item_add_url">إضافة رابط</string>
|
||||
</resources>
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name" translatable="false">v2rayNG</string>
|
||||
<string name="app_widget_name">تعویض</string>
|
||||
<string name="app_tile_name">تعویض</string>
|
||||
<string name="app_tile_first_use">برای اولین بار از این ویژگی استفاده میکنید، لطفا از برنامه برای افزودن سرور استفاده کنید</string>
|
||||
<string name="navigation_drawer_open">باز کردن منو کشویی</string>
|
||||
<string name="navigation_drawer_close">بستن منو کشویی</string>
|
||||
<string name="migration_success">موفقیت در انتقال داده!</string>
|
||||
<string name="migration_success">موفقیت در انتقال داده</string>
|
||||
<string name="migration_fail">انتقال داده انجام نشد!</string>
|
||||
|
||||
<!-- Notifications -->
|
||||
@@ -16,25 +15,26 @@
|
||||
<string name="toast_services_start">شروع خدمات</string>
|
||||
<string name="toast_services_stop">توقف خدمات</string>
|
||||
<string name="toast_services_success">خدمات با موفقیت شروع شد</string>
|
||||
<string name="toast_services_failure">شروع خدمات انجام نشد</string>
|
||||
<string name="toast_services_failure">شروع خدمات انجام نشد!</string>
|
||||
|
||||
<!--ServerActivity-->
|
||||
<string name="title_server">پرونده پیکربندی</string>
|
||||
<string name="menu_item_add_config">افزودن پیکربندی</string>
|
||||
<string name="menu_item_save_config">ذخیره پیکربندی</string>
|
||||
<string name="menu_item_del_config">حذف پیکربندی</string>
|
||||
<string name="menu_item_import_config_qrcode">پیکربندی را از QRcode وارد کنید</string>
|
||||
<string name="menu_item_import_config_clipboard">پیکربندی را از کلیپبورد وارد کنید</string>
|
||||
<string name="title_server">فایل کانفیگ</string>
|
||||
<string name="menu_item_add_config">افزودن کانفیگ</string>
|
||||
<string name="menu_item_save_config">ذخیره کانفیگ</string>
|
||||
<string name="menu_item_del_config">حذف کانفیگ</string>
|
||||
<string name="menu_item_import_config_qrcode">کانفیگ را از QRcode وارد کنید</string>
|
||||
<string name="menu_item_import_config_clipboard">کانفیگ را از کلیپبورد وارد کنید</string>
|
||||
<string name="menu_item_import_config_manually_vmess">تایپ دستی[Vmess]</string>
|
||||
<string name="menu_item_import_config_manually_vless">تایپ دستی[VLESS]</string>
|
||||
<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_custom">پیکربندی سفارشی</string>
|
||||
<string name="menu_item_import_config_custom_clipboard">پیکربندی سفارشی را از کلیپبورد وارد کنید</string>
|
||||
<string name="menu_item_import_config_custom_local">پیکربندی سفارشی را به صورت محلی وارد کنید</string>
|
||||
<string name="menu_item_import_config_custom_url">پیکربندی سفارشی را از طریق نشانی اینترنتی وارد کنید</string>
|
||||
<string name="menu_item_import_config_custom_url_scan">نشانی اینترنتی اسکن پیکربندی سفارشی را وارد کنید</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>
|
||||
<string name="menu_item_import_config_custom_url">کانفیگ سفارشی را از طریق نشانی اینترنتی وارد کنید</string>
|
||||
<string name="menu_item_import_config_custom_url_scan">نشانی اینترنتی اسکن کانفیگ سفارشی را وارد کنید</string>
|
||||
<string name="del_config_comfirm">حذف شود؟</string>
|
||||
<string name="server_lab_remarks">ملاحظات</string>
|
||||
<string name="server_lab_address">نشانی</string>
|
||||
@@ -46,10 +46,10 @@
|
||||
<string name="server_lab_more_function">انتقال</string>
|
||||
<string name="server_lab_head_type">نوع head</string>
|
||||
<string name="server_lab_mode_type">حالت gRPC</string>
|
||||
<string name="server_lab_request_host">درخواست میزبان (میزبان/میزبان ws/ میزبان h2)/امنیت QUIC</string>
|
||||
<string name="server_lab_request_host">gRPC Authority/میزبان درخواست (host/host ws/host h2)/امنیت QUIC</string>
|
||||
<string name="server_lab_path">مسیر (مسیر ws/ مسیر h2) کلید QUIC/دانه kcp/نامخدمات gRPC</string>
|
||||
<string name="server_lab_stream_security">TLS</string>
|
||||
<string name="server_lab_allow_insecure">allowInsecure</string>
|
||||
<string name="server_lab_allow_insecure">مجوز ناامن</string>
|
||||
<string name="server_lab_sni">SNI</string>
|
||||
<string name="server_lab_address3">نشانی</string>
|
||||
<string name="server_lab_port3">پورت</string>
|
||||
@@ -57,28 +57,34 @@
|
||||
<string name="server_lab_security3">امنیت</string>
|
||||
<string name="server_lab_id4">رمز عبور (اختیاری)</string>
|
||||
<string name="server_lab_security4">نامکاربری (اختیاری)</string>
|
||||
<string name="server_lab_encryption">رمزگذاری</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>
|
||||
<string name="toast_incorrect_protocol">پروتکل نادرست</string>
|
||||
<string name="toast_decoding_failed">رمزگشایی انجام نشد</string>
|
||||
<string name="title_file_chooser">انتخاب پرونده پیکربندی</string>
|
||||
<string name="toast_require_file_manager">لطفا یک مدیر پرونده نصب کنید.</string>
|
||||
<string name="server_customize_config">سفارشیسازی پیکربندی</string>
|
||||
<string name="toast_config_file_invalid">پیکربندی معتبر نیست</string>
|
||||
<string name="title_file_chooser">انتخاب فایل کانفیگ</string>
|
||||
<string name="toast_require_file_manager">لطفا یک برنامه مدیریت فایل نصب کنید.</string>
|
||||
<string name="server_customize_config">کانفیگ سفارشی</string>
|
||||
<string name="toast_config_file_invalid">کانفیگ معتبر نیست</string>
|
||||
<string name="server_lab_content">محتوا</string>
|
||||
<string name="toast_none_data_clipboard">هیچ دادهای در کلیپبورد وجود ندارد</string>
|
||||
<string name="toast_invalid_url">نشانی اینترنتی معتبر نیست</string>
|
||||
<string name="server_lab_need_inbound">اطمینان حاصل کنید که پورت ورودی با تنظیمات مطابقت دارد</string>
|
||||
<string name="toast_malformed_josn">پیکربندی درست نیست</string>
|
||||
<string name="toast_malformed_josn">کانفیگ درست نیست</string>
|
||||
<string name="server_lab_request_host6">میزبان (SNI) (اختیاری)</string>
|
||||
<string name="toast_asset_copy_failed">کپی پرونده انجام نشد، لطفا از مدیر پرونده استفاده کنید</string>
|
||||
<string name="menu_item_add_file">افزودن پروندهها</string>
|
||||
<string name="menu_item_download_file">دانلود پروندهها</string>
|
||||
<string name="toast_asset_copy_failed">کپی فایل انجام نشد، لطفا از برنامه مدیریت فایل استفاده کنید</string>
|
||||
<string name="menu_item_add_file">افزودن فایلها</string>
|
||||
<string name="menu_item_download_file">دانلود فایلها</string>
|
||||
|
||||
<!-- PerAppProxyActivity -->
|
||||
<string name="title_user_asset_add_url">URL را اضافه کنید</string>
|
||||
<string name="msg_file_not_found">فایل پیدا نشد</string>
|
||||
<string name="msg_remark_is_duplicate">نام قبلاً وجود دارد</string>
|
||||
<string name="msg_dialog_progress">بارگذاری</string>
|
||||
<string name="menu_item_search">جستجو</string>
|
||||
<string name="menu_item_select_all">انتخاب همه</string>
|
||||
@@ -89,22 +95,23 @@
|
||||
<string name="menu_item_export_proxy_app">خروجی گرفتن در کلیپبورد</string>
|
||||
<string name="menu_item_import_proxy_app">وارد کردن از کلیپبورد</string>
|
||||
|
||||
|
||||
<!-- Preferences -->
|
||||
<string name="title_settings">تنظیمات</string>
|
||||
<string name="title_advanced">تنظیمات پیشرفته</string>
|
||||
<string name="title_vpn_settings">تنظیمات VPN</string>
|
||||
<string name="title_pref_per_app_proxy">پروکسی هر برنامه</string>
|
||||
<string name="title_pref_per_app_proxy">پروکسی به تفکیک برنامه</string>
|
||||
<string name="summary_pref_per_app_proxy">عمومی: برنامه بررسی شده پروکسی است، اتصال مستقیم بدون بررسی است. \nحالت bypass: برنامه بررسی شده مستقیما متصل است، پراکسی بررسی نشده است. \nگزینهای برای انتخاب خودکار پروکسی برنامه در منو است</string>
|
||||
|
||||
<string name="title_mux_settings">تنظیمات Mux</string>
|
||||
<string name="title_pref_mux_enabled">فعال کردن Mux</string>
|
||||
<string name="summary_pref_mux_enabled">سریعتر است، اما ممکن است باعث اتصال ناپایدار شود\nمخزن ترافیک TCP با 8 اتصال پیشفرض، نحوه مدیریت 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 در تونل mux</string>
|
||||
<string-array name="mux_xudp_quic_entries">
|
||||
<item>رد کردن</item>
|
||||
<item>مجاز</item>
|
||||
<item>جست و خیز کردن</item>
|
||||
<item>پریدن</item>
|
||||
</string-array>
|
||||
|
||||
<string name="title_pref_speed_enabled">فعال کردن نمایش سرعت</string>
|
||||
@@ -151,8 +158,8 @@
|
||||
<string name="title_pref_local_dns_port">پورت DNS محلی</string>
|
||||
<string name="summary_pref_local_dns_port">پورت DNS محلی</string>
|
||||
|
||||
<string name="title_pref_confirm_remove">تایید حذف پرونده پیکربندی</string>
|
||||
<string name="summary_pref_confirm_remove">آیا برای حذف پرونده پیکربندی نیاز به تایید دوم توسط کاربر است</string>
|
||||
<string name="title_pref_confirm_remove">تایید حذف فایل کانفیگ</string>
|
||||
<string name="summary_pref_confirm_remove">آیا برای حذف فایل کانفیگ نیاز به تایید دوم توسط کاربر است</string>
|
||||
|
||||
<string name="title_pref_start_scan_immediate">فورا اسکن را شروع کن</string>
|
||||
<string name="summary_pref_start_scan_immediate">دوربین را برای اسکن بلافاصله در هنگام راه اندازی باز کنید، در غیر این صورت می توانید کد را اسکن کنید یا عکسی را در نوار ابزار انتخاب کنید.</string>
|
||||
@@ -162,9 +169,13 @@
|
||||
<string name="summary_pref_tg_group">عضویت در گروه تلگرام</string>
|
||||
<string name="toast_tg_app_not_found">برنامه تلگرام پیدا نشد</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">فاصله بهروزرسانی خودکار (دقیقه، حداقل مقدار 15)</string>
|
||||
<string name="title_core_loglevel">سطح گزارشات</string>
|
||||
<string name="title_mode">حالت</string>
|
||||
<string name="title_mode_help">برای راهنمایی بیشتر روی این متن، کلیک کنید</string>
|
||||
@@ -175,20 +186,21 @@
|
||||
<string name="logcat_copy">کپی</string>
|
||||
<string name="logcat_clear">پاک کردن</string>
|
||||
<string name="title_service_restart">راهاندازی مجدد خدمات</string>
|
||||
<string name="title_del_all_config">حذف تمام پیکربندی</string>
|
||||
<string name="title_del_all_config">حذف تمام کانفیگ</string>
|
||||
<string name="title_del_duplicate_config">حذف کانفیگ های تکراری</string>
|
||||
<string name="title_del_invalid_config">تنظیمات نامعتبر را حذف کنید (ابتدا آزمایش کنید)</string>
|
||||
<string name="title_export_all">خروجی گرفتن پیکربندیهای غیرسفارشی در کلیپبورد</string>
|
||||
<string name="title_del_invalid_config">حذف کانفیگهای نامعتبر (ابتدا آزمایش کنید)</string>
|
||||
<string name="title_export_all">خروجی گرفتن کانفیگهای غیرسفارشی در کلیپبورد</string>
|
||||
<string name="title_sub_setting">تنظیمات گروهی اشتراک</string>
|
||||
<string name="sub_setting_remarks">ملاحظات</string>
|
||||
<string name="sub_setting_url">نشانی اینترنتی اختیاری</string>
|
||||
<string name="sub_setting_enable">فعال کردن بهروزرسانی</string>
|
||||
<string name="sub_auto_update">فعال سازی بهروزرسانی خودکار</string>
|
||||
<string name="title_sub_update">بهروزرسانی اشتراک</string>
|
||||
<string name="title_ping_all_server">Tcping همه پیکربندی</string>
|
||||
<string name="title_real_ping_all_server">تاخیر واقعی همه پیکربندی</string>
|
||||
<string name="title_user_asset_setting">پروندههای دارایی جغرافیا</string>
|
||||
<string name="title_ping_all_server">Tcping همه کانفیگ</string>
|
||||
<string name="title_real_ping_all_server">تاخیر واقعی همه کانفیگ</string>
|
||||
<string name="title_user_asset_setting">فایلهای دارایی جغرافیا</string>
|
||||
<string name="title_sort_by_test_results">مرتبسازی بر اساس نتایج آزمایش</string>
|
||||
<string name="title_filter_config">فیلتر پرونده پیکربندی ها</string>
|
||||
<string name="title_filter_config">فیلتر کردن کانفیگها</string>
|
||||
<string name="filter_config_all">همه گروههای اشتراک</string>
|
||||
<string name="title_del_duplicate_config_count">حذف %d کانفیگ تکراری</string>
|
||||
|
||||
@@ -196,12 +208,12 @@
|
||||
<string name="tasker_setting_confirm">تایید</string>
|
||||
|
||||
<string name="routing_settings_title">تنظیمات مسیریابی</string>
|
||||
<string name="routing_settings_tips">با کاما (,) از هم جدا شوند، ذخیره کردن هم فراموش نک</string>
|
||||
<string name="routing_settings_tips">با کاما (,) از هم جدا شوند، ذخیره کردن فراموش نشود</string>
|
||||
<string name="routing_settings_save">ذخیره</string>
|
||||
<string name="routing_settings_delete">پاک کردن</string>
|
||||
<string name="routing_settings_scan_replace">اسکن و جایگزین کنید</string>
|
||||
<string name="routing_settings_scan_append">اسکن و اضافه کنید</string>
|
||||
<string name="routing_settings_default_rules"> قوانین مسیریابی پیشفرض را تنظیم کنید</string>
|
||||
<string name="routing_settings_default_rules">قوانین مسیریابی پیشفرض را تنظیم کنید</string>
|
||||
|
||||
<string name="connection_test_pending">اتصال را بررسی کنید</string>
|
||||
<string name="connection_test_testing">در حال آزمایش...</string>
|
||||
@@ -212,10 +224,17 @@
|
||||
<string name="connection_connected">متصل است، برای بررسی اتصال ضربه بزنید</string>
|
||||
<string name="connection_not_connected">متصل نیست</string>
|
||||
|
||||
<string name="import_subscription_success">اشتراک با موفقیت ذخیره شد</string>
|
||||
<string name="import_subscription_failure">ذخیره اشتراک ناموفق بود</string>
|
||||
<string name="title_fragment_settings">تنظیمات Fragment</string>
|
||||
<string name="title_pref_fragment_packets">Fragment Packets</string>
|
||||
<string name="title_pref_fragment_length">Fragment Length (min-max)</string>
|
||||
<string name="title_pref_fragment_interval">Fragment Interval (min-max)</string>
|
||||
<string name="title_pref_fragment_enabled">فعال کردن Fragment</string>
|
||||
<string-array name="share_method">
|
||||
<item>QRcode</item>
|
||||
<item>خروجی گرفتن در کلیپبورد</item>
|
||||
<item>خروجی گرفتن پیکربندی کامل در کلیپبورد</item>
|
||||
<item>خروجی گرفتن کانفیگ کامل در کلیپبورد</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="share_sub_method">
|
||||
@@ -232,7 +251,7 @@
|
||||
<string-array name="routing_mode">
|
||||
<item>پروکسی سراسری</item>
|
||||
<item>دور زدن آدرس LAN و سپس پروکسی</item>
|
||||
<item>دور زذن آدرس mainland و سپس پروکسی</item>
|
||||
<item>دور زدن آدرس mainland و سپس پروکسی</item>
|
||||
<item>دور زدن LAN و آدرس mainland و سپس پروکسی</item>
|
||||
<item>مستقیم سراسری</item>
|
||||
</string-array>
|
||||
@@ -241,7 +260,7 @@
|
||||
<item>VPN</item>
|
||||
<item>فقط پروکسی</item>
|
||||
</string-array>
|
||||
<string name="import_subscription_success">اشتراک با موفقیت ذخیره شد</string>
|
||||
<string name="import_subscription_failure">ذخیره اشتراک ناموفق بود</string>
|
||||
<string name="menu_item_add_asset">افزودن</string>
|
||||
<string name="menu_item_add_url">افزودن لینک</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name" translatable="false">v2rayNG</string>
|
||||
<string name="app_widget_name">Переключить</string>
|
||||
<string name="app_tile_name">Переключить</string>
|
||||
<string name="app_tile_first_use">Первое использование этой функции, пожалуйста, используйте приложение, чтобы добавить сервер</string>
|
||||
@@ -23,14 +22,15 @@
|
||||
<string name="menu_item_add_config">Добавить профиль</string>
|
||||
<string name="menu_item_save_config">Сохранить профиль</string>
|
||||
<string name="menu_item_del_config">Удалить профиль</string>
|
||||
<string name="menu_item_import_config_qrcode">Импорт профиля из QR-кода</string>
|
||||
<string name="menu_item_import_config_clipboard">Импорт профиля из буфера обмена</string>
|
||||
<string name="menu_item_import_config_manually_vmess">Ручной ввод профиля Vmess</string>
|
||||
<string name="menu_item_import_config_manually_vless">Ручной ввод профиля VLESS</string>
|
||||
<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_custom">Пользовательский профиль</string>
|
||||
<string name="menu_item_import_config_qrcode">Импорт из QR-кода</string>
|
||||
<string name="menu_item_import_config_clipboard">Импорт из буфера обмена</string>
|
||||
<string name="menu_item_import_config_manually_vmess">Ручной ввод Vmess</string>
|
||||
<string name="menu_item_import_config_manually_vless">Ручной ввод VLESS</string>
|
||||
<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>
|
||||
<string name="menu_item_import_config_custom_url">Импорт из URL</string>
|
||||
@@ -46,11 +46,9 @@
|
||||
<string name="server_lab_more_function">Другие параметры</string>
|
||||
<string name="server_lab_head_type">Тип заголовка</string>
|
||||
<string name="server_lab_mode_type">Режим gRPC</string>
|
||||
<string name="server_lab_request_host">Запрос узла (WS/H2) / Шифрование QUIC</string>
|
||||
<string name="server_lab_request_host">Запрос узла (WS/H2) / Шифрование QUIC/gRPC Authority</string>
|
||||
<string name="server_lab_path">Путь (WS/H2) / Ключ QUIC / Сид KCP / Сервис gRPC</string>
|
||||
<string name="server_lab_stream_security">TLS</string>
|
||||
<string name="server_lab_stream_fingerprint" translatable="false">Fingerprint</string>
|
||||
<string name="server_lab_stream_alpn" translatable="false">Alpn</string>
|
||||
<string name="server_lab_allow_insecure">Разрешать небезопасные</string>
|
||||
<string name="server_lab_sni">SNI</string>
|
||||
<string name="server_lab_address3">Адрес</string>
|
||||
@@ -61,9 +59,9 @@
|
||||
<string name="server_lab_security4">Пользователь (необязательно)</string>
|
||||
<string name="server_lab_encryption">Шифрование</string>
|
||||
<string name="server_lab_flow">Поток</string>
|
||||
<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_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>
|
||||
@@ -80,8 +78,13 @@
|
||||
<string name="toast_malformed_josn">Профиль повреждён</string>
|
||||
<string name="server_lab_request_host6">Узел (SNI) (необязательно)</string>
|
||||
<string name="toast_asset_copy_failed">Невозможно скопировать файл, используйте файловый менеджер</string>
|
||||
<string name="menu_item_add_asset">Добавить ресурс</string>
|
||||
<string name="menu_item_add_file">Добавить файлы</string>
|
||||
<string name="menu_item_add_url">Добавить URL</string>
|
||||
<string name="menu_item_download_file">Загрузить файлы</string>
|
||||
<string name="title_user_asset_add_url">Добавить URL ресурса</string>
|
||||
<string name="msg_file_not_found">Файл не найден</string>
|
||||
<string name="msg_remark_is_duplicate">Описание уже существует</string>
|
||||
|
||||
<!-- PerAppProxyActivity -->
|
||||
<string name="msg_dialog_progress">Загрузка…</string>
|
||||
@@ -100,10 +103,12 @@
|
||||
<string name="title_advanced">Расширенные настройки</string>
|
||||
<string name="title_vpn_settings">Настройки VPN</string>
|
||||
<string name="title_pref_per_app_proxy">Прокси для выбранных приложений</string>
|
||||
<string name="summary_pref_per_app_proxy">Основной: выделенное приложение соединяется через прокси, не выделенное — напрямую; \n\nРежим обхода: выделенное приложение соединяется напрямую, не выделенное — через прокси.\n\nЕсть возможность автоматического выбора проксируемых приложений в меню.</string>
|
||||
<string name="summary_pref_per_app_proxy">Основной: выделенное приложение соединяется через прокси, не выделенное — напрямую;\n\nРежим обхода: выделенное приложение соединяется напрямую, не выделенное — через прокси.\n\nЕсть возможность автоматического выбора проксируемых приложений в меню.</string>
|
||||
|
||||
<string name="title_mux_settings">Настройки мультиплексирования</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">
|
||||
@@ -141,7 +146,7 @@
|
||||
<string name="summary_pref_domestic_dns">DNS</string>
|
||||
|
||||
<string name="title_pref_proxy_sharing_enabled">Разрешать подключения из LAN</string>
|
||||
<string name="summary_pref_proxy_sharing_enabled">Другие устройства могут подключаться к прокси по вашему IP-адресу через протокол SOCKS/HTTP. Используйте только в надёжной сети, чтобы избежать несанкционированного подключения.</string>
|
||||
<string name="summary_pref_proxy_sharing_enabled">Другие устройства могут подключаться, используя ваш IP-адрес, чтобы использовать прокси по протоколам SOCKS/HTTP. Используйте только в доверенной сети, чтобы избежать несанкционированного подключения.</string>
|
||||
<string name="toast_warning_pref_proxysharing_short">Доступ из LAN разрешён, убедитесь, что вы находитесь в надёжной сети</string>
|
||||
|
||||
<string name="title_pref_allow_insecure">Разрешать небезопасные</string>
|
||||
@@ -166,13 +171,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>
|
||||
@@ -180,7 +186,7 @@
|
||||
<string name="title_language">Язык</string>
|
||||
<string name="title_ui_settings">Настройки интерфейса</string>
|
||||
|
||||
<string name="title_logcat">Системный журнал</string>
|
||||
<string name="title_logcat">Журнал</string>
|
||||
<string name="logcat_copy">Копировать</string>
|
||||
<string name="logcat_clear">Очистить</string>
|
||||
<string name="title_service_restart">Перезапуск службы</string>
|
||||
@@ -189,7 +195,7 @@
|
||||
<string name="title_del_invalid_config">Удалить нерабочие профили (после проверки)</string>
|
||||
<string name="title_export_all">Экспорт всех профилей в буфер обмена</string>
|
||||
<string name="title_sub_setting">Группы</string>
|
||||
<string name="sub_setting_remarks">примечания</string>
|
||||
<string name="sub_setting_remarks">Название</string>
|
||||
<string name="sub_setting_url">URL (необязательно)</string>
|
||||
<string name="sub_setting_enable">Использовать обновление</string>
|
||||
<string name="sub_auto_update">Использовать автоматическое обновление</string>
|
||||
@@ -198,7 +204,7 @@
|
||||
<string name="title_real_ping_all_server">Время отклика профилей</string>
|
||||
<string name="title_user_asset_setting">Файлы георесурсов</string>
|
||||
<string name="title_sort_by_test_results">Сортировка по результатам теста</string>
|
||||
<string name="title_filter_config">Фильтр профилей</string>
|
||||
<string name="title_filter_config">Фильтр групп</string>
|
||||
<string name="filter_config_all">Все группы</string>
|
||||
<string name="title_del_duplicate_config_count">Удалено дубликатов профилей: %d</string>
|
||||
|
||||
@@ -215,7 +221,7 @@
|
||||
|
||||
<string name="connection_test_pending">Проверить подключение</string>
|
||||
<string name="connection_test_testing">Проверка…</string>
|
||||
<string name="connection_test_available">Успешно: рукопожатие HTTP заняло %d мс</string>
|
||||
<string name="connection_test_available">Успешно: HTTP-соединение заняло %d мс</string>
|
||||
<string name="connection_test_error">Сбой проверки интернет-соединения: %s</string>
|
||||
<string name="connection_test_fail">Интернет недоступен</string>
|
||||
<string name="connection_test_error_status_code">Код ошибки: #%d</string>
|
||||
@@ -224,11 +230,16 @@
|
||||
|
||||
<string name="import_subscription_success">Подписка импортирована</string>
|
||||
<string name="import_subscription_failure">Невозможно импортировать подписку</string>
|
||||
<string name="title_fragment_settings">Настройки фрагментирования</string>
|
||||
<string name="title_pref_fragment_packets">Фрагментирование пакетов</string>
|
||||
<string name="title_pref_fragment_length">Длина фрагмента (от - до)</string>
|
||||
<string name="title_pref_fragment_interval">Интервал фрагментов (от - до)</string>
|
||||
<string name="title_pref_fragment_enabled">Использовать фрагментирование</string>
|
||||
|
||||
<string-array name="share_method">
|
||||
<item>QR-код</item>
|
||||
<item>Экспорт в буфер обмена</item>
|
||||
<item>Экспорт всего профиля в буфер обмена</item>
|
||||
<item>Экспорт всей конфигурации в буфер обмена</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="share_sub_method">
|
||||
|
||||
@@ -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/gRPC Authority</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ã hoá</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,17 +76,20 @@
|
||||
<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>
|
||||
|
||||
<!-- PerAppProxyActivity -->
|
||||
<string name="title_user_asset_add_url">Thêm URL nội dung</string>
|
||||
<string name="msg_file_not_found">Không tìm thấy tập tin</string>
|
||||
<string name="msg_remark_is_duplicate">Nhận xét đã tồn tại</string>
|
||||
<string name="msg_dialog_progress">Đang tải...</string>
|
||||
<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 +100,56 @@
|
||||
<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_mux_settings">Cài đặt 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,95 +157,101 @@
|
||||
<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 xoá 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="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_start_scan_immediate">Quét mã QR ngay lập tức</string>
|
||||
<string name="summary_pref_start_scan_immediate">Mở camera để quét mã QR ngay khi khởi động, nếu không, bạn cũng có thể chọn quét mã hoặc chọn ảnh từ thanh công cụ.</string>
|
||||
|
||||
<string name="title_pref_feedback">Phản hồi lỗi</string>
|
||||
<string name="summary_pref_feedback">Phản hồi cải tiến hoặc lỗi lên GitHub</string>
|
||||
<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_privacy_policy">Chính sách bảo mật</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 ký</string>
|
||||
<string name="title_pref_auto_update_subscription">Tự động cập nhật các gói đăng ký</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: Mất %d ms để truy cập Google.com</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 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>
|
||||
|
||||
@@ -244,5 +259,7 @@
|
||||
<item>Chế độ VPN</item>
|
||||
<item>Chế độ Proxy</item>
|
||||
</string-array>
|
||||
<string name="menu_item_add_asset">Thêm vào</string>
|
||||
<string name="menu_item_add_url">Thêm liên kết</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -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>
|
||||
@@ -45,8 +46,8 @@
|
||||
<string name="server_lab_more_function">底层传输方式(transport)</string>
|
||||
<string name="server_lab_head_type">伪装类型(type)</string>
|
||||
<string name="server_lab_mode_type">gRPC 传输模式(mode)</string>
|
||||
<string name="server_lab_request_host">伪装域名(host)(host/ws host/h2 host)/QUIC 加密方式</string>
|
||||
<string name="server_lab_path">path(ws path/h2 path)/QUIC 加密密钥/kcp seed/gRPC serviceName</string>
|
||||
<string name="server_lab_request_host">伪装域名(host)(host/ws host/httpupgrade host/h2 host)/QUIC 加密方式/gRPC Authority</string>
|
||||
<string name="server_lab_path">path(ws path/httpupgrade path/h2 path)/QUIC 加密密钥/kcp seed/gRPC serviceName</string>
|
||||
<string name="server_lab_stream_security">传输层安全(TLS)</string>
|
||||
<string name="server_lab_allow_insecure">跳过证书验证(allowInsecure)</string>
|
||||
<string name="server_lab_sni">SNI</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>
|
||||
@@ -79,6 +83,9 @@
|
||||
|
||||
|
||||
<!-- PerAppProxyActivity -->
|
||||
<string name="title_user_asset_add_url">添加资产网址</string>
|
||||
<string name="msg_file_not_found">文件未找到</string>
|
||||
<string name="msg_remark_is_duplicate">备注已经存在</string>
|
||||
<string name="msg_dialog_progress">正在加载</string>
|
||||
<string name="menu_item_search">搜索</string>
|
||||
<string name="menu_item_select_all">全选</string>
|
||||
@@ -96,8 +103,10 @@
|
||||
<string name="title_pref_per_app_proxy">分应用代理</string>
|
||||
<string name="summary_pref_per_app_proxy">常规:勾选的App被代理,未勾选的直连;\n绕行模式:勾选的App直连,未勾选的被代理.\n不明白者在菜单中选择自动选中需代理应用</string>
|
||||
|
||||
<string name="title_mux_settings">Mux 多路复用 设置</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">减低延时,但可能会断流,建议不要启用。\nTCP,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">
|
||||
@@ -160,13 +169,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>
|
||||
@@ -216,6 +226,16 @@
|
||||
<string name="connection_connected">"已连接,点击测试连接"</string>
|
||||
<string name="connection_not_connected">"未连接"</string>
|
||||
|
||||
<string name="import_subscription_success">订阅导入成功</string>
|
||||
<string name="import_subscription_failure">导入订阅失败</string>
|
||||
<string name="menu_item_add_asset">添加</string>
|
||||
<string name="menu_item_add_url">添加链接</string>
|
||||
<string name="title_fragment_settings">分片(Fragment) 设置</string>
|
||||
<string name="title_pref_fragment_packets">分片方式</string>
|
||||
<string name="title_pref_fragment_length">分片包长(最小-最大)</string>
|
||||
<string name="title_pref_fragment_interval">分片间隔(最小-最大)</string>
|
||||
<string name="title_pref_fragment_enabled">启用分片(Fragment)</string>
|
||||
|
||||
<string-array name="share_method">
|
||||
<item>二维码</item>
|
||||
<item>导出至剪贴板</item>
|
||||
@@ -246,7 +266,5 @@
|
||||
<item>VPN</item>
|
||||
<item>仅代理</item>
|
||||
</string-array>
|
||||
<string name="import_subscription_success">订阅导入成功</string>
|
||||
<string name="import_subscription_failure">导入订阅失败</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -18,22 +18,23 @@
|
||||
<string name="toast_services_failure">啟動服務失敗</string>
|
||||
|
||||
<!--ServerActivity-->
|
||||
<string name="title_server">組態檔案</string>
|
||||
<string name="menu_item_add_config">新增組態</string>
|
||||
<string name="menu_item_save_config">儲存組態</string>
|
||||
<string name="menu_item_del_config">刪除組態</string>
|
||||
<string name="menu_item_import_config_qrcode">從 QR Code 匯入組態</string>
|
||||
<string name="menu_item_import_config_clipboard">從剪貼簿匯入組態</string>
|
||||
<string name="title_server">配置檔案</string>
|
||||
<string name="menu_item_add_config">新增配置</string>
|
||||
<string name="menu_item_save_config">儲存配置</string>
|
||||
<string name="menu_item_del_config">刪除配置</string>
|
||||
<string name="menu_item_import_config_qrcode">從 QR Code 匯入配置</string>
|
||||
<string name="menu_item_import_config_clipboard">從剪貼簿匯入配置</string>
|
||||
<string name="menu_item_import_config_manually_vmess">手動鍵入 [Vmess]</string>
|
||||
<string name="menu_item_import_config_manually_vless">手動鍵入 [VLESS]</string>
|
||||
<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_custom">自訂組態</string>
|
||||
<string name="menu_item_import_config_custom_clipboard">從剪貼簿匯入自訂組態</string>
|
||||
<string name="menu_item_import_config_custom_local">從本地匯入自訂組態</string>
|
||||
<string name="menu_item_import_config_custom_url">從 URL 匯入自訂組態</string>
|
||||
<string name="menu_item_import_config_custom_url_scan">掃描 URL 匯入自訂組態</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>
|
||||
<string name="menu_item_import_config_custom_url">從 URL 匯入自訂配置</string>
|
||||
<string name="menu_item_import_config_custom_url_scan">掃描 URL 匯入自訂配置</string>
|
||||
<string name="del_config_comfirm">確定刪除?</string>
|
||||
<string name="server_lab_remarks">備註</string>
|
||||
<string name="server_lab_address">位址</string>
|
||||
@@ -45,8 +46,8 @@
|
||||
<string name="server_lab_more_function">底層傳輸方式 (transport)</string>
|
||||
<string name="server_lab_head_type">標頭類型</string>
|
||||
<string name="server_lab_mode_type">gRPC 傳輸模式 (mode)</string>
|
||||
<string name="server_lab_request_host">要求主機 (host)(host/ws host/h2 host)/QUIC 加密方式</string>
|
||||
<string name="server_lab_path">path(ws path/h2 path)/QUIC 加密金鑰/kcp seed/gRPC serviceName</string>
|
||||
<string name="server_lab_request_host">要求主機 (host)(host/ws host/httpupgrade host/h2 host)/QUIC 加密方式/gRPC Authority</string>
|
||||
<string name="server_lab_path">path(ws path/httpupgrade path/h2 path)/QUIC 加密金鑰/kcp seed/gRPC serviceName</string>
|
||||
<string name="server_lab_stream_security">傳輸層安全 (TLS)</string>
|
||||
<string name="server_lab_allow_insecure">跳過憑證驗證 (allowInsecure)</string>
|
||||
<string name="server_lab_sni">SNI</string>
|
||||
@@ -58,26 +59,32 @@
|
||||
<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>
|
||||
<string name="toast_incorrect_protocol">通訊協定不正確</string>
|
||||
<string name="toast_decoding_failed">解碼失敗</string>
|
||||
<string name="title_file_chooser">選取一個組態檔</string>
|
||||
<string name="title_file_chooser">選取一個配置檔</string>
|
||||
<string name="toast_require_file_manager">請安裝檔案總管。</string>
|
||||
<string name="server_customize_config">自訂組態</string>
|
||||
<string name="toast_config_file_invalid">無效組態</string>
|
||||
<string name="server_customize_config">自訂配置</string>
|
||||
<string name="toast_config_file_invalid">無效配置</string>
|
||||
<string name="server_lab_content">內容</string>
|
||||
<string name="toast_none_data_clipboard">剪貼簿內無資料</string>
|
||||
<string name="toast_invalid_url">URL 無效</string>
|
||||
<string name="server_lab_need_inbound">確保 inbounds port 和設定中的一致</string>
|
||||
<string name="toast_malformed_josn">組態格式不正確</string>
|
||||
<string name="toast_malformed_josn">配置格式不正確</string>
|
||||
<string name="server_lab_request_host6">Host(SNI)(可選)</string>
|
||||
<string name="toast_asset_copy_failed">失敗,請使用檔案總管</string>
|
||||
<string name="menu_item_add_file">新增檔案</string>
|
||||
<string name="menu_item_download_file">下載檔案</string>
|
||||
|
||||
<!-- PerAppProxyActivity -->
|
||||
<string name="title_user_asset_add_url">新增資產網址</string>
|
||||
<string name="msg_file_not_found">文件未找到</string>
|
||||
<string name="msg_remark_is_duplicate">備註已經存在</string>
|
||||
<string name="msg_dialog_progress">載入</string>
|
||||
<string name="menu_item_search">搜尋</string>
|
||||
<string name="menu_item_select_all">全選</string>
|
||||
@@ -96,8 +103,10 @@
|
||||
<string name="title_pref_per_app_proxy">Proxy 個別應用程式</string>
|
||||
<string name="summary_pref_per_app_proxy">常規:勾選的 App 啟用 Proxy,未勾選的直接連線;\n繞行模式:勾選的 App 直接連線,未勾選的啟用 Proxy。\n可在選單中選擇自動選中需 Proxy 應用</string>
|
||||
|
||||
<string name="title_mux_settings">Mux 設定</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">減低延時 但可能會斷流\nTCP,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">
|
||||
@@ -150,8 +159,8 @@
|
||||
<string name="title_pref_local_dns_port">本機 DNS 埠</string>
|
||||
<string name="summary_pref_local_dns_port">本機 DNS 埠</string>
|
||||
|
||||
<string name="title_pref_confirm_remove">刪除配置文件確認</string>
|
||||
<string name="summary_pref_confirm_remove">刪除配置文件是否需要用戶二次確認</string>
|
||||
<string name="title_pref_confirm_remove">刪除配置檔案確認</string>
|
||||
<string name="summary_pref_confirm_remove">刪除配置檔案是否需要用戶二次確認</string>
|
||||
|
||||
<string name="title_pref_start_scan_immediate">立即啟動掃碼</string>
|
||||
<string name="summary_pref_start_scan_immediate">啟動時立即打開相機掃描,否則可在工具欄選擇掃碼或選照片</string>
|
||||
@@ -160,39 +169,40 @@
|
||||
<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>
|
||||
<string name="title_mode_help">輕觸以檢視說明</string>
|
||||
<string name="title_language">語言</string>
|
||||
<string name="title_ui_settings">用戶界面設置</string>
|
||||
<string name="title_ui_settings">使用者介面設定</string>
|
||||
|
||||
<string name="title_logcat">Logcat</string>
|
||||
<string name="logcat_copy">複製</string>
|
||||
<string name="logcat_clear">清除</string>
|
||||
<string name="title_service_restart">服務重啟</string>
|
||||
<string name="title_del_all_config">刪除全部組態</string>
|
||||
<string name="title_del_duplicate_config">刪除重複組態</string>
|
||||
<string name="title_del_invalid_config">刪除無效組態 (先偵測)</string>
|
||||
<string name="title_export_all">匯出全部 (非自訂) 組態至剪貼簿</string>
|
||||
<string name="title_service_restart">重啟服務</string>
|
||||
<string name="title_del_all_config">刪除全部配置</string>
|
||||
<string name="title_del_duplicate_config">刪除重複配置</string>
|
||||
<string name="title_del_invalid_config">刪除無效配置 (先偵測)</string>
|
||||
<string name="title_export_all">匯出全部 (非自訂) 配置至剪貼簿</string>
|
||||
<string name="title_sub_setting">訂閱分組設定</string>
|
||||
<string name="sub_setting_remarks">備註</string>
|
||||
<string name="sub_setting_url">Optional URL</string>
|
||||
<string name="sub_setting_enable">啟用更新</string>
|
||||
<string name="sub_auto_update">啟用自動更新</string>
|
||||
<string name="title_sub_update">更新訂閱</string>
|
||||
<string name="title_ping_all_server">偵測所有組態 Tcping</string>
|
||||
<string name="title_real_ping_all_server">偵測所有組態真延遲</string>
|
||||
<string name="title_ping_all_server">偵測所有配置 Tcping</string>
|
||||
<string name="title_real_ping_all_server">偵測所有配置真延遲</string>
|
||||
<string name="title_user_asset_setting">Geo 資源檔案</string>
|
||||
<string name="title_sort_by_test_results">依偵測結果排序</string>
|
||||
<string name="title_filter_config">過濾組態</string>
|
||||
<string name="title_filter_config">過濾配置</string>
|
||||
<string name="filter_config_all">所有訂閱分組</string>
|
||||
<string name="title_del_duplicate_config_count">Delete %d duplicate configurations</string>
|
||||
|
||||
@@ -216,10 +226,20 @@
|
||||
<string name="connection_connected">"已連線,輕觸以檢查連線能力"</string>
|
||||
<string name="connection_not_connected">"未連線"</string>
|
||||
|
||||
<string name="import_subscription_success">匯入訂閱成功</string>
|
||||
<string name="import_subscription_failure">匯入訂閱失敗</string>
|
||||
<string name="menu_item_add_asset">添加</string>
|
||||
<string name="menu_item_add_url">添加連結</string>
|
||||
<string name="title_fragment_settings">分片(Fragment) 設定</string>
|
||||
<string name="title_pref_fragment_packets">分片方式</string>
|
||||
<string name="title_pref_fragment_length">分片包長(最小-最大)</string>
|
||||
<string name="title_pref_fragment_interval">分片間隔(最小-最大)</string>
|
||||
<string name="title_pref_fragment_enabled">啟用分片(Fragment)</string>
|
||||
|
||||
<string-array name="share_method">
|
||||
<item>QR Code</item>
|
||||
<item>匯出至剪貼簿</item>
|
||||
<item>匯出完整組態至剪貼簿</item>
|
||||
<item>匯出完整配置至剪貼簿</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="share_sub_method">
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
<item>tcp</item>
|
||||
<item>kcp</item>
|
||||
<item>ws</item>
|
||||
<item>httpupgrade</item>
|
||||
<item>h2</item>
|
||||
<item>quic</item>
|
||||
<item>grpc</item>
|
||||
@@ -60,6 +61,13 @@
|
||||
<item>reality</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="fragment_packets" translatable="false">
|
||||
<item>tlshello</item>
|
||||
<item>1-2</item>
|
||||
<item>1-3</item>
|
||||
<item>1-5</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="streamsecurity_utls" translatable="false">
|
||||
<item></item>
|
||||
<item>chrome</item>
|
||||
@@ -76,8 +84,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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -46,8 +47,8 @@
|
||||
<string name="server_lab_more_function">Transport</string>
|
||||
<string name="server_lab_head_type">head type</string>
|
||||
<string name="server_lab_mode_type">gRPC mode</string>
|
||||
<string name="server_lab_request_host">request 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_request_host">request host(host/ws host/httpupgrade host/h2 host)/QUIC security/gRPC Authority</string>
|
||||
<string name="server_lab_path">path(ws path/httpupgrade path/h2 path)/QUIC key/kcp seed/gRPC serviceName</string>
|
||||
<string name="server_lab_stream_security">TLS</string>
|
||||
<string name="server_lab_stream_fingerprint" translatable="false">Fingerprint</string>
|
||||
<string name="server_lab_stream_alpn" translatable="false">Alpn</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>
|
||||
@@ -80,8 +85,15 @@
|
||||
<string name="toast_malformed_josn">Config malformed</string>
|
||||
<string name="server_lab_request_host6">Host(SNI)(Optional)</string>
|
||||
<string name="toast_asset_copy_failed">File copy failed, please use File Manager</string>
|
||||
<string name="menu_item_add_asset">Add asset</string>
|
||||
<string name="menu_item_add_file">Add files</string>
|
||||
<string name="menu_item_add_url">Add URL</string>
|
||||
<string name="title_url" translatable="false">URL</string>
|
||||
<string name="menu_item_download_file">Download files</string>
|
||||
<string name="title_user_asset_add_url">Add asset URL</string>
|
||||
<string name="msg_file_not_found">File not found</string>
|
||||
<string name="msg_remark_is_duplicate">The remarks already exists</string>
|
||||
|
||||
|
||||
<!-- PerAppProxyActivity -->
|
||||
<string name="msg_dialog_progress">Loading</string>
|
||||
@@ -102,8 +114,10 @@
|
||||
<string name="title_pref_per_app_proxy">Per-app proxy</string>
|
||||
<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_mux_settings">Mux Settings</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 connections,customize 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 connections(range -1 to 1024)</string>
|
||||
<string name="title_pref_mux_xudp_concurency">XUDP connections(range -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 +182,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>
|
||||
@@ -226,6 +241,11 @@
|
||||
|
||||
<string name="import_subscription_success">Subscription imported Successfully</string>
|
||||
<string name="import_subscription_failure">Import subscription failed</string>
|
||||
<string name="title_fragment_settings">Fragment Settings</string>
|
||||
<string name="title_pref_fragment_packets">Fragment Packets</string>
|
||||
<string name="title_pref_fragment_length">Fragment Length (min-max)</string>
|
||||
<string name="title_pref_fragment_interval">Fragment Interval (min-max)</string>
|
||||
<string name="title_pref_fragment_enabled">Enable Fragment</string>
|
||||
|
||||
<string-array name="share_method">
|
||||
<item>QRcode</item>
|
||||
|
||||
@@ -7,25 +7,6 @@
|
||||
android:summary="@string/summary_pref_sniffing_enabled"
|
||||
android:title="@string/title_pref_sniffing_enabled" />
|
||||
|
||||
<CheckBoxPreference
|
||||
android:key="pref_mux_enabled"
|
||||
android:summary="@string/summary_pref_mux_enabled"
|
||||
android:title="@string/title_pref_mux_enabled" />
|
||||
|
||||
<EditTextPreference
|
||||
android:key="pref_mux_xudp_concurency"
|
||||
android:summary="8"
|
||||
android:inputType="number"
|
||||
android:title="@string/title_pref_mux_xudp_concurency" />
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="reject"
|
||||
android:entries="@array/mux_xudp_quic_entries"
|
||||
android:entryValues="@array/mux_xudp_quic_value"
|
||||
android:key="pref_mux_xudp_quic"
|
||||
android:summary="%s"
|
||||
android:title="@string/title_pref_mux_xudp_quic" />
|
||||
|
||||
<PreferenceCategory android:title="@string/title_vpn_settings">
|
||||
<CheckBoxPreference
|
||||
android:key="pref_per_app_proxy"
|
||||
@@ -78,6 +59,57 @@
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/title_mux_settings">
|
||||
<CheckBoxPreference
|
||||
android:key="pref_mux_enabled"
|
||||
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"
|
||||
android:inputType="number"
|
||||
android:title="@string/title_pref_mux_xudp_concurency" />
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="reject"
|
||||
android:entries="@array/mux_xudp_quic_entries"
|
||||
android:entryValues="@array/mux_xudp_quic_value"
|
||||
android:key="pref_mux_xudp_quic"
|
||||
android:summary="%s"
|
||||
android:title="@string/title_pref_mux_xudp_quic" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/title_fragment_settings">
|
||||
<CheckBoxPreference
|
||||
android:key="pref_fragment_enabled"
|
||||
android:title="@string/title_pref_fragment_enabled"/>
|
||||
|
||||
<ListPreference
|
||||
android:key="pref_fragment_packets"
|
||||
android:defaultValue="tlshello"
|
||||
android:entries="@array/fragment_packets"
|
||||
android:entryValues="@array/fragment_packets"
|
||||
android:summary="%s"
|
||||
android:title="@string/title_pref_fragment_packets" />
|
||||
|
||||
<EditTextPreference
|
||||
android:key="pref_fragment_length"
|
||||
android:summary="50-100"
|
||||
android:title="@string/title_pref_fragment_length" />
|
||||
|
||||
<EditTextPreference
|
||||
android:key="pref_fragment_interval"
|
||||
android:summary="10-20"
|
||||
android:title="@string/title_pref_fragment_interval" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/title_sub_setting">
|
||||
<CheckBoxPreference
|
||||
android:key="pref_auto_update_subscription"
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
plugins {
|
||||
id 'com.android.application' version '7.4.2' apply false
|
||||
id 'com.android.library' version '7.4.2' apply false
|
||||
id 'org.jetbrains.kotlin.android' version '1.8.0' apply false
|
||||
}
|
||||
6
V2rayNG/build.gradle.kts
Normal file
6
V2rayNG/build.gradle.kts
Normal file
@@ -0,0 +1,6 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
plugins {
|
||||
id("com.android.application") version "8.2.2" apply false
|
||||
id("com.android.library") version "8.2.2" apply false
|
||||
id("org.jetbrains.kotlin.android") version "1.9.23" apply false
|
||||
}
|
||||
@@ -1,7 +1,3 @@
|
||||
|
||||
buildToolsVer=33.0.2
|
||||
compileSdkVer=33
|
||||
targetSdkVer=33
|
||||
kotlin.incremental=true
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
|
||||
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
|
||||
|
||||
@@ -14,4 +14,4 @@ dependencyResolutionManagement {
|
||||
}
|
||||
}
|
||||
rootProject.name = "V2rayNG"
|
||||
include ':app'
|
||||
include(":app")
|
||||
Reference in New Issue
Block a user