Compare commits
168 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
2c80521f5b | ||
|
|
6351ce5991 | ||
|
|
683362f0ee | ||
|
|
84fc909339 | ||
|
|
74171e26db | ||
|
|
f25c0cc890 | ||
|
|
29848053a4 | ||
|
|
0d9856919e | ||
|
|
0ae7f2f7b3 | ||
|
|
3dde6b0ca3 | ||
|
|
6b28208044 | ||
|
|
13f855e3c4 | ||
|
|
c870595e98 | ||
|
|
ae19a3f68d | ||
|
|
545afc41b3 | ||
|
|
7177d88144 | ||
|
|
294ed50afd | ||
|
|
0105fe48f7 | ||
|
|
c7ff23e3d5 | ||
|
|
23e9d7fde5 | ||
|
|
c93edd8875 | ||
|
|
bde37e38a7 | ||
|
|
c401d63d2f | ||
|
|
52416dd43d | ||
|
|
59bd7128ae | ||
|
|
87f16467bb | ||
|
|
bed0fd00bd | ||
|
|
0672af98f8 | ||
|
|
caa2edcf05 | ||
|
|
dbe78d0aa5 | ||
|
|
71f2f590a7 | ||
|
|
ed26120581 | ||
|
|
71bd684b46 | ||
|
|
02ae19f0c7 | ||
|
|
062c0d8ddb | ||
|
|
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,135 +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 522
|
|
||||||
versionName "1.8.8"
|
|
||||||
}
|
|
||||||
|
|
||||||
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'
|
|
||||||
}
|
|
||||||
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 = 546
|
||||||
|
versionName = "1.8.16"
|
||||||
|
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.22")
|
||||||
|
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"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<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
|
<supports-screens
|
||||||
android:anyDensity="true"
|
android:anyDensity="true"
|
||||||
@@ -26,6 +27,9 @@
|
|||||||
<!-- <useapplications-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> -->
|
<!-- <useapplications-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> -->
|
||||||
<uses-permission android:name="android.permission.CAMERA" />
|
<uses-permission android:name="android.permission.CAMERA" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<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.ACCESS_NETWORK_STATE" /> -->
|
||||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
||||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
||||||
@@ -50,7 +54,7 @@
|
|||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
<category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
|
<!-- <category android:name="android.intent.category.LEANBACK_LAUNCHER"/>-->
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" />
|
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" />
|
||||||
@@ -89,6 +93,9 @@
|
|||||||
<activity
|
<activity
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:name=".ui.UserAssetActivity" />
|
android:name=".ui.UserAssetActivity" />
|
||||||
|
<activity
|
||||||
|
android:exported="false"
|
||||||
|
android:name=".ui.UserAssetUrlActivity" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
@@ -127,6 +134,7 @@
|
|||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:permission="android.permission.BIND_VPN_SERVICE"
|
android:permission="android.permission.BIND_VPN_SERVICE"
|
||||||
|
android:foregroundServiceType="specialUse"
|
||||||
android:process=":RunSoLibV2RayDaemon">
|
android:process=":RunSoLibV2RayDaemon">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.net.VpnService" />
|
<action android:name="android.net.VpnService" />
|
||||||
@@ -134,12 +142,19 @@
|
|||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.net.VpnService.SUPPORTS_ALWAYS_ON"
|
android:name="android.net.VpnService.SUPPORTS_ALWAYS_ON"
|
||||||
android:value="true" />
|
android:value="true" />
|
||||||
|
<property
|
||||||
|
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
|
||||||
|
android:value="vpn" />
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service android:name=".service.V2RayProxyOnlyService"
|
<service android:name=".service.V2RayProxyOnlyService"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
|
android:foregroundServiceType="specialUse"
|
||||||
android:process=":RunSoLibV2RayDaemon">
|
android:process=":RunSoLibV2RayDaemon">
|
||||||
|
<property
|
||||||
|
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
|
||||||
|
android:value="proxy" />
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service android:name=".service.V2RayTestService"
|
<service android:name=".service.V2RayTestService"
|
||||||
@@ -166,11 +181,15 @@
|
|||||||
android:name=".service.QSTileService"
|
android:name=".service.QSTileService"
|
||||||
android:icon="@drawable/ic_stat_name"
|
android:icon="@drawable/ic_stat_name"
|
||||||
android:label="@string/app_tile_name"
|
android:label="@string/app_tile_name"
|
||||||
|
android:foregroundServiceType="specialUse"
|
||||||
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
|
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
|
||||||
android:process=":RunSoLibV2RayDaemon">
|
android:process=":RunSoLibV2RayDaemon">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.service.quicksettings.action.QS_TILE" />
|
<action android:name="android.service.quicksettings.action.QS_TILE" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<property
|
||||||
|
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
|
||||||
|
android:value="tile" />
|
||||||
</service>
|
</service>
|
||||||
<!-- =====================Tasker===================== -->
|
<!-- =====================Tasker===================== -->
|
||||||
<activity
|
<activity
|
||||||
@@ -192,6 +211,18 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
<!-- =====================Tasker===================== -->
|
<!-- =====================Tasker===================== -->
|
||||||
|
<provider
|
||||||
|
android:name="androidx.startup.InitializationProvider"
|
||||||
|
android:authorities="${applicationId}.androidx-startup"
|
||||||
|
android:exported="false"
|
||||||
|
tools:node="merge">
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="androidx.work.WorkManagerInitializer"
|
||||||
|
android:value="androidx.startup"
|
||||||
|
tools:node="remove" />
|
||||||
|
|
||||||
|
</provider>
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,20 @@
|
|||||||
package com.v2ray.ang
|
package com.v2ray.ang
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import androidx.multidex.MultiDexApplication
|
import androidx.multidex.MultiDexApplication
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
|
import androidx.work.Configuration
|
||||||
import com.tencent.mmkv.MMKV
|
import com.tencent.mmkv.MMKV
|
||||||
|
|
||||||
class AngApplication : MultiDexApplication() {
|
class AngApplication : MultiDexApplication(), Configuration.Provider {
|
||||||
companion object {
|
companion object {
|
||||||
const val PREF_LAST_VERSION = "pref_last_version"
|
const val PREF_LAST_VERSION = "pref_last_version"
|
||||||
|
lateinit var application: AngApplication
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun attachBaseContext(base: Context?) {
|
||||||
|
super.attachBaseContext(base)
|
||||||
|
application = this
|
||||||
}
|
}
|
||||||
|
|
||||||
var firstRun = false
|
var firstRun = false
|
||||||
@@ -25,4 +33,10 @@ class AngApplication : MultiDexApplication() {
|
|||||||
//Logger.init().logLevel(if (BuildConfig.DEBUG) LogLevel.FULL else LogLevel.NONE)
|
//Logger.init().logLevel(if (BuildConfig.DEBUG) LogLevel.FULL else LogLevel.NONE)
|
||||||
MMKV.initialize(this)
|
MMKV.initialize(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getWorkManagerConfiguration(): Configuration {
|
||||||
|
return Configuration.Builder()
|
||||||
|
.setDefaultProcessName("${BuildConfig.APPLICATION_ID}:bg")
|
||||||
|
.build()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,9 +40,18 @@ object AppConfig {
|
|||||||
const val PREF_BYPASS_APPS = "pref_bypass_apps"
|
const val PREF_BYPASS_APPS = "pref_bypass_apps"
|
||||||
const val PREF_CONFIRM_REMOVE = "pref_confirm_remove"
|
const val PREF_CONFIRM_REMOVE = "pref_confirm_remove"
|
||||||
const val PREF_START_SCAN_IMMEDIATE = "pref_start_scan_immediate"
|
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 PROTOCOL_HTTP: String = "http://"
|
||||||
const val HTTPS_PROTOCOL: String = "https://"
|
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_SERVICE = "com.v2ray.ang.action.service"
|
||||||
const val BROADCAST_ACTION_ACTIVITY = "com.v2ray.ang.action.activity"
|
const val BROADCAST_ACTION_ACTIVITY = "com.v2ray.ang.action.activity"
|
||||||
@@ -57,13 +66,17 @@ object AppConfig {
|
|||||||
const val TAG_AGENT = "proxy"
|
const val TAG_AGENT = "proxy"
|
||||||
const val TAG_DIRECT = "direct"
|
const val TAG_DIRECT = "direct"
|
||||||
const val TAG_BLOCKED = "block"
|
const val TAG_BLOCKED = "block"
|
||||||
|
const val TAG_FRAGMENT = "fragment"
|
||||||
|
|
||||||
const val androidpackagenamelistUrl = "https://raw.githubusercontent.com/2dust/androidpackagenamelist/master/proxy.txt"
|
const val androidpackagenamelistUrl =
|
||||||
const val v2rayCustomRoutingListUrl = "https://raw.githubusercontent.com/2dust/v2rayCustomRoutingList/master/"
|
"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 v2rayNGIssues = "https://github.com/2dust/v2rayNG/issues"
|
||||||
const val v2rayNGWikiMode = "https://github.com/2dust/v2rayNG/wiki/Mode"
|
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 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_AGENT = "1.1.1.1"
|
||||||
const val DNS_DIRECT = "223.5.5.5"
|
const val DNS_DIRECT = "223.5.5.5"
|
||||||
@@ -71,6 +84,9 @@ object AppConfig {
|
|||||||
const val PORT_LOCAL_DNS = "10853"
|
const val PORT_LOCAL_DNS = "10853"
|
||||||
const val PORT_SOCKS = "10808"
|
const val PORT_SOCKS = "10808"
|
||||||
const val PORT_HTTP = "10809"
|
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_REGISTER_CLIENT = 1
|
||||||
const val MSG_STATE_RUNNING = 11
|
const val MSG_STATE_RUNNING = 11
|
||||||
@@ -87,4 +103,13 @@ object AppConfig {
|
|||||||
const val MSG_MEASURE_CONFIG = 7
|
const val MSG_MEASURE_CONFIG = 7
|
||||||
const val MSG_MEASURE_CONFIG_SUCCESS = 71
|
const val MSG_MEASURE_CONFIG_SUCCESS = 71
|
||||||
const val MSG_MEASURE_CONFIG_CANCEL = 72
|
const val MSG_MEASURE_CONFIG_CANCEL = 72
|
||||||
|
|
||||||
|
// subscription settings
|
||||||
|
const val SUBSCRIPTION_AUTO_UPDATE = "pref_auto_update_subscription"
|
||||||
|
const val SUBSCRIPTION_AUTO_UPDATE_INTERVAL = "pref_auto_update_interval"
|
||||||
|
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) {
|
when(configType) {
|
||||||
EConfigType.VMESS, EConfigType.VLESS ->
|
EConfigType.VMESS, EConfigType.VLESS ->
|
||||||
return ServerConfig(
|
return ServerConfig(
|
||||||
configType = configType,
|
configType = configType,
|
||||||
outboundBean = V2rayConfig.OutboundBean(
|
outboundBean = V2rayConfig.OutboundBean(
|
||||||
protocol = configType.name.lowercase(),
|
protocol = configType.name.lowercase(),
|
||||||
settings = V2rayConfig.OutboundBean.OutSettingsBean(
|
settings = V2rayConfig.OutboundBean.OutSettingsBean(
|
||||||
vnext = listOf(V2rayConfig.OutboundBean.OutSettingsBean.VnextBean(
|
vnext = listOf(V2rayConfig.OutboundBean.OutSettingsBean.VnextBean(
|
||||||
users = listOf(V2rayConfig.OutboundBean.OutSettingsBean.VnextBean.UsersBean())))),
|
users = listOf(V2rayConfig.OutboundBean.OutSettingsBean.VnextBean.UsersBean())))),
|
||||||
streamSettings = V2rayConfig.OutboundBean.StreamSettingsBean()))
|
streamSettings = V2rayConfig.OutboundBean.StreamSettingsBean()))
|
||||||
EConfigType.CUSTOM, EConfigType.WIREGUARD ->
|
EConfigType.CUSTOM ->
|
||||||
return ServerConfig(configType = configType)
|
return ServerConfig(configType = configType)
|
||||||
EConfigType.SHADOWSOCKS, EConfigType.SOCKS, EConfigType.TROJAN ->
|
EConfigType.SHADOWSOCKS, EConfigType.SOCKS, EConfigType.TROJAN ->
|
||||||
return ServerConfig(
|
return ServerConfig(
|
||||||
configType = configType,
|
configType = configType,
|
||||||
outboundBean = V2rayConfig.OutboundBean(
|
outboundBean = V2rayConfig.OutboundBean(
|
||||||
protocol = configType.name.lowercase(),
|
protocol = configType.name.lowercase(),
|
||||||
settings = V2rayConfig.OutboundBean.OutSettingsBean(
|
settings = V2rayConfig.OutboundBean.OutSettingsBean(
|
||||||
servers = listOf(V2rayConfig.OutboundBean.OutSettingsBean.ServersBean())),
|
servers = listOf(V2rayConfig.OutboundBean.OutSettingsBean.ServersBean())),
|
||||||
streamSettings = V2rayConfig.OutboundBean.StreamSettingsBean()))
|
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 {
|
fun getV2rayPointDomainAndPort(): String {
|
||||||
val address = getProxyOutbound()?.getServerAddress().orEmpty()
|
val address = getProxyOutbound()?.getServerAddress().orEmpty()
|
||||||
val port = getProxyOutbound()?.getServerPort()
|
val port = getProxyOutbound()?.getServerPort()
|
||||||
return if (Utils.isIpv6Address(address)) {
|
return Utils.getIpv6Address(address) + ":" + port
|
||||||
String.format("[%s]:%s", address, port)
|
|
||||||
} else {
|
|
||||||
String.format("%s:%s", address, port)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
package com.v2ray.ang.dto
|
package com.v2ray.ang.dto
|
||||||
|
|
||||||
data class SubscriptionItem(
|
data class SubscriptionItem(
|
||||||
var remarks: String = "",
|
var remarks: String = "",
|
||||||
var url: String = "",
|
var url: String = "",
|
||||||
var enabled: Boolean = true,
|
var enabled: Boolean = true,
|
||||||
val addedTime: Long = System.currentTimeMillis()) {
|
val addedTime: Long = System.currentTimeMillis(),
|
||||||
}
|
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
|
import java.lang.reflect.Type
|
||||||
|
|
||||||
data class V2rayConfig(
|
data class V2rayConfig(
|
||||||
|
var remarks: String? = null,
|
||||||
var stats: Any? = null,
|
var stats: Any? = null,
|
||||||
val log: LogBean,
|
val log: LogBean,
|
||||||
var policy: PolicyBean?,
|
var policy: PolicyBean?,
|
||||||
@@ -60,21 +61,22 @@ data class V2rayConfig(
|
|||||||
val metadataOnly: Boolean? = null)
|
val metadataOnly: Boolean? = null)
|
||||||
}
|
}
|
||||||
|
|
||||||
data class OutboundBean(val tag: String = "proxy",
|
data class OutboundBean(var tag: String = "proxy",
|
||||||
var protocol: String,
|
var protocol: String,
|
||||||
var settings: OutSettingsBean? = null,
|
var settings: OutSettingsBean? = null,
|
||||||
var streamSettings: StreamSettingsBean? = null,
|
var streamSettings: StreamSettingsBean? = null,
|
||||||
val proxySettings: Any? = null,
|
val proxySettings: Any? = null,
|
||||||
val sendThrough: String? = null,
|
val sendThrough: String? = null,
|
||||||
val mux: MuxBean? = MuxBean(false)) {
|
var mux: MuxBean? = MuxBean(false)) {
|
||||||
|
|
||||||
data class OutSettingsBean(var vnext: List<VnextBean>? = null,
|
data class OutSettingsBean(var vnext: List<VnextBean>? = null,
|
||||||
|
var fragment: FragmentBean? = null,
|
||||||
var servers: List<ServersBean>? = null,
|
var servers: List<ServersBean>? = null,
|
||||||
/*Blackhole*/
|
/*Blackhole*/
|
||||||
var response: Response? = null,
|
var response: Response? = null,
|
||||||
/*DNS*/
|
/*DNS*/
|
||||||
val network: String? = null,
|
val network: String? = null,
|
||||||
val address: Any? = null,
|
var address: Any? = null,
|
||||||
val port: Int? = null,
|
val port: Int? = null,
|
||||||
/*Freedom*/
|
/*Freedom*/
|
||||||
var domainStrategy: String? = null,
|
var domainStrategy: String? = null,
|
||||||
@@ -83,8 +85,10 @@ data class V2rayConfig(
|
|||||||
/*Loopback*/
|
/*Loopback*/
|
||||||
val inboundTag: String? = null,
|
val inboundTag: String? = null,
|
||||||
/*Wireguard*/
|
/*Wireguard*/
|
||||||
val secretKey: String? = null,
|
var secretKey: String? = null,
|
||||||
val peers: List<WireGuardBean>? = null,
|
val peers: List<WireGuardBean>? = null,
|
||||||
|
var reserved: List<Int>? = null,
|
||||||
|
var mtu :Int? = null
|
||||||
) {
|
) {
|
||||||
|
|
||||||
data class VnextBean(var address: String = "",
|
data class VnextBean(var address: String = "",
|
||||||
@@ -99,6 +103,10 @@ data class V2rayConfig(
|
|||||||
var flow: String = "")
|
var flow: String = "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class FragmentBean(var packets: String? = null,
|
||||||
|
var length: String? = null,
|
||||||
|
var interval: String? = null)
|
||||||
|
|
||||||
data class ServersBean(var address: String = "",
|
data class ServersBean(var address: String = "",
|
||||||
var method: String = "chacha20-poly1305",
|
var method: String = "chacha20-poly1305",
|
||||||
var ota: Boolean = false,
|
var ota: Boolean = false,
|
||||||
@@ -133,7 +141,7 @@ data class V2rayConfig(
|
|||||||
var realitySettings: TlsSettingsBean? = null,
|
var realitySettings: TlsSettingsBean? = null,
|
||||||
var grpcSettings: GrpcSettingsBean? = null,
|
var grpcSettings: GrpcSettingsBean? = null,
|
||||||
val dsSettings: Any? = null,
|
val dsSettings: Any? = null,
|
||||||
val sockopt: Any? = null
|
var sockopt: SockoptBean? = null
|
||||||
) {
|
) {
|
||||||
|
|
||||||
data class TcpSettingsBean(var header: HeaderBean = HeaderBean(),
|
data class TcpSettingsBean(var header: HeaderBean = HeaderBean(),
|
||||||
@@ -179,6 +187,13 @@ data class V2rayConfig(
|
|||||||
data class HttpSettingsBean(var host: List<String> = ArrayList(),
|
data class HttpSettingsBean(var host: List<String> = ArrayList(),
|
||||||
var path: String = "")
|
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,
|
data class TlsSettingsBean(var allowInsecure: Boolean = false,
|
||||||
var serverName: String = "",
|
var serverName: String = "",
|
||||||
val alpn: List<String>? = null,
|
val alpn: List<String>? = null,
|
||||||
@@ -292,7 +307,10 @@ data class V2rayConfig(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class MuxBean(var enabled: Boolean, var concurrency: Int = 8)
|
data class MuxBean(var enabled: Boolean,
|
||||||
|
var concurrency: Int = 8,
|
||||||
|
var xudpConcurrency: Int = 8,
|
||||||
|
var xudpProxyUDP443: String = "",)
|
||||||
|
|
||||||
fun getServerAddress(): String? {
|
fun getServerAddress(): String? {
|
||||||
if (protocol.equals(EConfigType.VMESS.name, true)
|
if (protocol.equals(EConfigType.VMESS.name, true)
|
||||||
|
|||||||
@@ -17,16 +17,16 @@ val Context.v2RayApplication: AngApplication
|
|||||||
get() = applicationContext as AngApplication
|
get() = applicationContext as AngApplication
|
||||||
|
|
||||||
fun Context.toast(message: Int): Toast = ToastCompat
|
fun Context.toast(message: Int): Toast = ToastCompat
|
||||||
.makeText(this, message, Toast.LENGTH_SHORT)
|
.makeText(this, message, Toast.LENGTH_SHORT)
|
||||||
.apply {
|
.apply {
|
||||||
show()
|
show()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Context.toast(message: CharSequence): Toast = ToastCompat
|
fun Context.toast(message: CharSequence): Toast = ToastCompat
|
||||||
.makeText(this, message, Toast.LENGTH_SHORT)
|
.makeText(this, message, Toast.LENGTH_SHORT)
|
||||||
.apply {
|
.apply {
|
||||||
show()
|
show()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun JSONObject.putOpt(pair: Pair<String, Any>) = putOpt(pair.first, pair.second)
|
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) }
|
fun JSONObject.putOpt(pairs: Map<String, Any>) = pairs.forEach { putOpt(it.key to it.value) }
|
||||||
@@ -77,4 +77,8 @@ val URLConnection.responseLength: Long
|
|||||||
get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) contentLengthLong else contentLength.toLong()
|
get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) contentLengthLong else contentLength.toLong()
|
||||||
|
|
||||||
val URI.idnHost: String
|
val URI.idnHost: String
|
||||||
get() = (host!!).replace("[", "").replace("]", "")
|
get() = (host!!).replace("[", "").replace("]", "")
|
||||||
|
|
||||||
|
fun String.removeWhiteSpace(): String {
|
||||||
|
return this.replace(" ", "")
|
||||||
|
}
|
||||||
@@ -36,7 +36,12 @@ class QSTileService : TileService() {
|
|||||||
super.onStartListening()
|
super.onStartListening()
|
||||||
setState(Tile.STATE_INACTIVE)
|
setState(Tile.STATE_INACTIVE)
|
||||||
mMsgReceive = ReceiveMessageHandler(this)
|
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, "")
|
MessageUtil.sendMsg2Service(this, AppConfig.MSG_REGISTER_CLIENT, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,80 @@
|
|||||||
|
package com.v2ray.ang.service
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.app.NotificationChannel
|
||||||
|
import android.app.NotificationManager
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Build
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.core.app.NotificationCompat
|
||||||
|
import androidx.core.app.NotificationManagerCompat
|
||||||
|
import androidx.work.CoroutineWorker
|
||||||
|
import androidx.work.WorkerParameters
|
||||||
|
import com.v2ray.ang.AppConfig
|
||||||
|
import com.v2ray.ang.R
|
||||||
|
import com.v2ray.ang.util.AngConfigManager
|
||||||
|
import com.v2ray.ang.util.MmkvManager
|
||||||
|
import com.v2ray.ang.util.Utils
|
||||||
|
|
||||||
|
object SubscriptionUpdater {
|
||||||
|
|
||||||
|
const val notificationChannel = "subscription_update_channel"
|
||||||
|
|
||||||
|
class UpdateTask(context: Context, params: WorkerParameters) :
|
||||||
|
CoroutineWorker(context, params) {
|
||||||
|
|
||||||
|
private val notificationManager = NotificationManagerCompat.from(applicationContext)
|
||||||
|
private val notification =
|
||||||
|
NotificationCompat.Builder(applicationContext, notificationChannel)
|
||||||
|
.setWhen(0)
|
||||||
|
.setTicker("Update")
|
||||||
|
.setContentTitle(context.getString(R.string.title_pref_auto_update_subscription))
|
||||||
|
.setSmallIcon(R.drawable.ic_stat_name)
|
||||||
|
.setCategory(NotificationCompat.CATEGORY_SERVICE)
|
||||||
|
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
||||||
|
|
||||||
|
@SuppressLint("MissingPermission")
|
||||||
|
override suspend fun doWork(): Result {
|
||||||
|
Log.d(AppConfig.ANG_PACKAGE, "subscription automatic update starting")
|
||||||
|
|
||||||
|
val subs = MmkvManager.decodeSubscriptions().filter { it.second.autoUpdate }
|
||||||
|
|
||||||
|
for (i in subs) {
|
||||||
|
val subscription = i.second
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
notification.setChannelId(notificationChannel)
|
||||||
|
val channel =
|
||||||
|
NotificationChannel(
|
||||||
|
notificationChannel,
|
||||||
|
"Subscription Update Service",
|
||||||
|
NotificationManager.IMPORTANCE_MIN
|
||||||
|
)
|
||||||
|
notificationManager.createNotificationChannel(channel)
|
||||||
|
}
|
||||||
|
notificationManager.notify(3, notification.build())
|
||||||
|
Log.d(
|
||||||
|
AppConfig.ANG_PACKAGE,
|
||||||
|
"subscription automatic update: ---${subscription.remarks}"
|
||||||
|
)
|
||||||
|
val configs = Utils.getUrlContentWithCustomUserAgent(subscription.url)
|
||||||
|
importBatchConfig(configs, i.first)
|
||||||
|
notification.setContentText("Updating ${subscription.remarks}")
|
||||||
|
}
|
||||||
|
notificationManager.cancel(3)
|
||||||
|
return Result.success()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun importBatchConfig(server: String?, subid: String = "") {
|
||||||
|
val append = subid.isEmpty()
|
||||||
|
|
||||||
|
val count = AngConfigManager.importBatchConfig(server, subid, append)
|
||||||
|
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) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
Seq.setContext(value?.get()?.getService()?.applicationContext)
|
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
|
var currentConfig: ServerConfig? = null
|
||||||
|
|
||||||
@@ -133,7 +133,11 @@ object V2RayServiceManager {
|
|||||||
mFilter.addAction(Intent.ACTION_SCREEN_ON)
|
mFilter.addAction(Intent.ACTION_SCREEN_ON)
|
||||||
mFilter.addAction(Intent.ACTION_SCREEN_OFF)
|
mFilter.addAction(Intent.ACTION_SCREEN_OFF)
|
||||||
mFilter.addAction(Intent.ACTION_USER_PRESENT)
|
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) {
|
} catch (e: Exception) {
|
||||||
Log.d(ANG_PACKAGE, e.toString())
|
Log.d(ANG_PACKAGE, e.toString())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ class V2RayTestService : Service() {
|
|||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
Seq.setContext(this)
|
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 {
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||||
|
|||||||
@@ -2,13 +2,25 @@ package com.v2ray.ang.ui
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import android.os.Bundle
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.view.WindowCompat
|
||||||
import com.v2ray.ang.util.MyContextWrapper
|
import com.v2ray.ang.util.MyContextWrapper
|
||||||
import com.v2ray.ang.util.Utils
|
import com.v2ray.ang.util.Utils
|
||||||
|
|
||||||
abstract class BaseActivity : AppCompatActivity() {
|
abstract class BaseActivity : AppCompatActivity() {
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
|
if (!Utils.getDarkModeStatus(this)) {
|
||||||
|
WindowCompat.getInsetsController(window, window.decorView).apply {
|
||||||
|
isAppearanceLightStatusBars = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
|
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
|
||||||
android.R.id.home -> {
|
android.R.id.home -> {
|
||||||
onBackPressed()
|
onBackPressed()
|
||||||
|
|||||||
@@ -25,13 +25,12 @@ class LogcatActivity : BaseActivity() {
|
|||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
binding = ActivityLogcatBinding.inflate(layoutInflater)
|
binding = ActivityLogcatBinding.inflate(layoutInflater)
|
||||||
val view = binding.root
|
val view = binding.root
|
||||||
setContentView(view)
|
setContentView(view)
|
||||||
|
|
||||||
title = getString(R.string.title_logcat)
|
title = getString(R.string.title_logcat)
|
||||||
|
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
|
||||||
logcat(false)
|
logcat(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import android.widget.Toast
|
|||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.core.view.WindowCompat
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.tencent.mmkv.MMKV
|
import com.tencent.mmkv.MMKV
|
||||||
import com.v2ray.ang.AppConfig.ANG_PACKAGE
|
import com.v2ray.ang.AppConfig.ANG_PACKAGE
|
||||||
@@ -66,11 +65,6 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
|||||||
setContentView(view)
|
setContentView(view)
|
||||||
title = getString(R.string.title_server)
|
title = getString(R.string.title_server)
|
||||||
setSupportActionBar(binding.toolbar)
|
setSupportActionBar(binding.toolbar)
|
||||||
if (!Utils.getDarkModeStatus(this)) {
|
|
||||||
WindowCompat.getInsetsController(window, window.decorView).apply {
|
|
||||||
isAppearanceLightStatusBars = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.fab.setOnClickListener {
|
binding.fab.setOnClickListener {
|
||||||
if (mainViewModel.isRunning.value == true) {
|
if (mainViewModel.isRunning.value == true) {
|
||||||
@@ -109,7 +103,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
|||||||
binding.drawerLayout.addDrawerListener(toggle)
|
binding.drawerLayout.addDrawerListener(toggle)
|
||||||
toggle.syncState()
|
toggle.syncState()
|
||||||
binding.navView.setNavigationItemSelectedListener(this)
|
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()
|
setupViewModel()
|
||||||
copyAssets()
|
copyAssets()
|
||||||
@@ -259,6 +253,10 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
|||||||
importManually(EConfigType.TROJAN.value)
|
importManually(EConfigType.TROJAN.value)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
R.id.import_manually_wireguard -> {
|
||||||
|
importManually(EConfigType.WIREGUARD.value)
|
||||||
|
true
|
||||||
|
}
|
||||||
R.id.import_config_custom_clipboard -> {
|
R.id.import_config_custom_clipboard -> {
|
||||||
importConfigCustomClipboard()
|
importConfigCustomClipboard()
|
||||||
true
|
true
|
||||||
@@ -421,6 +419,9 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
|||||||
if (count <= 0) {
|
if (count <= 0) {
|
||||||
count = AngConfigManager.importBatchConfig(Utils.decode(server!!), subid2, append)
|
count = AngConfigManager.importBatchConfig(Utils.decode(server!!), subid2, append)
|
||||||
}
|
}
|
||||||
|
if (count <= 0) {
|
||||||
|
count = AngConfigManager.appendCustomConfigServer(server, subid2)
|
||||||
|
}
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
toast(R.string.toast_success)
|
toast(R.string.toast_success)
|
||||||
mainViewModel.reloadServerList()
|
mainViewModel.reloadServerList()
|
||||||
@@ -688,6 +689,9 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
|||||||
R.id.logcat -> {
|
R.id.logcat -> {
|
||||||
startActivity(Intent(this, LogcatActivity::class.java))
|
startActivity(Intent(this, LogcatActivity::class.java))
|
||||||
}
|
}
|
||||||
|
R.id.privacy_policy-> {
|
||||||
|
Utils.openUri(this, AppConfig.v2rayNGPrivacyPolicy)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
binding.drawerLayout.closeDrawer(GravityCompat.START)
|
binding.drawerLayout.closeDrawer(GravityCompat.START)
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -96,7 +96,8 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
|
|||||||
holder.itemMainBinding.tvType.text = config.configType.name.lowercase()
|
holder.itemMainBinding.tvType.text = config.configType.name.lowercase()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
holder.itemMainBinding.tvStatistics.text = "${outbound?.getServerAddress()} : ${outbound?.getServerPort()}"
|
val strState = "${outbound?.getServerAddress()?.dropLast(3)}*** : ${outbound?.getServerPort()}"
|
||||||
|
holder.itemMainBinding.tvStatistics.text = strState
|
||||||
|
|
||||||
holder.itemMainBinding.layoutShare.setOnClickListener {
|
holder.itemMainBinding.layoutShare.setOnClickListener {
|
||||||
AlertDialog.Builder(mActivity).setItems(shareOptions.toTypedArray()) { _, i ->
|
AlertDialog.Builder(mActivity).setItems(shareOptions.toTypedArray()) { _, i ->
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import com.v2ray.ang.extension.v2RayApplication
|
|||||||
import com.v2ray.ang.util.AppManagerUtil
|
import com.v2ray.ang.util.AppManagerUtil
|
||||||
import com.v2ray.ang.util.Utils
|
import com.v2ray.ang.util.Utils
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import rx.android.schedulers.AndroidSchedulers
|
import rx.android.schedulers.AndroidSchedulers
|
||||||
import rx.schedulers.Schedulers
|
import rx.schedulers.Schedulers
|
||||||
@@ -41,8 +40,6 @@ class PerAppProxyActivity : BaseActivity() {
|
|||||||
val view = binding.root
|
val view = binding.root
|
||||||
setContentView(view)
|
setContentView(view)
|
||||||
|
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
|
||||||
|
|
||||||
val dividerItemDecoration = DividerItemDecoration(this, LinearLayoutManager.VERTICAL)
|
val dividerItemDecoration = DividerItemDecoration(this, LinearLayoutManager.VERTICAL)
|
||||||
binding.recyclerView.addItemDecoration(dividerItemDecoration)
|
binding.recyclerView.addItemDecoration(dividerItemDecoration)
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ class RoutingSettingsActivity : BaseActivity() {
|
|||||||
setContentView(view)
|
setContentView(view)
|
||||||
|
|
||||||
title = getString(R.string.title_pref_routing_custom)
|
title = getString(R.string.title_pref_routing_custom)
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
|
||||||
|
|
||||||
val fragments = ArrayList<Fragment>()
|
val fragments = ArrayList<Fragment>()
|
||||||
fragments.add(RoutingSettingsFragment().newInstance(AppConfig.PREF_V2RAY_ROUTING_AGENT))
|
fragments.add(RoutingSettingsFragment().newInstance(AppConfig.PREF_V2RAY_ROUTING_AGENT))
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.v2ray.ang.ui
|
package com.v2ray.ang.ui
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.app.Activity.RESULT_OK
|
import androidx.appcompat.app.AppCompatActivity.RESULT_OK
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
@@ -18,7 +18,6 @@ import com.v2ray.ang.extension.toast
|
|||||||
import com.v2ray.ang.extension.v2RayApplication
|
import com.v2ray.ang.extension.v2RayApplication
|
||||||
import com.v2ray.ang.util.Utils
|
import com.v2ray.ang.util.Utils
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class RoutingSettingsFragment : Fragment() {
|
class RoutingSettingsFragment : Fragment() {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.v2ray.ang.ui
|
package com.v2ray.ang.ui
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.app.Activity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
@@ -31,16 +31,6 @@ class ScannerActivity : BaseActivity(){
|
|||||||
if (settingsStorage?.decodeBool(AppConfig.PREF_START_SCAN_IMMEDIATE) == true) {
|
if (settingsStorage?.decodeBool(AppConfig.PREF_START_SCAN_IMMEDIATE) == true) {
|
||||||
launchScan()
|
launchScan()
|
||||||
}
|
}
|
||||||
|
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
public override fun onResume() {
|
|
||||||
super.onResume()
|
|
||||||
}
|
|
||||||
|
|
||||||
public override fun onPause() {
|
|
||||||
super.onPause()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun launchScan(){
|
private fun launchScan(){
|
||||||
@@ -55,7 +45,7 @@ class ScannerActivity : BaseActivity(){
|
|||||||
|
|
||||||
private fun handleResult(result: QRResult) {
|
private fun handleResult(result: QRResult) {
|
||||||
if (result is QRResult.QRSuccess ) {
|
if (result is QRResult.QRSuccess ) {
|
||||||
finished(result.content.rawValue)
|
finished(result.content.rawValue!!)
|
||||||
} else {
|
} else {
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
@@ -64,7 +54,7 @@ class ScannerActivity : BaseActivity(){
|
|||||||
private fun finished(text: String) {
|
private fun finished(text: String) {
|
||||||
val intent = Intent()
|
val intent = Intent()
|
||||||
intent.putExtra("SCAN_RESULT", text)
|
intent.putExtra("SCAN_RESULT", text)
|
||||||
setResult(Activity.RESULT_OK, intent)
|
setResult(AppCompatActivity.RESULT_OK, intent)
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,22 +10,32 @@ import androidx.appcompat.app.AlertDialog
|
|||||||
import com.tencent.mmkv.MMKV
|
import com.tencent.mmkv.MMKV
|
||||||
import com.v2ray.ang.AppConfig
|
import com.v2ray.ang.AppConfig
|
||||||
import com.v2ray.ang.AppConfig.PREF_ALLOW_INSECURE
|
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.R
|
||||||
import com.v2ray.ang.dto.EConfigType
|
import com.v2ray.ang.dto.EConfigType
|
||||||
import com.v2ray.ang.dto.ServerConfig
|
import com.v2ray.ang.dto.ServerConfig
|
||||||
import com.v2ray.ang.dto.V2rayConfig
|
import com.v2ray.ang.dto.V2rayConfig
|
||||||
import com.v2ray.ang.dto.V2rayConfig.Companion.DEFAULT_PORT
|
import com.v2ray.ang.dto.V2rayConfig.Companion.DEFAULT_PORT
|
||||||
import com.v2ray.ang.dto.V2rayConfig.Companion.TLS
|
import com.v2ray.ang.dto.V2rayConfig.Companion.TLS
|
||||||
|
import com.v2ray.ang.extension.removeWhiteSpace
|
||||||
import com.v2ray.ang.extension.toast
|
import com.v2ray.ang.extension.toast
|
||||||
import com.v2ray.ang.util.MmkvManager
|
import com.v2ray.ang.util.MmkvManager
|
||||||
import com.v2ray.ang.util.MmkvManager.ID_MAIN
|
import com.v2ray.ang.util.MmkvManager.ID_MAIN
|
||||||
import com.v2ray.ang.util.MmkvManager.KEY_SELECTED_SERVER
|
import com.v2ray.ang.util.MmkvManager.KEY_SELECTED_SERVER
|
||||||
import com.v2ray.ang.util.Utils
|
import com.v2ray.ang.util.Utils
|
||||||
|
import com.v2ray.ang.util.Utils.getIpv6Address
|
||||||
|
|
||||||
class ServerActivity : BaseActivity() {
|
class ServerActivity : BaseActivity() {
|
||||||
|
|
||||||
private val mainStorage by lazy { MMKV.mmkvWithID(ID_MAIN, MMKV.MULTI_PROCESS_MODE) }
|
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 editGuid by lazy { intent.getStringExtra("guid").orEmpty() }
|
||||||
private val isRunning by lazy {
|
private val isRunning by lazy {
|
||||||
intent.getBooleanExtra("isRunning", false)
|
intent.getBooleanExtra("isRunning", false)
|
||||||
@@ -33,7 +43,8 @@ class ServerActivity : BaseActivity() {
|
|||||||
&& editGuid == mainStorage?.decodeString(KEY_SELECTED_SERVER)
|
&& editGuid == mainStorage?.decodeString(KEY_SELECTED_SERVER)
|
||||||
}
|
}
|
||||||
private val createConfigType by lazy {
|
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 {
|
private val subscriptionId by lazy {
|
||||||
intent.getStringExtra("subscriptionId")
|
intent.getStringExtra("subscriptionId")
|
||||||
@@ -66,12 +77,13 @@ class ServerActivity : BaseActivity() {
|
|||||||
private val allowinsecures: Array<out String> by lazy {
|
private val allowinsecures: Array<out String> by lazy {
|
||||||
resources.getStringArray(R.array.allowinsecures)
|
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)
|
resources.getStringArray(R.array.streamsecurity_utls)
|
||||||
}
|
}
|
||||||
private val alpns: Array<out String> by lazy {
|
private val alpns: Array<out String> by lazy {
|
||||||
resources.getStringArray(R.array.streamsecurity_alpn)
|
resources.getStringArray(R.array.streamsecurity_alpn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Kotlin synthetics was used, but since it is removed in 1.8. We switch to old manual approach.
|
// 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
|
// 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.
|
// 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 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 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 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?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
title = getString(R.string.title_server)
|
title = getString(R.string.title_server)
|
||||||
|
|
||||||
val config = MmkvManager.decodeServerConfig(editGuid)
|
val config = MmkvManager.decodeServerConfig(editGuid)
|
||||||
when(config?.configType ?: createConfigType) {
|
when (config?.configType ?: createConfigType) {
|
||||||
EConfigType.VMESS -> setContentView(R.layout.activity_server_vmess)
|
EConfigType.VMESS -> setContentView(R.layout.activity_server_vmess)
|
||||||
EConfigType.CUSTOM -> return
|
EConfigType.CUSTOM -> return
|
||||||
EConfigType.SHADOWSOCKS -> setContentView(R.layout.activity_server_shadowsocks)
|
EConfigType.SHADOWSOCKS -> setContentView(R.layout.activity_server_shadowsocks)
|
||||||
EConfigType.SOCKS -> setContentView(R.layout.activity_server_socks)
|
EConfigType.SOCKS -> setContentView(R.layout.activity_server_socks)
|
||||||
EConfigType.VLESS -> setContentView(R.layout.activity_server_vless)
|
EConfigType.VLESS -> setContentView(R.layout.activity_server_vless)
|
||||||
EConfigType.TROJAN -> setContentView(R.layout.activity_server_trojan)
|
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 {
|
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])
|
val types = transportTypes(networks[position])
|
||||||
sp_header_type?.isEnabled = types.size > 1
|
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)
|
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
|
||||||
sp_header_type?.adapter = adapter
|
sp_header_type?.adapter = adapter
|
||||||
sp_header_type_title?.text = if (networks[position] == "grpc")
|
sp_header_type_title?.text = if (networks[position] == "grpc")
|
||||||
@@ -134,12 +158,18 @@ class ServerActivity : BaseActivity() {
|
|||||||
et_path?.text = Utils.getEditable(transportDetails[2])
|
et_path?.text = Utils.getEditable(transportDetails[2])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onNothingSelected(parent: AdapterView<*>?) {
|
override fun onNothingSelected(parent: AdapterView<*>?) {
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sp_stream_security?.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
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()) {
|
if (streamSecuritys[position].isBlank()) {
|
||||||
container_sni?.visibility = View.GONE
|
container_sni?.visibility = View.GONE
|
||||||
container_fingerprint?.visibility = View.GONE
|
container_fingerprint?.visibility = View.GONE
|
||||||
@@ -166,6 +196,7 @@ class ServerActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onNothingSelected(p0: AdapterView<*>?) {
|
override fun onNothingSelected(p0: AdapterView<*>?) {
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
@@ -175,7 +206,6 @@ class ServerActivity : BaseActivity() {
|
|||||||
} else {
|
} else {
|
||||||
clearServer()
|
clearServer()
|
||||||
}
|
}
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -183,37 +213,67 @@ class ServerActivity : BaseActivity() {
|
|||||||
*/
|
*/
|
||||||
private fun bindingServer(config: ServerConfig): Boolean {
|
private fun bindingServer(config: ServerConfig): Boolean {
|
||||||
val outbound = config.getProxyOutbound() ?: return false
|
val outbound = config.getProxyOutbound() ?: return false
|
||||||
val streamSetting = config.outboundBean?.streamSettings ?: return false
|
|
||||||
|
|
||||||
et_remarks.text = Utils.getEditable(config.remarks)
|
et_remarks.text = Utils.getEditable(config.remarks)
|
||||||
et_address.text = Utils.getEditable(outbound.getServerAddress().orEmpty())
|
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_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) {
|
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) {
|
} else if (config.configType == EConfigType.VLESS) {
|
||||||
et_security?.text = Utils.getEditable(outbound.getSecurityEncryption().orEmpty())
|
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) {
|
if (flow >= 0) {
|
||||||
sp_flow?.setSelection(flow)
|
sp_flow?.setSelection(flow)
|
||||||
}
|
}
|
||||||
} else if (config.configType == EConfigType.TROJAN) {
|
} else if (config.configType == EConfigType.WIREGUARD) {
|
||||||
val flow = Utils.arrayFind(flows, outbound.settings?.servers?.get(0)?.flow.orEmpty())
|
et_public_key?.text =
|
||||||
if (flow >= 0) {
|
Utils.getEditable(outbound.settings?.peers?.get(0)?.publicKey.orEmpty())
|
||||||
sp_flow?.setSelection(flow)
|
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 securityEncryptions =
|
||||||
val security = Utils.arrayFind(securityEncryptions, outbound.getSecurityEncryption().orEmpty())
|
if (config.configType == EConfigType.SHADOWSOCKS) shadowsocksSecuritys else securitys
|
||||||
|
val security =
|
||||||
|
Utils.arrayFind(securityEncryptions, outbound.getSecurityEncryption().orEmpty())
|
||||||
if (security >= 0) {
|
if (security >= 0) {
|
||||||
sp_security?.setSelection(security)
|
sp_security?.setSelection(security)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val streamSetting = config.outboundBean?.streamSettings ?: return true
|
||||||
val streamSecurity = Utils.arrayFind(streamSecuritys, streamSetting.security)
|
val streamSecurity = Utils.arrayFind(streamSecuritys, streamSetting.security)
|
||||||
if (streamSecurity >= 0) {
|
if (streamSecurity >= 0) {
|
||||||
sp_stream_security?.setSelection(streamSecurity)
|
sp_stream_security?.setSelection(streamSecurity)
|
||||||
(streamSetting.tlsSettings?: streamSetting.realitySettings)?.let { tlsSetting ->
|
(streamSetting.tlsSettings ?: streamSetting.realitySettings)?.let { tlsSetting ->
|
||||||
container_sni?.visibility = View.VISIBLE
|
container_sni?.visibility = View.VISIBLE
|
||||||
container_fingerprint?.visibility = View.VISIBLE
|
container_fingerprint?.visibility = View.VISIBLE
|
||||||
container_alpn?.visibility = View.VISIBLE
|
container_alpn?.visibility = View.VISIBLE
|
||||||
@@ -223,12 +283,16 @@ class ServerActivity : BaseActivity() {
|
|||||||
sp_stream_fingerprint?.setSelection(utlsIndex)
|
sp_stream_fingerprint?.setSelection(utlsIndex)
|
||||||
}
|
}
|
||||||
tlsSetting.alpn?.let {
|
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)
|
sp_stream_alpn?.setSelection(alpnIndex)
|
||||||
}
|
}
|
||||||
if (streamSetting.tlsSettings != null) {
|
if (streamSetting.tlsSettings != null) {
|
||||||
container_allow_insecure?.visibility = View.VISIBLE
|
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) {
|
if (allowinsecure >= 0) {
|
||||||
sp_allow_insecure?.setSelection(allowinsecure)
|
sp_allow_insecure?.setSelection(allowinsecure)
|
||||||
}
|
}
|
||||||
@@ -283,6 +347,13 @@ class ServerActivity : BaseActivity() {
|
|||||||
|
|
||||||
//et_security.text = null
|
//et_security.text = null
|
||||||
sp_flow?.setSelection(0)
|
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
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,9 +374,14 @@ class ServerActivity : BaseActivity() {
|
|||||||
toast(R.string.server_lab_port)
|
toast(R.string.server_lab_port)
|
||||||
return false
|
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.SOCKS && TextUtils.isEmpty(et_id.text.toString())) {
|
||||||
toast(R.string.server_lab_id)
|
if (config.configType == EConfigType.TROJAN || config.configType == EConfigType.SHADOWSOCKS) {
|
||||||
|
toast(R.string.server_lab_id3)
|
||||||
|
} else {
|
||||||
|
toast(R.string.server_lab_id)
|
||||||
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
sp_stream_security?.let {
|
sp_stream_security?.let {
|
||||||
@@ -329,10 +405,14 @@ class ServerActivity : BaseActivity() {
|
|||||||
config.outboundBean?.settings?.servers?.get(0)?.let { server ->
|
config.outboundBean?.settings?.servers?.get(0)?.let { server ->
|
||||||
saveServers(server, port, config)
|
saveServers(server, port, config)
|
||||||
}
|
}
|
||||||
|
val wireguard = config.outboundBean?.settings
|
||||||
|
wireguard?.peers?.get(0)?.let { _ ->
|
||||||
|
savePeer(wireguard, port)
|
||||||
|
}
|
||||||
config.outboundBean?.streamSettings?.let {
|
config.outboundBean?.streamSettings?.let {
|
||||||
saveStreamSettings(it)
|
saveStreamSettings(it)
|
||||||
}
|
}
|
||||||
if(config.subscriptionId.isEmpty() && !subscriptionId.isNullOrEmpty()) {
|
if (config.subscriptionId.isEmpty() && !subscriptionId.isNullOrEmpty()) {
|
||||||
config.subscriptionId = subscriptionId!!
|
config.subscriptionId = subscriptionId!!
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,7 +422,11 @@ class ServerActivity : BaseActivity() {
|
|||||||
return true
|
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.address = et_address.text.toString().trim()
|
||||||
vnext.port = port
|
vnext.port = port
|
||||||
vnext.users[0].id = et_id.text.toString().trim()
|
vnext.users[0].id = et_id.text.toString().trim()
|
||||||
@@ -355,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.address = et_address.text.toString().trim()
|
||||||
server.port = port
|
server.port = port
|
||||||
if (config.configType == EConfigType.SHADOWSOCKS) {
|
if (config.configType == EConfigType.SHADOWSOCKS) {
|
||||||
@@ -365,7 +453,8 @@ class ServerActivity : BaseActivity() {
|
|||||||
if (TextUtils.isEmpty(et_security?.text) && TextUtils.isEmpty(et_id.text)) {
|
if (TextUtils.isEmpty(et_security?.text) && TextUtils.isEmpty(et_id.text)) {
|
||||||
server.users = null
|
server.users = null
|
||||||
} else {
|
} else {
|
||||||
val socksUsersBean = V2rayConfig.OutboundBean.OutSettingsBean.ServersBean.SocksUsersBean()
|
val socksUsersBean =
|
||||||
|
V2rayConfig.OutboundBean.OutSettingsBean.ServersBean.SocksUsersBean()
|
||||||
socksUsersBean.user = et_security?.text.toString().trim()
|
socksUsersBean.user = et_security?.text.toString().trim()
|
||||||
socksUsersBean.pass = et_id.text.toString().trim()
|
socksUsersBean.pass = et_id.text.toString().trim()
|
||||||
server.users = listOf(socksUsersBean)
|
server.users = listOf(socksUsersBean)
|
||||||
@@ -375,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) {
|
private fun saveStreamSettings(streamSetting: V2rayConfig.OutboundBean.StreamSettingsBean) {
|
||||||
val network = sp_network?.selectedItemPosition ?: return
|
val network = sp_network?.selectedItemPosition ?: return
|
||||||
val type = sp_header_type?.selectedItemPosition ?: return
|
val type = sp_header_type?.selectedItemPosition ?: return
|
||||||
@@ -390,15 +496,15 @@ class ServerActivity : BaseActivity() {
|
|||||||
val spiderX = et_spider_x?.text?.toString()?.trim() ?: return
|
val spiderX = et_spider_x?.text?.toString()?.trim() ?: return
|
||||||
|
|
||||||
var sni = streamSetting.populateTransportSettings(
|
var sni = streamSetting.populateTransportSettings(
|
||||||
transport = networks[network],
|
transport = networks[network],
|
||||||
headerType = transportTypes(networks[network])[type],
|
headerType = transportTypes(networks[network])[type],
|
||||||
host = requestHost,
|
host = requestHost,
|
||||||
path = path,
|
path = path,
|
||||||
seed = path,
|
seed = path,
|
||||||
quicSecurity = requestHost,
|
quicSecurity = requestHost,
|
||||||
key = path,
|
key = path,
|
||||||
mode = transportTypes(networks[network])[type],
|
mode = transportTypes(networks[network])[type],
|
||||||
serviceName = path
|
serviceName = path
|
||||||
)
|
)
|
||||||
if (sniField.isNotBlank()) {
|
if (sniField.isNotBlank()) {
|
||||||
sni = sniField
|
sni = sniField
|
||||||
@@ -410,14 +516,14 @@ class ServerActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
streamSetting.populateTlsSettings(
|
streamSetting.populateTlsSettings(
|
||||||
streamSecurity = streamSecuritys[streamSecurity],
|
streamSecurity = streamSecuritys[streamSecurity],
|
||||||
allowInsecure = allowInsecure,
|
allowInsecure = allowInsecure,
|
||||||
sni = sni,
|
sni = sni,
|
||||||
fingerprint = uTlsItems[utlsIndex],
|
fingerprint = uTlsItems[utlsIndex],
|
||||||
alpns = alpns[alpnIndex],
|
alpns = alpns[alpnIndex],
|
||||||
publicKey = publicKey,
|
publicKey = publicKey,
|
||||||
shortId = shortId,
|
shortId = shortId,
|
||||||
spiderX = spiderX
|
spiderX = spiderX
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -426,12 +532,15 @@ class ServerActivity : BaseActivity() {
|
|||||||
"tcp" -> {
|
"tcp" -> {
|
||||||
tcpTypes
|
tcpTypes
|
||||||
}
|
}
|
||||||
|
|
||||||
"kcp", "quic" -> {
|
"kcp", "quic" -> {
|
||||||
kcpAndQuicTypes
|
kcpAndQuicTypes
|
||||||
}
|
}
|
||||||
|
|
||||||
"grpc" -> {
|
"grpc" -> {
|
||||||
grpcModes
|
grpcModes
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
arrayOf("---")
|
arrayOf("---")
|
||||||
}
|
}
|
||||||
@@ -482,10 +591,12 @@ class ServerActivity : BaseActivity() {
|
|||||||
deleteServer()
|
deleteServer()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.save_config -> {
|
R.id.save_config -> {
|
||||||
saveServer()
|
saveServer()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> super.onOptionsItemSelected(item)
|
else -> super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,6 @@ class ServerCustomConfigActivity : BaseActivity() {
|
|||||||
} else {
|
} else {
|
||||||
clearServer()
|
clearServer()
|
||||||
}
|
}
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -6,10 +6,16 @@ import android.text.TextUtils
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
import androidx.preference.*
|
import androidx.preference.*
|
||||||
|
import androidx.work.ExistingPeriodicWorkPolicy
|
||||||
|
import androidx.work.PeriodicWorkRequest
|
||||||
|
import androidx.work.multiprocess.RemoteWorkManager
|
||||||
|
import com.v2ray.ang.AngApplication
|
||||||
import com.v2ray.ang.AppConfig
|
import com.v2ray.ang.AppConfig
|
||||||
import com.v2ray.ang.R
|
import com.v2ray.ang.R
|
||||||
|
import com.v2ray.ang.service.SubscriptionUpdater
|
||||||
import com.v2ray.ang.util.Utils
|
import com.v2ray.ang.util.Utils
|
||||||
import com.v2ray.ang.viewmodel.SettingsViewModel
|
import com.v2ray.ang.viewmodel.SettingsViewModel
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
class SettingsActivity : BaseActivity() {
|
class SettingsActivity : BaseActivity() {
|
||||||
private val settingsViewModel: SettingsViewModel by viewModels()
|
private val settingsViewModel: SettingsViewModel by viewModels()
|
||||||
@@ -20,8 +26,6 @@ class SettingsActivity : BaseActivity() {
|
|||||||
|
|
||||||
title = getString(R.string.title_settings)
|
title = getString(R.string.title_settings)
|
||||||
|
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
|
||||||
|
|
||||||
settingsViewModel.startListenPreferenceChange()
|
settingsViewModel.startListenPreferenceChange()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,12 +35,25 @@ class SettingsActivity : BaseActivity() {
|
|||||||
private val fakeDns by lazy { findPreference<CheckBoxPreference>(AppConfig.PREF_FAKE_DNS_ENABLED) }
|
private val fakeDns by lazy { findPreference<CheckBoxPreference>(AppConfig.PREF_FAKE_DNS_ENABLED) }
|
||||||
private val localDnsPort by lazy { findPreference<EditTextPreference>(AppConfig.PREF_LOCAL_DNS_PORT) }
|
private val localDnsPort by lazy { findPreference<EditTextPreference>(AppConfig.PREF_LOCAL_DNS_PORT) }
|
||||||
private val vpnDns by lazy { findPreference<EditTextPreference>(AppConfig.PREF_VPN_DNS) }
|
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 }
|
// val autoRestart by lazy { findPreference(PREF_AUTO_RESTART) as CheckBoxPreference }
|
||||||
private val remoteDns by lazy { findPreference<EditTextPreference>(AppConfig.PREF_REMOTE_DNS) }
|
private val remoteDns by lazy { findPreference<EditTextPreference>(AppConfig.PREF_REMOTE_DNS) }
|
||||||
private val domesticDns by lazy { findPreference<EditTextPreference>(AppConfig.PREF_DOMESTIC_DNS) }
|
private val domesticDns by lazy { findPreference<EditTextPreference>(AppConfig.PREF_DOMESTIC_DNS) }
|
||||||
private val socksPort by lazy { findPreference<EditTextPreference>(AppConfig.PREF_SOCKS_PORT) }
|
private val socksPort by lazy { findPreference<EditTextPreference>(AppConfig.PREF_SOCKS_PORT) }
|
||||||
private val httpPort by lazy { findPreference<EditTextPreference>(AppConfig.PREF_HTTP_PORT) }
|
private val httpPort by lazy { findPreference<EditTextPreference>(AppConfig.PREF_HTTP_PORT) }
|
||||||
private val routingCustom by lazy { findPreference<Preference>(AppConfig.PREF_ROUTING_CUSTOM) }
|
private val routingCustom by lazy { findPreference<Preference>(AppConfig.PREF_ROUTING_CUSTOM) }
|
||||||
|
private val autoUpdateCheck by lazy { findPreference<CheckBoxPreference>(AppConfig.SUBSCRIPTION_AUTO_UPDATE) }
|
||||||
|
private val autoUpdateInterval by lazy { findPreference<EditTextPreference>(AppConfig.SUBSCRIPTION_AUTO_UPDATE_INTERVAL) }
|
||||||
// val licenses: Preference by lazy { findPreference(PREF_LICENSES) }
|
// val licenses: Preference by lazy { findPreference(PREF_LICENSES) }
|
||||||
// val feedback: Preference by lazy { findPreference(PREF_FEEDBACK) }
|
// val feedback: Preference by lazy { findPreference(PREF_FEEDBACK) }
|
||||||
// val tgGroup: Preference by lazy { findPreference(PREF_TG_GROUP) }
|
// val tgGroup: Preference by lazy { findPreference(PREF_TG_GROUP) }
|
||||||
@@ -51,6 +68,27 @@ class SettingsActivity : BaseActivity() {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
autoUpdateCheck?.setOnPreferenceChangeListener { _, newValue ->
|
||||||
|
val value = newValue as Boolean
|
||||||
|
autoUpdateCheck?.isChecked = value
|
||||||
|
autoUpdateInterval?.isEnabled = value
|
||||||
|
autoUpdateInterval?.text?.toLong()?.let {
|
||||||
|
if (newValue) configureUpdateTask(it) else cancelUpdateTask()
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
autoUpdateInterval?.setOnPreferenceChangeListener { _, any ->
|
||||||
|
var nval = any as String
|
||||||
|
|
||||||
|
autoUpdateInterval?.summary = nval
|
||||||
|
// It must be greater than 15 minutes because WorkManager couldn't run tasks under 15 minutes intervals
|
||||||
|
nval =
|
||||||
|
if (TextUtils.isEmpty(nval) or (nval.toLong() < 15)) AppConfig.SUBSCRIPTION_DEFAULT_UPDATE_INTERVAL else nval
|
||||||
|
configureUpdateTask(nval.toLong())
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
// licenses.onClick {
|
// licenses.onClick {
|
||||||
// val fragment = LicensesDialogFragment.Builder(act)
|
// val fragment = LicensesDialogFragment.Builder(act)
|
||||||
// .setNotices(R.raw.licenses)
|
// .setNotices(R.raw.licenses)
|
||||||
@@ -92,13 +130,14 @@ class SettingsActivity : BaseActivity() {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
localDns?.setOnPreferenceChangeListener{ _, any ->
|
localDns?.setOnPreferenceChangeListener { _, any ->
|
||||||
updateLocalDns(any as Boolean)
|
updateLocalDns(any as Boolean)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
localDnsPort?.setOnPreferenceChangeListener { _, any ->
|
localDnsPort?.setOnPreferenceChangeListener { _, any ->
|
||||||
val nval = any as String
|
val nval = any as String
|
||||||
localDnsPort?.summary = if (TextUtils.isEmpty(nval)) AppConfig.PORT_LOCAL_DNS else nval
|
localDnsPort?.summary =
|
||||||
|
if (TextUtils.isEmpty(nval)) AppConfig.PORT_LOCAL_DNS else nval
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
vpnDns?.setOnPreferenceChangeListener { _, any ->
|
vpnDns?.setOnPreferenceChangeListener { _, any ->
|
||||||
@@ -121,18 +160,57 @@ class SettingsActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
mode?.dialogLayoutResource = R.layout.preference_with_help_link
|
mode?.dialogLayoutResource = R.layout.preference_with_help_link
|
||||||
//loglevel.summary = "LogLevel"
|
//loglevel.summary = "LogLevel"
|
||||||
|
mux?.setOnPreferenceChangeListener { _, newValue ->
|
||||||
|
updateMux(newValue as Boolean)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
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() {
|
override fun onStart() {
|
||||||
super.onStart()
|
super.onStart()
|
||||||
val defaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity())
|
val defaultSharedPreferences =
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(requireActivity())
|
||||||
updateMode(defaultSharedPreferences.getString(AppConfig.PREF_MODE, "VPN"))
|
updateMode(defaultSharedPreferences.getString(AppConfig.PREF_MODE, "VPN"))
|
||||||
var remoteDnsString = defaultSharedPreferences.getString(AppConfig.PREF_REMOTE_DNS, "")
|
var remoteDnsString = defaultSharedPreferences.getString(AppConfig.PREF_REMOTE_DNS, "")
|
||||||
domesticDns?.summary = defaultSharedPreferences.getString(AppConfig.PREF_DOMESTIC_DNS, "")
|
|
||||||
|
|
||||||
|
domesticDns?.summary = defaultSharedPreferences.getString(AppConfig.PREF_DOMESTIC_DNS, "")
|
||||||
localDnsPort?.summary = defaultSharedPreferences.getString(AppConfig.PREF_LOCAL_DNS_PORT, AppConfig.PORT_LOCAL_DNS)
|
localDnsPort?.summary = defaultSharedPreferences.getString(AppConfig.PREF_LOCAL_DNS_PORT, AppConfig.PORT_LOCAL_DNS)
|
||||||
socksPort?.summary = defaultSharedPreferences.getString(AppConfig.PREF_SOCKS_PORT, AppConfig.PORT_SOCKS)
|
socksPort?.summary = defaultSharedPreferences.getString(AppConfig.PREF_SOCKS_PORT, AppConfig.PORT_SOCKS)
|
||||||
httpPort?.summary = defaultSharedPreferences.getString(AppConfig.PREF_HTTP_PORT, AppConfig.PORT_HTTP)
|
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)
|
||||||
|
|
||||||
if (TextUtils.isEmpty(remoteDnsString)) {
|
if (TextUtils.isEmpty(remoteDnsString)) {
|
||||||
remoteDnsString = AppConfig.DNS_AGENT
|
remoteDnsString = AppConfig.DNS_AGENT
|
||||||
@@ -141,7 +219,8 @@ class SettingsActivity : BaseActivity() {
|
|||||||
domesticDns?.summary = AppConfig.DNS_DIRECT
|
domesticDns?.summary = AppConfig.DNS_DIRECT
|
||||||
}
|
}
|
||||||
remoteDns?.summary = remoteDnsString
|
remoteDns?.summary = remoteDnsString
|
||||||
vpnDns?.summary = defaultSharedPreferences.getString(AppConfig.PREF_VPN_DNS, remoteDnsString)
|
vpnDns?.summary =
|
||||||
|
defaultSharedPreferences.getString(AppConfig.PREF_VPN_DNS, remoteDnsString)
|
||||||
|
|
||||||
if (TextUtils.isEmpty(localDnsPort?.summary)) {
|
if (TextUtils.isEmpty(localDnsPort?.summary)) {
|
||||||
localDnsPort?.summary = AppConfig.PORT_LOCAL_DNS
|
localDnsPort?.summary = AppConfig.PORT_LOCAL_DNS
|
||||||
@@ -155,17 +234,24 @@ class SettingsActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun updateMode(mode: String?) {
|
private fun updateMode(mode: String?) {
|
||||||
val defaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity())
|
val defaultSharedPreferences =
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(requireActivity())
|
||||||
val vpn = mode == "VPN"
|
val vpn = mode == "VPN"
|
||||||
perAppProxy?.isEnabled = vpn
|
perAppProxy?.isEnabled = vpn
|
||||||
perAppProxy?.isChecked = PreferenceManager.getDefaultSharedPreferences(requireActivity())
|
perAppProxy?.isChecked =
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(requireActivity())
|
||||||
.getBoolean(AppConfig.PREF_PER_APP_PROXY, false)
|
.getBoolean(AppConfig.PREF_PER_APP_PROXY, false)
|
||||||
localDns?.isEnabled = vpn
|
localDns?.isEnabled = vpn
|
||||||
fakeDns?.isEnabled = vpn
|
fakeDns?.isEnabled = vpn
|
||||||
localDnsPort?.isEnabled = vpn
|
localDnsPort?.isEnabled = vpn
|
||||||
vpnDns?.isEnabled = vpn
|
vpnDns?.isEnabled = vpn
|
||||||
if (vpn) {
|
if (vpn) {
|
||||||
updateLocalDns(defaultSharedPreferences.getBoolean(AppConfig.PREF_LOCAL_DNS_ENABLED, false))
|
updateLocalDns(
|
||||||
|
defaultSharedPreferences.getBoolean(
|
||||||
|
AppConfig.PREF_LOCAL_DNS_ENABLED,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,6 +260,79 @@ class SettingsActivity : BaseActivity() {
|
|||||||
localDnsPort?.isEnabled = enabled
|
localDnsPort?.isEnabled = enabled
|
||||||
vpnDns?.isEnabled = !enabled
|
vpnDns?.isEnabled = !enabled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun configureUpdateTask(interval: Long) {
|
||||||
|
val rw = RemoteWorkManager.getInstance(AngApplication.application)
|
||||||
|
rw.cancelUniqueWork(AppConfig.SUBSCRIPTION_UPDATE_TASK_NAME)
|
||||||
|
rw.enqueueUniquePeriodicWork(
|
||||||
|
AppConfig.SUBSCRIPTION_UPDATE_TASK_NAME,
|
||||||
|
ExistingPeriodicWorkPolicy.UPDATE,
|
||||||
|
PeriodicWorkRequest.Builder(
|
||||||
|
SubscriptionUpdater.UpdateTask::class.java,
|
||||||
|
interval,
|
||||||
|
TimeUnit.MINUTES
|
||||||
|
)
|
||||||
|
.apply {
|
||||||
|
setInitialDelay(interval, TimeUnit.MINUTES)
|
||||||
|
}
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun cancelUpdateTask() {
|
||||||
|
val rw = RemoteWorkManager.getInstance(AngApplication.application)
|
||||||
|
rw.cancelUniqueWork(AppConfig.SUBSCRIPTION_UPDATE_TASK_NAME)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateMux(enabled: Boolean) {
|
||||||
|
val defaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity())
|
||||||
|
muxConcurrency?.isVisible = enabled
|
||||||
|
muxXudpConcurrency?.isVisible = enabled
|
||||||
|
muxXudpQuic?.isVisible = enabled
|
||||||
|
if (enabled) {
|
||||||
|
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 {
|
||||||
|
val concurrency = value.toIntOrNull() ?: 8
|
||||||
|
muxXudpConcurrency?.summary = concurrency.toString()
|
||||||
|
muxXudpQuic?.isEnabled = concurrency >= 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateFragment(enabled: Boolean) {
|
||||||
|
val defaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity())
|
||||||
|
fragmentPackets?.isVisible = enabled
|
||||||
|
fragmentLength?.isVisible = enabled
|
||||||
|
fragmentInterval?.isVisible = 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) {
|
fun onModeHelpClicked(view: View) {
|
||||||
|
|||||||
@@ -5,14 +5,22 @@ import android.text.TextUtils
|
|||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.work.Constraints
|
||||||
|
import androidx.work.ExistingPeriodicWorkPolicy
|
||||||
|
import androidx.work.NetworkType
|
||||||
|
import androidx.work.PeriodicWorkRequest
|
||||||
|
import androidx.work.multiprocess.RemoteWorkManager
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.tencent.mmkv.MMKV
|
import com.tencent.mmkv.MMKV
|
||||||
|
import com.v2ray.ang.AngApplication
|
||||||
import com.v2ray.ang.R
|
import com.v2ray.ang.R
|
||||||
import com.v2ray.ang.databinding.ActivitySubEditBinding
|
import com.v2ray.ang.databinding.ActivitySubEditBinding
|
||||||
import com.v2ray.ang.dto.SubscriptionItem
|
import com.v2ray.ang.dto.SubscriptionItem
|
||||||
import com.v2ray.ang.extension.toast
|
import com.v2ray.ang.extension.toast
|
||||||
|
import com.v2ray.ang.service.SubscriptionUpdater
|
||||||
import com.v2ray.ang.util.MmkvManager
|
import com.v2ray.ang.util.MmkvManager
|
||||||
import com.v2ray.ang.util.Utils
|
import com.v2ray.ang.util.Utils
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
class SubEditActivity : BaseActivity() {
|
class SubEditActivity : BaseActivity() {
|
||||||
private lateinit var binding: ActivitySubEditBinding
|
private lateinit var binding: ActivitySubEditBinding
|
||||||
@@ -36,7 +44,6 @@ class SubEditActivity : BaseActivity() {
|
|||||||
} else {
|
} else {
|
||||||
clearServer()
|
clearServer()
|
||||||
}
|
}
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -46,6 +53,7 @@ class SubEditActivity : BaseActivity() {
|
|||||||
binding.etRemarks.text = Utils.getEditable(subItem.remarks)
|
binding.etRemarks.text = Utils.getEditable(subItem.remarks)
|
||||||
binding.etUrl.text = Utils.getEditable(subItem.url)
|
binding.etUrl.text = Utils.getEditable(subItem.url)
|
||||||
binding.chkEnable.isChecked = subItem.enabled
|
binding.chkEnable.isChecked = subItem.enabled
|
||||||
|
binding.autoUpdateCheck.isChecked = subItem.autoUpdate
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,6 +84,7 @@ class SubEditActivity : BaseActivity() {
|
|||||||
subItem.remarks = binding.etRemarks.text.toString()
|
subItem.remarks = binding.etRemarks.text.toString()
|
||||||
subItem.url = binding.etUrl.text.toString()
|
subItem.url = binding.etUrl.text.toString()
|
||||||
subItem.enabled = binding.chkEnable.isChecked
|
subItem.enabled = binding.chkEnable.isChecked
|
||||||
|
subItem.autoUpdate = binding.autoUpdateCheck.isChecked
|
||||||
|
|
||||||
if (TextUtils.isEmpty(subItem.remarks)) {
|
if (TextUtils.isEmpty(subItem.remarks)) {
|
||||||
toast(R.string.sub_setting_remarks)
|
toast(R.string.sub_setting_remarks)
|
||||||
@@ -130,4 +139,5 @@ class SubEditActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
else -> super.onOptionsItemSelected(item)
|
else -> super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,8 +27,6 @@ class SubSettingActivity : BaseActivity() {
|
|||||||
binding.recyclerView.setHasFixedSize(true)
|
binding.recyclerView.setHasFixedSize(true)
|
||||||
binding.recyclerView.layoutManager = LinearLayoutManager(this)
|
binding.recyclerView.layoutManager = LinearLayoutManager(this)
|
||||||
binding.recyclerView.adapter = adapter
|
binding.recyclerView.adapter = adapter
|
||||||
|
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.v2ray.ang.ui
|
package com.v2ray.ang.ui
|
||||||
|
|
||||||
import android.app.Activity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.ArrayAdapter
|
import android.widget.ArrayAdapter
|
||||||
@@ -90,7 +90,7 @@ class TaskerActivity : BaseActivity() {
|
|||||||
|
|
||||||
intent.putExtra(AppConfig.TASKER_EXTRA_BUNDLE, extraBundle)
|
intent.putExtra(AppConfig.TASKER_EXTRA_BUNDLE, extraBundle)
|
||||||
intent.putExtra(AppConfig.TASKER_EXTRA_STRING_BLURB, blurb)
|
intent.putExtra(AppConfig.TASKER_EXTRA_STRING_BLURB, blurb)
|
||||||
setResult(Activity.RESULT_OK, intent)
|
setResult(AppCompatActivity.RESULT_OK, intent)
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ class UrlSchemeActivity : BaseActivity() {
|
|||||||
if ("text/plain" == type) {
|
if ("text/plain" == type) {
|
||||||
intent.getStringExtra(Intent.EXTRA_TEXT)?.let {
|
intent.getStringExtra(Intent.EXTRA_TEXT)?.let {
|
||||||
val uri = Uri.parse(it)
|
val uri = Uri.parse(it)
|
||||||
if (uri.scheme?.startsWith(AppConfig.HTTPS_PROTOCOL) == true || uri.scheme?.startsWith(
|
if (uri.scheme?.startsWith(AppConfig.PROTOCOL_HTTPS) == true || uri.scheme?.startsWith(
|
||||||
AppConfig.HTTP_PROTOCOL
|
AppConfig.PROTOCOL_HTTP
|
||||||
) == true
|
) == true
|
||||||
) {
|
) {
|
||||||
val name = uri.getQueryParameter("name") ?: "Subscription"
|
val name = uri.getQueryParameter("name") ?: "Subscription"
|
||||||
|
|||||||
@@ -15,12 +15,14 @@ import androidx.activity.result.contract.ActivityResultContracts
|
|||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.google.gson.Gson
|
||||||
import com.tbruyelle.rxpermissions.RxPermissions
|
import com.tbruyelle.rxpermissions.RxPermissions
|
||||||
import com.tencent.mmkv.MMKV
|
import com.tencent.mmkv.MMKV
|
||||||
import com.v2ray.ang.AppConfig
|
import com.v2ray.ang.AppConfig
|
||||||
import com.v2ray.ang.R
|
import com.v2ray.ang.R
|
||||||
import com.v2ray.ang.databinding.ActivitySubSettingBinding
|
import com.v2ray.ang.databinding.ActivitySubSettingBinding
|
||||||
import com.v2ray.ang.databinding.ItemRecyclerUserAssetBinding
|
import com.v2ray.ang.databinding.ItemRecyclerUserAssetBinding
|
||||||
|
import com.v2ray.ang.dto.AssetUrlItem
|
||||||
import com.v2ray.ang.extension.toTrafficString
|
import com.v2ray.ang.extension.toTrafficString
|
||||||
import com.v2ray.ang.extension.toast
|
import com.v2ray.ang.extension.toast
|
||||||
import com.v2ray.ang.util.MmkvManager
|
import com.v2ray.ang.util.MmkvManager
|
||||||
@@ -39,9 +41,11 @@ import java.util.*
|
|||||||
class UserAssetActivity : BaseActivity() {
|
class UserAssetActivity : BaseActivity() {
|
||||||
private lateinit var binding: ActivitySubSettingBinding
|
private lateinit var binding: ActivitySubSettingBinding
|
||||||
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 assetStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_ASSET, MMKV.MULTI_PROCESS_MODE) }
|
||||||
|
|
||||||
val extDir by lazy { File(Utils.userAssetPath(this)) }
|
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?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@@ -49,13 +53,17 @@ class UserAssetActivity : BaseActivity() {
|
|||||||
val view = binding.root
|
val view = binding.root
|
||||||
setContentView(view)
|
setContentView(view)
|
||||||
title = getString(R.string.title_user_asset_setting)
|
title = getString(R.string.title_user_asset_setting)
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
|
||||||
|
|
||||||
binding.recyclerView.setHasFixedSize(true)
|
binding.recyclerView.setHasFixedSize(true)
|
||||||
binding.recyclerView.layoutManager = LinearLayoutManager(this)
|
binding.recyclerView.layoutManager = LinearLayoutManager(this)
|
||||||
binding.recyclerView.adapter = UserAssetAdapter()
|
binding.recyclerView.adapter = UserAssetAdapter()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
binding.recyclerView.adapter?.notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||||
menuInflater.inflate(R.menu.menu_asset, menu)
|
menuInflater.inflate(R.menu.menu_asset, menu)
|
||||||
return super.onCreateOptionsMenu(menu)
|
return super.onCreateOptionsMenu(menu)
|
||||||
@@ -67,6 +75,11 @@ class UserAssetActivity : BaseActivity() {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
R.id.add_url -> {
|
||||||
|
val intent = Intent(this, UserAssetUrlActivity::class.java)
|
||||||
|
startActivity(intent)
|
||||||
|
true
|
||||||
|
}
|
||||||
R.id.download_file -> {
|
R.id.download_file -> {
|
||||||
downloadGeoFiles()
|
downloadGeoFiles()
|
||||||
true
|
true
|
||||||
@@ -105,13 +118,27 @@ class UserAssetActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val chooseFile =
|
private val chooseFile =
|
||||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { it ->
|
||||||
val uri = it.data?.data
|
val uri = it.data?.data
|
||||||
if (it.resultCode == RESULT_OK && uri != null) {
|
if (it.resultCode == RESULT_OK && uri != null) {
|
||||||
|
val assetId = Utils.getUuid()
|
||||||
try {
|
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)
|
copyFile(uri)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
toast(R.string.toast_asset_copy_failed)
|
toast(R.string.toast_asset_copy_failed)
|
||||||
|
MmkvManager.removeAssetUrl(assetId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -144,31 +171,33 @@ class UserAssetActivity : BaseActivity() {
|
|||||||
val httpPort = Utils.parseInt(settingsStorage?.decodeString(AppConfig.PREF_HTTP_PORT), AppConfig.PORT_HTTP.toInt())
|
val httpPort = Utils.parseInt(settingsStorage?.decodeString(AppConfig.PREF_HTTP_PORT), AppConfig.PORT_HTTP.toInt())
|
||||||
|
|
||||||
toast(R.string.msg_downloading_content)
|
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)
|
//toast(getString(R.string.msg_downloading_content) + it)
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
val result = downloadGeo(it, 60000, httpPort)
|
val result = downloadGeo(it.second, 60000, httpPort)
|
||||||
launch(Dispatchers.Main) {
|
launch(Dispatchers.Main) {
|
||||||
if (result) {
|
if (result) {
|
||||||
toast(getString(R.string.toast_success) + " " + it)
|
toast(getString(R.string.toast_success) + " " + it.second.remarks)
|
||||||
binding.recyclerView.adapter?.notifyDataSetChanged()
|
binding.recyclerView.adapter?.notifyDataSetChanged()
|
||||||
} else {
|
} 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 {
|
private fun downloadGeo(item: AssetUrlItem, timeout: Int, httpPort: Int): Boolean {
|
||||||
val url = AppConfig.geoUrl + name
|
val targetTemp = File(extDir, item.remarks + "_temp")
|
||||||
val targetTemp = File(extDir, name + "_temp")
|
val target = File(extDir, item.remarks)
|
||||||
val target = File(extDir, name)
|
|
||||||
var conn: HttpURLConnection? = null
|
var conn: HttpURLConnection? = null
|
||||||
//Log.d(AppConfig.ANG_PACKAGE, url)
|
//Log.d(AppConfig.ANG_PACKAGE, url)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
conn = URL(url).openConnection(
|
conn = URL(item.url).openConnection(
|
||||||
Proxy(
|
Proxy(
|
||||||
Proxy.Type.HTTP,
|
Proxy.Type.HTTP,
|
||||||
InetSocketAddress("127.0.0.1", httpPort)
|
InetSocketAddress("127.0.0.1", httpPort)
|
||||||
@@ -193,33 +222,74 @@ class UserAssetActivity : BaseActivity() {
|
|||||||
conn?.disconnect()
|
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>() {
|
inner class UserAssetAdapter : RecyclerView.Adapter<UserAssetViewHolder>() {
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): 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")
|
@SuppressLint("SetTextI18n")
|
||||||
override fun onBindViewHolder(holder: UserAssetViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: UserAssetViewHolder, position: Int) {
|
||||||
val file = extDir.listFiles()?.getOrNull(position) ?: return
|
var assets = MmkvManager.decodeAssetUrls();
|
||||||
holder.itemUserAssetBinding.assetName.text = file.name
|
assets = addBuiltInGeoItems(assets);
|
||||||
val dateFormat = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM)
|
val item = assets.getOrNull(position) ?: return
|
||||||
holder.itemUserAssetBinding.assetProperties.text = "${file.length().toTrafficString()} • ${dateFormat.format(Date(file.lastModified()))}"
|
// file with name == item.second.remarks
|
||||||
if (file.name in geofiles) {
|
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
|
holder.itemUserAssetBinding.layoutRemove.visibility = GONE
|
||||||
} else {
|
} else {
|
||||||
|
holder.itemUserAssetBinding.layoutEdit.visibility = item.second.url.let { if (it == "file") GONE else VISIBLE }
|
||||||
holder.itemUserAssetBinding.layoutRemove.visibility = 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 {
|
holder.itemUserAssetBinding.layoutRemove.setOnClickListener {
|
||||||
file.delete()
|
file?.delete()
|
||||||
|
MmkvManager.removeAssetUrl(item.first)
|
||||||
binding.recyclerView.adapter?.notifyItemRemoved(position)
|
binding.recyclerView.adapter?.notifyItemRemoved(position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemCount(): Int {
|
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,14 @@ import android.text.TextUtils
|
|||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
|
import com.google.gson.GsonBuilder
|
||||||
import com.tencent.mmkv.MMKV
|
import com.tencent.mmkv.MMKV
|
||||||
import com.v2ray.ang.AppConfig
|
import com.v2ray.ang.AppConfig
|
||||||
import com.v2ray.ang.AppConfig.ANG_CONFIG
|
import com.v2ray.ang.AppConfig.ANG_CONFIG
|
||||||
import com.v2ray.ang.AppConfig.HTTPS_PROTOCOL
|
import com.v2ray.ang.AppConfig.PROTOCOL_HTTPS
|
||||||
import com.v2ray.ang.AppConfig.HTTP_PROTOCOL
|
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.R
|
||||||
import com.v2ray.ang.dto.*
|
import com.v2ray.ang.dto.*
|
||||||
import com.v2ray.ang.dto.V2rayConfig.Companion.DEFAULT_SECURITY
|
import com.v2ray.ang.dto.V2rayConfig.Companion.DEFAULT_SECURITY
|
||||||
@@ -20,7 +23,7 @@ import com.v2ray.ang.util.MmkvManager.KEY_SELECTED_SERVER
|
|||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import com.v2ray.ang.extension.idnHost
|
import com.v2ray.ang.extension.idnHost
|
||||||
import com.v2ray.ang.extension.toast
|
import com.v2ray.ang.extension.removeWhiteSpace
|
||||||
|
|
||||||
object AngConfigManager {
|
object AngConfigManager {
|
||||||
private val mainStorage by lazy {
|
private val mainStorage by lazy {
|
||||||
@@ -212,8 +215,8 @@ object AngConfigManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//maybe sub
|
//maybe sub
|
||||||
if (TextUtils.isEmpty(subid) && (str.startsWith(HTTP_PROTOCOL) || str.startsWith(
|
if (TextUtils.isEmpty(subid) && (str.startsWith(PROTOCOL_HTTP) || str.startsWith(
|
||||||
HTTPS_PROTOCOL
|
PROTOCOL_HTTPS
|
||||||
))
|
))
|
||||||
) {
|
) {
|
||||||
MmkvManager.importUrlAsSubscription(str)
|
MmkvManager.importUrlAsSubscription(str)
|
||||||
@@ -432,6 +435,28 @@ object AngConfigManager {
|
|||||||
queryParam["security"] ?: "", allowInsecure,
|
queryParam["security"] ?: "", allowInsecure,
|
||||||
queryParam["sni"] ?: sni, fingerprint, queryParam["alpn"], pbk, sid, spx
|
queryParam["sni"] ?: sni, fingerprint, queryParam["alpn"], pbk, sid, spx
|
||||||
)
|
)
|
||||||
|
} else if (str.startsWith(EConfigType.WIREGUARD.protocolScheme)) {
|
||||||
|
val uri = URI(Utils.fixIllegalUrl(str))
|
||||||
|
config = ServerConfig.create(EConfigType.WIREGUARD)
|
||||||
|
config.remarks = Utils.urlDecode(uri.fragment ?: "")
|
||||||
|
|
||||||
|
if (uri.rawQuery != null) {
|
||||||
|
val queryParam = uri.rawQuery.split("&")
|
||||||
|
.associate { it.split("=").let { (k, v) -> k to Utils.urlDecode(v) } }
|
||||||
|
|
||||||
|
config.outboundBean?.settings?.let { wireguard ->
|
||||||
|
wireguard.secretKey = uri.userInfo
|
||||||
|
wireguard.address =
|
||||||
|
(queryParam["address"] ?: WIREGUARD_LOCAL_ADDRESS_V4).removeWhiteSpace()
|
||||||
|
.split(",")
|
||||||
|
wireguard.peers?.get(0)?.publicKey = queryParam["publickey"] ?: ""
|
||||||
|
wireguard.peers?.get(0)?.endpoint = "${uri.idnHost}:${uri.port}"
|
||||||
|
wireguard.mtu = Utils.parseInt(queryParam["mtu"] ?: WIREGUARD_LOCAL_MTU)
|
||||||
|
wireguard.reserved =
|
||||||
|
(queryParam["reserved"] ?: "0,0,0").removeWhiteSpace().split(",")
|
||||||
|
.map { it.toInt() }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (config == null) {
|
if (config == null) {
|
||||||
return R.string.toast_incorrect_protocol
|
return R.string.toast_incorrect_protocol
|
||||||
@@ -443,7 +468,8 @@ object AngConfigManager {
|
|||||||
?.getServerAddress() == removedSelectedServer.getProxyOutbound()
|
?.getServerAddress() == removedSelectedServer.getProxyOutbound()
|
||||||
?.getServerAddress() &&
|
?.getServerAddress() &&
|
||||||
config.getProxyOutbound()
|
config.getProxyOutbound()
|
||||||
?.getServerPort() == removedSelectedServer.getProxyOutbound()?.getServerPort()
|
?.getServerPort() == removedSelectedServer.getProxyOutbound()
|
||||||
|
?.getServerPort()
|
||||||
) {
|
) {
|
||||||
mainStorage?.encode(KEY_SELECTED_SERVER, guid)
|
mainStorage?.encode(KEY_SELECTED_SERVER, guid)
|
||||||
}
|
}
|
||||||
@@ -460,7 +486,7 @@ object AngConfigManager {
|
|||||||
allowInsecure: Boolean
|
allowInsecure: Boolean
|
||||||
): Boolean {
|
): Boolean {
|
||||||
return runCatching {
|
return runCatching {
|
||||||
val uri = URI(uriString)
|
val uri = URI(Utils.fixIllegalUrl(uriString))
|
||||||
check(uri.scheme == "vmess")
|
check(uri.scheme == "vmess")
|
||||||
val (_, protocol, tlsStr, uuid, alterId) =
|
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})")
|
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})")
|
||||||
@@ -571,7 +597,11 @@ object AngConfigManager {
|
|||||||
try {
|
try {
|
||||||
val config = MmkvManager.decodeServerConfig(guid) ?: return ""
|
val config = MmkvManager.decodeServerConfig(guid) ?: return ""
|
||||||
val outbound = config.getProxyOutbound() ?: 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) {
|
return config.configType.protocolScheme + when (config.configType) {
|
||||||
EConfigType.VMESS -> {
|
EConfigType.VMESS -> {
|
||||||
val vmessQRCode = VmessQRCode()
|
val vmessQRCode = VmessQRCode()
|
||||||
@@ -600,7 +630,8 @@ object AngConfigManager {
|
|||||||
Utils.encode(json)
|
Utils.encode(json)
|
||||||
}
|
}
|
||||||
|
|
||||||
EConfigType.CUSTOM, EConfigType.WIREGUARD -> ""
|
EConfigType.CUSTOM -> ""
|
||||||
|
|
||||||
EConfigType.SHADOWSOCKS -> {
|
EConfigType.SHADOWSOCKS -> {
|
||||||
val remark = "#" + Utils.urlEncode(config.remarks)
|
val remark = "#" + Utils.urlEncode(config.remarks)
|
||||||
val pw =
|
val pw =
|
||||||
@@ -617,10 +648,13 @@ object AngConfigManager {
|
|||||||
EConfigType.SOCKS -> {
|
EConfigType.SOCKS -> {
|
||||||
val remark = "#" + Utils.urlEncode(config.remarks)
|
val remark = "#" + Utils.urlEncode(config.remarks)
|
||||||
val pw =
|
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(
|
val url = String.format(
|
||||||
"%s@%s:%s",
|
"%s@%s:%s",
|
||||||
pw,
|
Utils.encode(pw),
|
||||||
Utils.getIpv6Address(outbound.getServerAddress()!!),
|
Utils.getIpv6Address(outbound.getServerAddress()!!),
|
||||||
outbound.getServerPort()
|
outbound.getServerPort()
|
||||||
)
|
)
|
||||||
@@ -672,7 +706,8 @@ object AngConfigManager {
|
|||||||
dicQuery["spx"] = Utils.urlEncode(tlsSetting.spiderX!!)
|
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 ->
|
outbound.getTransportSettingDetails()?.let { transportDetails ->
|
||||||
when (streamSetting.network) {
|
when (streamSetting.network) {
|
||||||
@@ -733,6 +768,38 @@ object AngConfigManager {
|
|||||||
)
|
)
|
||||||
url + query + remark
|
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) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
@@ -858,17 +925,19 @@ object AngConfigManager {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
val removedSelectedServer =
|
val removedSelectedServer =
|
||||||
if (!TextUtils.isEmpty(subid) && !append) {
|
if (!TextUtils.isEmpty(subid) && !append) {
|
||||||
MmkvManager.decodeServerConfig(mainStorage?.decodeString(KEY_SELECTED_SERVER) ?: "")?.let {
|
MmkvManager.decodeServerConfig(
|
||||||
if (it.subscriptionId == subid) {
|
mainStorage?.decodeString(KEY_SELECTED_SERVER) ?: ""
|
||||||
return@let it
|
)?.let {
|
||||||
}
|
if (it.subscriptionId == subid) {
|
||||||
return@let null
|
return@let it
|
||||||
}
|
}
|
||||||
} else {
|
return@let null
|
||||||
null
|
|
||||||
}
|
}
|
||||||
if(!append) {
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
if (!append) {
|
||||||
MmkvManager.removeServerViaSubid(subid)
|
MmkvManager.removeServerViaSubid(subid)
|
||||||
}
|
}
|
||||||
// var servers = server
|
// var servers = server
|
||||||
@@ -908,4 +977,51 @@ object AngConfigManager {
|
|||||||
|
|
||||||
return true
|
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 serverList: Array<V2rayConfig> =
|
||||||
|
Gson().fromJson(server, Array<V2rayConfig>::class.java)
|
||||||
|
if (serverList.isNotEmpty()) {
|
||||||
|
var count = 0
|
||||||
|
for (srv in serverList) {
|
||||||
|
if (srv.inbounds != null && srv.outbounds != null && srv.routing != null) {
|
||||||
|
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 = System.currentTimeMillis().toString()
|
||||||
|
// config.remarks = config.fullConfig?.remarks ?: System.currentTimeMillis().toString()
|
||||||
|
val key = MmkvManager.encodeServerConfig("", config)
|
||||||
|
serverRawStorage?.encode(key, server)
|
||||||
|
return 1
|
||||||
|
} else {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,11 @@ package com.v2ray.ang.util
|
|||||||
|
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.tencent.mmkv.MMKV
|
import com.tencent.mmkv.MMKV
|
||||||
|
import com.v2ray.ang.dto.AssetUrlItem
|
||||||
import com.v2ray.ang.dto.ServerAffiliationInfo
|
import com.v2ray.ang.dto.ServerAffiliationInfo
|
||||||
import com.v2ray.ang.dto.ServerConfig
|
import com.v2ray.ang.dto.ServerConfig
|
||||||
import com.v2ray.ang.dto.SubscriptionItem
|
import com.v2ray.ang.dto.SubscriptionItem
|
||||||
|
import java.net.URI
|
||||||
|
|
||||||
object MmkvManager {
|
object MmkvManager {
|
||||||
const val ID_MAIN = "MAIN"
|
const val ID_MAIN = "MAIN"
|
||||||
@@ -12,6 +14,7 @@ object MmkvManager {
|
|||||||
const val ID_SERVER_RAW = "SERVER_RAW"
|
const val ID_SERVER_RAW = "SERVER_RAW"
|
||||||
const val ID_SERVER_AFF = "SERVER_AFF"
|
const val ID_SERVER_AFF = "SERVER_AFF"
|
||||||
const val ID_SUB = "SUB"
|
const val ID_SUB = "SUB"
|
||||||
|
const val ID_ASSET = "ASSET"
|
||||||
const val ID_SETTING = "SETTING"
|
const val ID_SETTING = "SETTING"
|
||||||
const val KEY_SELECTED_SERVER = "SELECTED_SERVER"
|
const val KEY_SELECTED_SERVER = "SELECTED_SERVER"
|
||||||
const val KEY_ANG_CONFIGS = "ANG_CONFIGS"
|
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 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 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 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> {
|
fun decodeServerList(): MutableList<String> {
|
||||||
val json = mainStorage?.decodeString(KEY_ANG_CONFIGS)
|
val json = mainStorage?.decodeString(KEY_ANG_CONFIGS)
|
||||||
@@ -118,8 +122,9 @@ object MmkvManager {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
val uri = URI(Utils.fixIllegalUrl(url))
|
||||||
val subItem = SubscriptionItem()
|
val subItem = SubscriptionItem()
|
||||||
subItem.remarks = "import sub"
|
subItem.remarks = Utils.urlDecode(uri.fragment ?: "import sub")
|
||||||
subItem.url = url
|
subItem.url = url
|
||||||
subStorage?.encode(Utils.getUuid(), Gson().toJson(subItem))
|
subStorage?.encode(Utils.getUuid(), Gson().toJson(subItem))
|
||||||
return 1
|
return 1
|
||||||
@@ -142,6 +147,22 @@ object MmkvManager {
|
|||||||
removeServerViaSubid(subid)
|
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() {
|
fun removeAllServer() {
|
||||||
mainStorage?.clearAll()
|
mainStorage?.clearAll()
|
||||||
serverStorage?.clearAll()
|
serverStorage?.clearAll()
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import android.content.res.Configuration.UI_MODE_NIGHT_NO
|
|||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.LocaleList
|
import android.os.LocaleList
|
||||||
|
import android.provider.Settings
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.util.Patterns
|
import android.util.Patterns
|
||||||
import android.webkit.URLUtil
|
import android.webkit.URLUtil
|
||||||
@@ -282,7 +283,7 @@ object Utils {
|
|||||||
|
|
||||||
fun urlDecode(url: String): String {
|
fun urlDecode(url: String): String {
|
||||||
return try {
|
return try {
|
||||||
URLDecoder.decode(URLDecoder.decode(url), "utf-8")
|
URLDecoder.decode(url, "UTF-8")
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
url
|
url
|
||||||
@@ -317,6 +318,11 @@ object Utils {
|
|||||||
return extDir.absolutePath
|
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 {
|
fun getUrlContext(url: String, timeout: Int): String {
|
||||||
var result: String
|
var result: String
|
||||||
var conn: HttpURLConnection? = null
|
var conn: HttpURLConnection? = null
|
||||||
@@ -360,7 +366,7 @@ object Utils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getIpv6Address(address: String): String {
|
fun getIpv6Address(address: String): String {
|
||||||
return if (isIpv6Address(address)) {
|
return if (isIpv6Address(address) && !address.contains('[') && !address.contains(']')) {
|
||||||
String.format("[%s]", address)
|
String.format("[%s]", address)
|
||||||
} else {
|
} else {
|
||||||
address
|
address
|
||||||
|
|||||||
@@ -2,11 +2,14 @@ package com.v2ray.ang.util
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import android.util.Log
|
|
||||||
import com.google.gson.*
|
import com.google.gson.*
|
||||||
import com.tencent.mmkv.MMKV
|
import com.tencent.mmkv.MMKV
|
||||||
import com.v2ray.ang.AppConfig
|
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.V2rayConfig
|
||||||
import com.v2ray.ang.dto.EConfigType
|
import com.v2ray.ang.dto.EConfigType
|
||||||
import com.v2ray.ang.dto.ERoutingMode
|
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
|
import com.v2ray.ang.dto.V2rayConfig.Companion.HTTP
|
||||||
|
|
||||||
object V2rayConfigUtil {
|
object V2rayConfigUtil {
|
||||||
private val serverRawStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SERVER_RAW, MMKV.MULTI_PROCESS_MODE) }
|
private val serverRawStorage by lazy {
|
||||||
private val settingsStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SETTING, MMKV.MULTI_PROCESS_MODE) }
|
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)
|
data class Result(var status: Boolean, var content: String)
|
||||||
|
|
||||||
@@ -32,12 +45,12 @@ object V2rayConfigUtil {
|
|||||||
} else {
|
} else {
|
||||||
raw
|
raw
|
||||||
}
|
}
|
||||||
Log.d(ANG_PACKAGE, customConfig)
|
//Log.d(ANG_PACKAGE, customConfig)
|
||||||
return Result(true, customConfig)
|
return Result(true, customConfig)
|
||||||
}
|
}
|
||||||
val outbound = config.getProxyOutbound() ?: return Result(false, "")
|
val outbound = config.getProxyOutbound() ?: return Result(false, "")
|
||||||
val result = getV2rayNonCustomConfig(context, outbound)
|
val result = getV2rayNonCustomConfig(context, outbound)
|
||||||
Log.d(ANG_PACKAGE, result.content)
|
//Log.d(ANG_PACKAGE, result.content)
|
||||||
return result
|
return result
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
@@ -48,7 +61,10 @@ object V2rayConfigUtil {
|
|||||||
/**
|
/**
|
||||||
* 生成v2ray的客户端配置文件
|
* 生成v2ray的客户端配置文件
|
||||||
*/
|
*/
|
||||||
private fun getV2rayNonCustomConfig(context: Context, outbound: V2rayConfig.OutboundBean): Result {
|
private fun getV2rayNonCustomConfig(
|
||||||
|
context: Context,
|
||||||
|
outbound: V2rayConfig.OutboundBean
|
||||||
|
): Result {
|
||||||
val result = Result(false, "")
|
val result = Result(false, "")
|
||||||
//取得默认配置
|
//取得默认配置
|
||||||
val assets = Utils.readTextFromAssets(context, "v2ray_config.json")
|
val assets = Utils.readTextFromAssets(context, "v2ray_config.json")
|
||||||
@@ -60,14 +76,15 @@ object V2rayConfigUtil {
|
|||||||
val v2rayConfig = Gson().fromJson(assets, V2rayConfig::class.java) ?: return result
|
val v2rayConfig = Gson().fromJson(assets, V2rayConfig::class.java) ?: return result
|
||||||
|
|
||||||
v2rayConfig.log.loglevel = settingsStorage?.decodeString(AppConfig.PREF_LOGLEVEL)
|
v2rayConfig.log.loglevel = settingsStorage?.decodeString(AppConfig.PREF_LOGLEVEL)
|
||||||
?: "warning"
|
?: "warning"
|
||||||
|
|
||||||
inbounds(v2rayConfig)
|
inbounds(v2rayConfig)
|
||||||
|
|
||||||
httpRequestObject(outbound)
|
updateOutboundWithGlobalSettings(outbound)
|
||||||
|
|
||||||
v2rayConfig.outbounds[0] = outbound
|
v2rayConfig.outbounds[0] = outbound
|
||||||
|
|
||||||
|
updateOutboundFragment(v2rayConfig)
|
||||||
|
|
||||||
routing(v2rayConfig)
|
routing(v2rayConfig)
|
||||||
|
|
||||||
fakedns(v2rayConfig)
|
fakedns(v2rayConfig)
|
||||||
@@ -91,8 +108,14 @@ object V2rayConfigUtil {
|
|||||||
*/
|
*/
|
||||||
private fun inbounds(v2rayConfig: V2rayConfig): Boolean {
|
private fun inbounds(v2rayConfig: V2rayConfig): Boolean {
|
||||||
try {
|
try {
|
||||||
val socksPort = Utils.parseInt(settingsStorage?.decodeString(AppConfig.PREF_SOCKS_PORT), AppConfig.PORT_SOCKS.toInt())
|
val socksPort = Utils.parseInt(
|
||||||
val httpPort = Utils.parseInt(settingsStorage?.decodeString(AppConfig.PREF_HTTP_PORT), AppConfig.PORT_HTTP.toInt())
|
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 ->
|
v2rayConfig.inbounds.forEach { curInbound ->
|
||||||
if (settingsStorage?.decodeBool(AppConfig.PREF_PROXY_SHARING) != true) {
|
if (settingsStorage?.decodeBool(AppConfig.PREF_PROXY_SHARING) != true) {
|
||||||
@@ -102,8 +125,9 @@ object V2rayConfigUtil {
|
|||||||
}
|
}
|
||||||
v2rayConfig.inbounds[0].port = socksPort
|
v2rayConfig.inbounds[0].port = socksPort
|
||||||
val fakedns = settingsStorage?.decodeBool(AppConfig.PREF_FAKE_DNS_ENABLED)
|
val fakedns = settingsStorage?.decodeBool(AppConfig.PREF_FAKE_DNS_ENABLED)
|
||||||
?: false
|
?: false
|
||||||
val sniffAllTlsAndHttp = settingsStorage?.decodeBool(AppConfig.PREF_SNIFFING_ENABLED, true)
|
val sniffAllTlsAndHttp =
|
||||||
|
settingsStorage?.decodeBool(AppConfig.PREF_SNIFFING_ENABLED, true)
|
||||||
?: true
|
?: true
|
||||||
v2rayConfig.inbounds[0].sniffing?.enabled = fakedns || sniffAllTlsAndHttp
|
v2rayConfig.inbounds[0].sniffing?.enabled = fakedns || sniffAllTlsAndHttp
|
||||||
if (!sniffAllTlsAndHttp) {
|
if (!sniffAllTlsAndHttp) {
|
||||||
@@ -129,11 +153,14 @@ object V2rayConfigUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun fakedns(v2rayConfig: V2rayConfig) {
|
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.fakedns = listOf(V2rayConfig.FakednsBean())
|
||||||
v2rayConfig.outbounds.filter { it.protocol == "freedom" }.forEach {
|
v2rayConfig.outbounds.filter { it.protocol == PROTOCOL_FREEDOM && it.tag == TAG_DIRECT }
|
||||||
it.settings?.domainStrategy = "UseIP"
|
.forEach {
|
||||||
}
|
it.settings?.domainStrategy = "UseIP"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,38 +169,49 @@ object V2rayConfigUtil {
|
|||||||
*/
|
*/
|
||||||
private fun routing(v2rayConfig: V2rayConfig): Boolean {
|
private fun routing(v2rayConfig: V2rayConfig): Boolean {
|
||||||
try {
|
try {
|
||||||
routingUserRule(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_AGENT)
|
routingUserRule(
|
||||||
?: "", AppConfig.TAG_AGENT, v2rayConfig)
|
settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_AGENT)
|
||||||
routingUserRule(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_DIRECT)
|
?: "", AppConfig.TAG_AGENT, v2rayConfig
|
||||||
?: "", AppConfig.TAG_DIRECT, v2rayConfig)
|
)
|
||||||
routingUserRule(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_BLOCKED)
|
routingUserRule(
|
||||||
?: "", AppConfig.TAG_BLOCKED, v2rayConfig)
|
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"
|
?: "IPIfNonMatch"
|
||||||
// v2rayConfig.routing.domainMatcher = "mph"
|
// 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
|
// Hardcode googleapis.cn
|
||||||
val googleapisRoute = V2rayConfig.RoutingBean.RulesBean(
|
val googleapisRoute = V2rayConfig.RoutingBean.RulesBean(
|
||||||
type = "field",
|
type = "field",
|
||||||
outboundTag = AppConfig.TAG_AGENT,
|
outboundTag = AppConfig.TAG_AGENT,
|
||||||
domain = arrayListOf("domain:googleapis.cn")
|
domain = arrayListOf("domain:googleapis.cn")
|
||||||
)
|
)
|
||||||
|
|
||||||
when (routingMode) {
|
when (routingMode) {
|
||||||
ERoutingMode.BYPASS_LAN.value -> {
|
ERoutingMode.BYPASS_LAN.value -> {
|
||||||
routingGeo("ip", "private", AppConfig.TAG_DIRECT, v2rayConfig)
|
routingGeo("ip", "private", AppConfig.TAG_DIRECT, v2rayConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
ERoutingMode.BYPASS_MAINLAND.value -> {
|
ERoutingMode.BYPASS_MAINLAND.value -> {
|
||||||
routingGeo("", "cn", AppConfig.TAG_DIRECT, v2rayConfig)
|
routingGeo("", "cn", AppConfig.TAG_DIRECT, v2rayConfig)
|
||||||
v2rayConfig.routing.rules.add(0, googleapisRoute)
|
v2rayConfig.routing.rules.add(0, googleapisRoute)
|
||||||
}
|
}
|
||||||
|
|
||||||
ERoutingMode.BYPASS_LAN_MAINLAND.value -> {
|
ERoutingMode.BYPASS_LAN_MAINLAND.value -> {
|
||||||
routingGeo("ip", "private", AppConfig.TAG_DIRECT, v2rayConfig)
|
routingGeo("ip", "private", AppConfig.TAG_DIRECT, v2rayConfig)
|
||||||
routingGeo("", "cn", AppConfig.TAG_DIRECT, v2rayConfig)
|
routingGeo("", "cn", AppConfig.TAG_DIRECT, v2rayConfig)
|
||||||
v2rayConfig.routing.rules.add(0, googleapisRoute)
|
v2rayConfig.routing.rules.add(0, googleapisRoute)
|
||||||
}
|
}
|
||||||
|
|
||||||
ERoutingMode.GLOBAL_DIRECT.value -> {
|
ERoutingMode.GLOBAL_DIRECT.value -> {
|
||||||
val globalDirect = V2rayConfig.RoutingBean.RulesBean(
|
val globalDirect = V2rayConfig.RoutingBean.RulesBean(
|
||||||
type = "field",
|
type = "field",
|
||||||
@@ -190,7 +228,12 @@ object V2rayConfigUtil {
|
|||||||
return true
|
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 {
|
try {
|
||||||
if (!TextUtils.isEmpty(code)) {
|
if (!TextUtils.isEmpty(code)) {
|
||||||
//IP
|
//IP
|
||||||
@@ -275,51 +318,70 @@ object V2rayConfigUtil {
|
|||||||
try {
|
try {
|
||||||
if (settingsStorage?.decodeBool(AppConfig.PREF_FAKE_DNS_ENABLED) == true) {
|
if (settingsStorage?.decodeBool(AppConfig.PREF_FAKE_DNS_ENABLED) == true) {
|
||||||
val geositeCn = arrayListOf("geosite:cn")
|
val geositeCn = arrayListOf("geosite:cn")
|
||||||
val proxyDomain = userRule2Domian(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_AGENT)
|
val proxyDomain = userRule2Domian(
|
||||||
?: "")
|
settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_AGENT)
|
||||||
val directDomain = userRule2Domian(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_DIRECT)
|
?: ""
|
||||||
?: "")
|
)
|
||||||
|
val directDomain = userRule2Domian(
|
||||||
|
settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_DIRECT)
|
||||||
|
?: ""
|
||||||
|
)
|
||||||
// fakedns with all domains to make it always top priority
|
// fakedns with all domains to make it always top priority
|
||||||
v2rayConfig.dns.servers?.add(0,
|
v2rayConfig.dns.servers?.add(
|
||||||
V2rayConfig.DnsBean.ServersBean(address = "fakedns", domains = geositeCn.plus(proxyDomain).plus(directDomain)))
|
0,
|
||||||
|
V2rayConfig.DnsBean.ServersBean(
|
||||||
|
address = "fakedns",
|
||||||
|
domains = geositeCn.plus(proxyDomain).plus(directDomain)
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DNS inbound对象
|
// DNS inbound对象
|
||||||
val remoteDns = Utils.getRemoteDnsServers()
|
val remoteDns = Utils.getRemoteDnsServers()
|
||||||
if (v2rayConfig.inbounds.none { e -> e.protocol == "dokodemo-door" && e.tag == "dns-in" }) {
|
if (v2rayConfig.inbounds.none { e -> e.protocol == "dokodemo-door" && e.tag == "dns-in" }) {
|
||||||
val dnsInboundSettings = V2rayConfig.InboundBean.InSettingsBean(
|
val dnsInboundSettings = V2rayConfig.InboundBean.InSettingsBean(
|
||||||
address = if (Utils.isPureIpAddress(remoteDns.first())) remoteDns.first() else "1.1.1.1",
|
address = if (Utils.isPureIpAddress(remoteDns.first())) remoteDns.first() else "1.1.1.1",
|
||||||
port = 53,
|
port = 53,
|
||||||
network = "tcp,udp")
|
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.inbounds.add(
|
||||||
V2rayConfig.InboundBean(
|
V2rayConfig.InboundBean(
|
||||||
tag = "dns-in",
|
tag = "dns-in",
|
||||||
port = localDnsPort,
|
port = localDnsPort,
|
||||||
listen = "127.0.0.1",
|
listen = "127.0.0.1",
|
||||||
protocol = "dokodemo-door",
|
protocol = "dokodemo-door",
|
||||||
settings = dnsInboundSettings,
|
settings = dnsInboundSettings,
|
||||||
sniffing = null))
|
sniffing = null
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DNS outbound对象
|
// DNS outbound对象
|
||||||
if (v2rayConfig.outbounds.none { e -> e.protocol == "dns" && e.tag == "dns-out" }) {
|
if (v2rayConfig.outbounds.none { e -> e.protocol == "dns" && e.tag == "dns-out" }) {
|
||||||
v2rayConfig.outbounds.add(
|
v2rayConfig.outbounds.add(
|
||||||
V2rayConfig.OutboundBean(
|
V2rayConfig.OutboundBean(
|
||||||
protocol = "dns",
|
protocol = "dns",
|
||||||
tag = "dns-out",
|
tag = "dns-out",
|
||||||
settings = null,
|
settings = null,
|
||||||
streamSettings = null,
|
streamSettings = null,
|
||||||
mux = null))
|
mux = null
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DNS routing tag
|
// DNS routing tag
|
||||||
v2rayConfig.routing.rules.add(0, V2rayConfig.RoutingBean.RulesBean(
|
v2rayConfig.routing.rules.add(
|
||||||
|
0, V2rayConfig.RoutingBean.RulesBean(
|
||||||
type = "field",
|
type = "field",
|
||||||
inboundTag = arrayListOf("dns-in"),
|
inboundTag = arrayListOf("dns-in"),
|
||||||
outboundTag = "dns-out",
|
outboundTag = "dns-out",
|
||||||
domain = null)
|
domain = null
|
||||||
|
)
|
||||||
)
|
)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
@@ -333,43 +395,73 @@ object V2rayConfigUtil {
|
|||||||
val hosts = mutableMapOf<String, String>()
|
val hosts = mutableMapOf<String, String>()
|
||||||
val servers = ArrayList<Any>()
|
val servers = ArrayList<Any>()
|
||||||
val remoteDns = Utils.getRemoteDnsServers()
|
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 {
|
remoteDns.forEach {
|
||||||
servers.add(it)
|
servers.add(it)
|
||||||
}
|
}
|
||||||
if (proxyDomain.size > 0) {
|
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
|
// domestic DNS
|
||||||
val directDomain = userRule2Domian(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_DIRECT)
|
val directDomain = userRule2Domian(
|
||||||
?: "")
|
settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_DIRECT)
|
||||||
val routingMode = settingsStorage?.decodeString(AppConfig.PREF_ROUTING_MODE) ?: ERoutingMode.GLOBAL_PROXY.value
|
?: ""
|
||||||
|
)
|
||||||
|
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) {
|
if (directDomain.size > 0 || routingMode == ERoutingMode.BYPASS_MAINLAND.value || routingMode == ERoutingMode.BYPASS_LAN_MAINLAND.value) {
|
||||||
val domesticDns = Utils.getDomesticDnsServers()
|
val domesticDns = Utils.getDomesticDnsServers()
|
||||||
val geositeCn = arrayListOf("geosite:cn")
|
val geositeCn = arrayListOf("geosite:cn")
|
||||||
val geoipCn = arrayListOf("geoip:cn")
|
val geoipCn = arrayListOf("geoip:cn")
|
||||||
if (directDomain.size > 0) {
|
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) {
|
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())) {
|
if (Utils.isPureIpAddress(domesticDns.first())) {
|
||||||
v2rayConfig.routing.rules.add(0, V2rayConfig.RoutingBean.RulesBean(
|
v2rayConfig.routing.rules.add(
|
||||||
|
0, V2rayConfig.RoutingBean.RulesBean(
|
||||||
type = "field",
|
type = "field",
|
||||||
outboundTag = AppConfig.TAG_DIRECT,
|
outboundTag = AppConfig.TAG_DIRECT,
|
||||||
port = "53",
|
port = "53",
|
||||||
ip = arrayListOf(domesticDns.first()),
|
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) {
|
if (blkDomain.size > 0) {
|
||||||
hosts.putAll(blkDomain.map { it to "127.0.0.1" })
|
hosts.putAll(blkDomain.map { it to "127.0.0.1" })
|
||||||
}
|
}
|
||||||
@@ -379,17 +471,20 @@ object V2rayConfigUtil {
|
|||||||
|
|
||||||
// DNS dns对象
|
// DNS dns对象
|
||||||
v2rayConfig.dns = V2rayConfig.DnsBean(
|
v2rayConfig.dns = V2rayConfig.DnsBean(
|
||||||
servers = servers,
|
servers = servers,
|
||||||
hosts = hosts)
|
hosts = hosts
|
||||||
|
)
|
||||||
|
|
||||||
// DNS routing
|
// DNS routing
|
||||||
if (Utils.isPureIpAddress(remoteDns.first())) {
|
if (Utils.isPureIpAddress(remoteDns.first())) {
|
||||||
v2rayConfig.routing.rules.add(0, V2rayConfig.RoutingBean.RulesBean(
|
v2rayConfig.routing.rules.add(
|
||||||
|
0, V2rayConfig.RoutingBean.RulesBean(
|
||||||
type = "field",
|
type = "field",
|
||||||
outboundTag = AppConfig.TAG_AGENT,
|
outboundTag = AppConfig.TAG_AGENT,
|
||||||
port = "53",
|
port = "53",
|
||||||
ip = arrayListOf(remoteDns.first()),
|
ip = arrayListOf(remoteDns.first()),
|
||||||
domain = null)
|
domain = null
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@@ -399,10 +494,49 @@ object V2rayConfigUtil {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun httpRequestObject(outbound: V2rayConfig.OutboundBean): Boolean {
|
private fun updateOutboundWithGlobalSettings(outbound: V2rayConfig.OutboundBean): Boolean {
|
||||||
try {
|
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)
|
||||||
|
&& outbound.settings?.vnext?.get(0)?.users?.get(0)?.flow?.isNotEmpty() == true
|
||||||
|
) {
|
||||||
|
muxEnabled = false
|
||||||
|
}
|
||||||
|
if (muxEnabled == true) {
|
||||||
|
outbound.mux?.enabled = true
|
||||||
|
outbound.mux?.concurrency =
|
||||||
|
settingsStorage?.decodeInt(AppConfig.PREF_MUX_CONCURRENCY) ?: 8
|
||||||
|
outbound.mux?.xudpConcurrency =
|
||||||
|
settingsStorage?.decodeInt(AppConfig.PREF_MUX_XUDP_CONCURRENCY) ?: 8
|
||||||
|
outbound.mux?.xudpProxyUDP443 =
|
||||||
|
settingsStorage?.decodeString(AppConfig.PREF_MUX_XUDP_QUIC) ?: "reject"
|
||||||
|
} else {
|
||||||
|
outbound.mux?.enabled = false
|
||||||
|
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
|
if (outbound.streamSettings?.network == DEFAULT_NETWORK
|
||||||
&& outbound.streamSettings?.tcpSettings?.header?.type == HTTP) {
|
&& outbound.streamSettings?.tcpSettings?.header?.type == HTTP
|
||||||
|
) {
|
||||||
val path = outbound.streamSettings?.tcpSettings?.header?.request?.path
|
val path = outbound.streamSettings?.tcpSettings?.header?.request?.path
|
||||||
val host = outbound.streamSettings?.tcpSettings?.header?.request?.headers?.Host
|
val host = outbound.streamSettings?.tcpSettings?.header?.request?.headers?.Host
|
||||||
|
|
||||||
@@ -422,6 +556,7 @@ object V2rayConfigUtil {
|
|||||||
outbound.streamSettings?.tcpSettings?.header?.request?.headers?.Host = host!!
|
outbound.streamSettings?.tcpSettings?.header?.request?.headers?.Host = host!!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
return false
|
return false
|
||||||
@@ -429,4 +564,50 @@ object V2rayConfigUtil {
|
|||||||
return true
|
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
|
||||||
|
)
|
||||||
|
fragmentOutbound.settings = V2rayConfig.OutboundBean.OutSettingsBean(
|
||||||
|
fragment = V2rayConfig.OutboundBean.OutSettingsBean.FragmentBean(
|
||||||
|
packets = settingsStorage?.decodeString(AppConfig.PREF_FRAGMENT_PACKETS)
|
||||||
|
?: "tlshello",
|
||||||
|
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.app.Application
|
||||||
import android.content.*
|
import android.content.*
|
||||||
|
import android.os.Build
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.widget.ArrayAdapter
|
import android.widget.ArrayAdapter
|
||||||
@@ -24,12 +25,28 @@ import kotlinx.coroutines.*
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class MainViewModel(application: Application) : AndroidViewModel(application) {
|
class MainViewModel(application: Application) : AndroidViewModel(application) {
|
||||||
private val mainStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_MAIN, MMKV.MULTI_PROCESS_MODE) }
|
private val mainStorage by lazy {
|
||||||
private val serverRawStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SERVER_RAW, MMKV.MULTI_PROCESS_MODE) }
|
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 serverList = MmkvManager.decodeServerList()
|
||||||
var subscriptionId: String = ""
|
var subscriptionId: String = settingsStorage.decodeString(AppConfig.CACHE_SUBSCRIPTION_ID, "")!!
|
||||||
var keywordFilter: String = ""
|
var keywordFilter: String = settingsStorage.decodeString(AppConfig.CACHE_KEYWORD_FILTER, "")!!
|
||||||
private set
|
private set
|
||||||
val serversCache = mutableListOf<ServersCache>()
|
val serversCache = mutableListOf<ServersCache>()
|
||||||
val isRunning by lazy { MutableLiveData<Boolean>() }
|
val isRunning by lazy { MutableLiveData<Boolean>() }
|
||||||
@@ -40,7 +57,18 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
|
|||||||
|
|
||||||
fun startListenBroadcast() {
|
fun startListenBroadcast() {
|
||||||
isRunning.value = false
|
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, "")
|
MessageUtil.sendMsg2Service(getApplication(), AppConfig.MSG_REGISTER_CLIENT, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,7 +90,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
|
|||||||
serverList.remove(guid)
|
serverList.remove(guid)
|
||||||
MmkvManager.removeServer(guid)
|
MmkvManager.removeServer(guid)
|
||||||
val index = getPosition(guid)
|
val index = getPosition(guid)
|
||||||
if(index >= 0){
|
if (index >= 0) {
|
||||||
serversCache.removeAt(index)
|
serversCache.removeAt(index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -75,7 +103,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
|
|||||||
val key = MmkvManager.encodeServerConfig("", config)
|
val key = MmkvManager.encodeServerConfig("", config)
|
||||||
serverRawStorage?.encode(key, server)
|
serverRawStorage?.encode(key, server)
|
||||||
serverList.add(0, key)
|
serverList.add(0, key)
|
||||||
serversCache.add(0, ServersCache(key,config))
|
serversCache.add(0, ServersCache(key, config))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun swapServer(fromPosition: Int, toPosition: Int) {
|
fun swapServer(fromPosition: Int, toPosition: Int) {
|
||||||
@@ -115,7 +143,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
|
|||||||
val testResult = SpeedtestUtil.tcping(serverAddress, serverPort)
|
val testResult = SpeedtestUtil.tcping(serverAddress, serverPort)
|
||||||
launch(Dispatchers.Main) {
|
launch(Dispatchers.Main) {
|
||||||
MmkvManager.encodeServerTestDelayMillis(item.guid, testResult)
|
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()
|
MmkvManager.clearAllTestDelayResults()
|
||||||
updateListAction.value = -1 // update all
|
updateListAction.value = -1 // update all
|
||||||
|
|
||||||
|
val serversCopy = serversCache.toList() // Create a copy of the list
|
||||||
|
|
||||||
getApplication<AngApplication>().toast(R.string.connection_test_testing)
|
getApplication<AngApplication>().toast(R.string.connection_test_testing)
|
||||||
viewModelScope.launch(Dispatchers.Default) { // without Dispatchers.Default viewModelScope will launch in main thread
|
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)
|
val config = V2rayConfigUtil.getV2rayConfig(getApplication(), item.guid)
|
||||||
if (config.status) {
|
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, "")
|
MessageUtil.sendMsg2Service(getApplication(), AppConfig.MSG_MEASURE_DELAY, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun filterConfig(context :Context) {
|
fun filterConfig(context: Context) {
|
||||||
val subscriptions = MmkvManager.decodeSubscriptions()
|
val subscriptions = MmkvManager.decodeSubscriptions()
|
||||||
val listId = subscriptions.map { it.first }.toList().toMutableList()
|
val listId = subscriptions.map { it.first }.toList().toMutableList()
|
||||||
val listRemarks = subscriptions.map { it.second.remarks }.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))
|
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.spSubscriptionId.setSelection(checkedItem)
|
||||||
ivBinding.etKeyword.text = Utils.getEditable(keywordFilter)
|
ivBinding.etKeyword.text = Utils.getEditable(keywordFilter)
|
||||||
val builder = AlertDialog.Builder(context).setView(ivBinding.root)
|
val builder = AlertDialog.Builder(context).setView(ivBinding.root)
|
||||||
@@ -169,6 +207,8 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
|
|||||||
subscriptions[position].first
|
subscriptions[position].first
|
||||||
}
|
}
|
||||||
keywordFilter = ivBinding.etKeyword.text.toString()
|
keywordFilter = ivBinding.etKeyword.text.toString()
|
||||||
|
settingsStorage?.encode(AppConfig.CACHE_SUBSCRIPTION_ID, subscriptionId)
|
||||||
|
settingsStorage?.encode(AppConfig.CACHE_KEYWORD_FILTER, keywordFilter)
|
||||||
reloadServerList()
|
reloadServerList()
|
||||||
|
|
||||||
dialogInterface?.dismiss()
|
dialogInterface?.dismiss()
|
||||||
@@ -193,7 +233,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
|
|||||||
// }.show()
|
// }.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getPosition(guid: String) : Int {
|
fun getPosition(guid: String): Int {
|
||||||
serversCache.forEachIndexed { index, it ->
|
serversCache.forEachIndexed { index, it ->
|
||||||
if (it.guid == guid)
|
if (it.guid == guid)
|
||||||
return index
|
return index
|
||||||
@@ -206,20 +246,24 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
|
|||||||
serversCache.forEachIndexed { index, it ->
|
serversCache.forEachIndexed { index, it ->
|
||||||
val outbound = it.config.getProxyOutbound()
|
val outbound = it.config.getProxyOutbound()
|
||||||
serversCache.forEachIndexed { index2, it2 ->
|
serversCache.forEachIndexed { index2, it2 ->
|
||||||
if(index2 > index){
|
if (index2 > index) {
|
||||||
val outbound2 = it2.config.getProxyOutbound()
|
val outbound2 = it2.config.getProxyOutbound()
|
||||||
if( outbound == outbound2 && !deleteServer.contains(it2.guid))
|
if (outbound == outbound2 && !deleteServer.contains(it2.guid)) {
|
||||||
{
|
|
||||||
deleteServer.add(it2.guid)
|
deleteServer.add(it2.guid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for(it in deleteServer){
|
for (it in deleteServer) {
|
||||||
MmkvManager.removeServer(it)
|
MmkvManager.removeServer(it)
|
||||||
}
|
}
|
||||||
reloadServerList()
|
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() {
|
private val mMsgReceiver = object : BroadcastReceiver() {
|
||||||
@@ -228,23 +272,29 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
|
|||||||
AppConfig.MSG_STATE_RUNNING -> {
|
AppConfig.MSG_STATE_RUNNING -> {
|
||||||
isRunning.value = true
|
isRunning.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
AppConfig.MSG_STATE_NOT_RUNNING -> {
|
AppConfig.MSG_STATE_NOT_RUNNING -> {
|
||||||
isRunning.value = false
|
isRunning.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
AppConfig.MSG_STATE_START_SUCCESS -> {
|
AppConfig.MSG_STATE_START_SUCCESS -> {
|
||||||
getApplication<AngApplication>().toast(R.string.toast_services_success)
|
getApplication<AngApplication>().toast(R.string.toast_services_success)
|
||||||
isRunning.value = true
|
isRunning.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
AppConfig.MSG_STATE_START_FAILURE -> {
|
AppConfig.MSG_STATE_START_FAILURE -> {
|
||||||
getApplication<AngApplication>().toast(R.string.toast_services_failure)
|
getApplication<AngApplication>().toast(R.string.toast_services_failure)
|
||||||
isRunning.value = false
|
isRunning.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
AppConfig.MSG_STATE_STOP_SUCCESS -> {
|
AppConfig.MSG_STATE_STOP_SUCCESS -> {
|
||||||
isRunning.value = false
|
isRunning.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
AppConfig.MSG_MEASURE_DELAY_SUCCESS -> {
|
AppConfig.MSG_MEASURE_DELAY_SUCCESS -> {
|
||||||
updateTestResultAction.value = intent.getStringExtra("content")
|
updateTestResultAction.value = intent.getStringExtra("content")
|
||||||
}
|
}
|
||||||
|
|
||||||
AppConfig.MSG_MEASURE_CONFIG_SUCCESS -> {
|
AppConfig.MSG_MEASURE_CONFIG_SUCCESS -> {
|
||||||
val resultPair = intent.getSerializableExtra("content") as Pair<String, Long>
|
val resultPair = intent.getSerializableExtra("content") as Pair<String, Long>
|
||||||
MmkvManager.encodeServerTestDelayMillis(resultPair.first, resultPair.second)
|
MmkvManager.encodeServerTestDelayMillis(resultPair.first, resultPair.second)
|
||||||
|
|||||||
@@ -39,7 +39,12 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
|
|||||||
AppConfig.PREF_ROUTING_MODE,
|
AppConfig.PREF_ROUTING_MODE,
|
||||||
AppConfig.PREF_V2RAY_ROUTING_AGENT,
|
AppConfig.PREF_V2RAY_ROUTING_AGENT,
|
||||||
AppConfig.PREF_V2RAY_ROUTING_BLOCKED,
|
AppConfig.PREF_V2RAY_ROUTING_BLOCKED,
|
||||||
AppConfig.PREF_V2RAY_ROUTING_DIRECT, -> {
|
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, ""))
|
settingsStorage?.encode(key, sharedPreferences.getString(key, ""))
|
||||||
}
|
}
|
||||||
AppConfig.PREF_SPEED_ENABLED,
|
AppConfig.PREF_SPEED_ENABLED,
|
||||||
@@ -51,12 +56,19 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
|
|||||||
AppConfig.PREF_PER_APP_PROXY,
|
AppConfig.PREF_PER_APP_PROXY,
|
||||||
AppConfig.PREF_BYPASS_APPS,
|
AppConfig.PREF_BYPASS_APPS,
|
||||||
AppConfig.PREF_CONFIRM_REMOVE,
|
AppConfig.PREF_CONFIRM_REMOVE,
|
||||||
AppConfig.PREF_START_SCAN_IMMEDIATE, -> {
|
AppConfig.PREF_START_SCAN_IMMEDIATE,
|
||||||
|
AppConfig.SUBSCRIPTION_AUTO_UPDATE,
|
||||||
|
AppConfig.PREF_FRAGMENT_ENABLED,
|
||||||
|
AppConfig.PREF_MUX_ENABLED, -> {
|
||||||
settingsStorage?.encode(key, sharedPreferences.getBoolean(key, false))
|
settingsStorage?.encode(key, sharedPreferences.getBoolean(key, false))
|
||||||
}
|
}
|
||||||
AppConfig.PREF_SNIFFING_ENABLED -> {
|
AppConfig.PREF_SNIFFING_ENABLED -> {
|
||||||
settingsStorage?.encode(key, sharedPreferences.getBoolean(key, true))
|
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)
|
||||||
|
}
|
||||||
AppConfig.PREF_PER_APP_PROXY_SET -> {
|
AppConfig.PREF_PER_APP_PROXY_SET -> {
|
||||||
settingsStorage?.encode(key, sharedPreferences.getStringSet(key, setOf()))
|
settingsStorage?.encode(key, sharedPreferences.getStringSet(key, setOf()))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@
|
|||||||
android:id="@+id/layout_test"
|
android:id="@+id/layout_test"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="@dimen/connection_test_height"
|
android:layout_height="@dimen/connection_test_height"
|
||||||
android:background="?attr/colorPrimary"
|
android:background="@color/colorMainTestBg"
|
||||||
android:gravity="center|start"
|
android:gravity="center|start"
|
||||||
android:nextFocusRight="@+id/fab"
|
android:nextFocusRight="@+id/fab"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
|
|||||||
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>
|
||||||
@@ -69,6 +69,28 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/layout_margin_top_height"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1.0"
|
||||||
|
android:text="@string/sub_auto_update" />
|
||||||
|
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatCheckBox
|
||||||
|
android:id="@+id/auto_update_check"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingStart="2dp"
|
||||||
|
android:paddingEnd="6dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -94,6 +116,7 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
|||||||
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"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<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_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
@@ -11,5 +12,5 @@
|
|||||||
android:layout_width="330dp"
|
android:layout_width="330dp"
|
||||||
android:layout_height="330dp"
|
android:layout_height="330dp"
|
||||||
android:scaleType="fitXY"
|
android:scaleType="fitXY"
|
||||||
android:src="@drawable/ic_fab_check" />
|
app:srcCompat="@drawable/ic_fab_check" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:background="@color/colorPrimary"
|
android:background="@color/colorPrimary"
|
||||||
android:foreground="?android:attr/selectableItemBackground"
|
android:foreground="?attr/selectableItemBackground"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true">
|
android:focusable="true">
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:nextFocusRight="@+id/layout_share"
|
android:nextFocusRight="@+id/layout_share"
|
||||||
android:background="@color/colorPrimary"
|
android:background="@color/colorPrimary"
|
||||||
android:foreground="?android:attr/selectableItemBackground"
|
android:foreground="?attr/selectableItemBackground"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="@dimen/server_height"
|
android:layout_height="@dimen/server_height"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
@@ -41,7 +41,12 @@
|
|||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:paddingStart="9dp">
|
android:paddingStart="9dp">
|
||||||
|
|
||||||
<TextView
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
android:id="@+id/tv_name"
|
android:id="@+id/tv_name"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -49,18 +54,18 @@
|
|||||||
android:minLines="1"
|
android:minLines="1"
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Subhead" />
|
android:textAppearance="@style/TextAppearance.AppCompat.Subhead" />
|
||||||
|
|
||||||
<LinearLayout
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="0dp"
|
||||||
android:orientation="horizontal">
|
android:layout_weight="1" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tv_statistics"
|
android:id="@+id/tv_statistics"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="10dp"
|
|
||||||
android:lines="1"
|
android:lines="1"
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Small" />
|
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||||
|
android:textSize="12sp"/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
@@ -111,7 +116,7 @@
|
|||||||
android:id="@+id/layout_share"
|
android:id="@+id/layout_share"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="@dimen/server_height"
|
android:layout_height="@dimen/server_height"
|
||||||
android:background="?android:attr/selectableItemBackground"
|
android:background="?attr/selectableItemBackground"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
@@ -122,7 +127,7 @@
|
|||||||
<ImageView
|
<ImageView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="@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" />
|
app:tint="?attr/colorMainText" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@@ -131,7 +136,7 @@
|
|||||||
android:id="@+id/layout_edit"
|
android:id="@+id/layout_edit"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="@dimen/server_height"
|
android:layout_height="@dimen/server_height"
|
||||||
android:background="?android:attr/selectableItemBackground"
|
android:background="?attr/selectableItemBackground"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
@@ -141,7 +146,7 @@
|
|||||||
<ImageView
|
<ImageView
|
||||||
android:layout_width="@dimen/png_height"
|
android:layout_width="@dimen/png_height"
|
||||||
android:layout_height="@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" />
|
app:tint="?attr/colorMainText" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@@ -150,7 +155,7 @@
|
|||||||
android:id="@+id/layout_remove"
|
android:id="@+id/layout_remove"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="@dimen/server_height"
|
android:layout_height="@dimen/server_height"
|
||||||
android:background="?android:attr/selectableItemBackground"
|
android:background="?attr/selectableItemBackground"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
@@ -160,7 +165,7 @@
|
|||||||
<ImageView
|
<ImageView
|
||||||
android:layout_width="@dimen/png_height"
|
android:layout_width="@dimen/png_height"
|
||||||
android:layout_height="@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" />
|
app:tint="?attr/colorMainText" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:background="@color/colorPrimary"
|
android:background="@color/colorPrimary"
|
||||||
android:foreground="?android:attr/selectableItemBackground"
|
android:foreground="?attr/selectableItemBackground"
|
||||||
android:nextFocusRight="@+id/layout_edit"
|
android:nextFocusRight="@+id/layout_edit"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="@dimen/server_height"
|
android:layout_height="@dimen/server_height"
|
||||||
@@ -68,7 +68,7 @@
|
|||||||
android:id="@+id/layout_share"
|
android:id="@+id/layout_share"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="?android:attr/selectableItemBackground"
|
android:background="?attr/selectableItemBackground"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
@@ -79,7 +79,7 @@
|
|||||||
<ImageView
|
<ImageView
|
||||||
android:layout_width="@dimen/png_height"
|
android:layout_width="@dimen/png_height"
|
||||||
android:layout_height="@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"/>
|
app:tint="?attr/colorMainText"/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@@ -88,7 +88,7 @@
|
|||||||
android:id="@+id/layout_edit"
|
android:id="@+id/layout_edit"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="?android:attr/selectableItemBackground"
|
android:background="?attr/selectableItemBackground"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
@@ -99,7 +99,7 @@
|
|||||||
<ImageView
|
<ImageView
|
||||||
android:layout_width="@dimen/png_height"
|
android:layout_width="@dimen/png_height"
|
||||||
android:layout_height="@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" />
|
app:tint="?attr/colorMainText" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@@ -17,36 +17,70 @@
|
|||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:background="@color/colorPrimary"
|
android:background="@color/colorPrimary"
|
||||||
android:foreground="?android:attr/selectableItemBackground"
|
android:foreground="?attr/selectableItemBackground"
|
||||||
android:padding="@dimen/nav_header_vertical_spacing">
|
android:padding="@dimen/nav_header_vertical_spacing">
|
||||||
|
|
||||||
<LinearLayout
|
<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_height="wrap_content"
|
||||||
android:layout_weight="1"
|
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/asset_name"
|
android:id="@+id/asset_name"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:minLines="1"
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
|
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
|
||||||
tools:text="Placeholder.dat" />
|
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:id="@+id/asset_properties"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:textAlignment="textEnd"
|
android:lines="1"
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||||
|
android:textSize="12sp"
|
||||||
tools:text="1MB . 2020.01.01" />
|
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>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/layout_remove"
|
android:id="@+id/layout_remove"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="?android:attr/selectableItemBackground"
|
android:background="?attr/selectableItemBackground"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:padding="@dimen/nav_header_vertical_spacing">
|
android:padding="@dimen/nav_header_vertical_spacing">
|
||||||
@@ -54,7 +88,7 @@
|
|||||||
<ImageView
|
<ImageView
|
||||||
android:layout_width="@dimen/png_height"
|
android:layout_width="@dimen/png_height"
|
||||||
android:layout_height="@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" />
|
app:tint="?attr/colorMainText" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<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_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
@@ -12,5 +13,5 @@
|
|||||||
android:layout_width="40dp"
|
android:layout_width="40dp"
|
||||||
android:layout_height="40dp"
|
android:layout_height="40dp"
|
||||||
android:padding="10dp"
|
android:padding="10dp"
|
||||||
android:src="@drawable/ic_stat_name" />
|
app:srcCompat="@drawable/ic_stat_name" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@@ -2,10 +2,20 @@
|
|||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
<item
|
<item
|
||||||
android:id="@+id/add_file"
|
|
||||||
android:icon="@drawable/ic_add_24dp"
|
android:icon="@drawable/ic_add_24dp"
|
||||||
android:title="@string/menu_item_add_file"
|
android:title="@string/menu_item_add_asset"
|
||||||
app:showAsAction="ifRoom" />
|
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
|
<item
|
||||||
android:id="@+id/download_file"
|
android:id="@+id/download_file"
|
||||||
android:icon="@drawable/ic_cloud_download_24dp"
|
android:icon="@drawable/ic_cloud_download_24dp"
|
||||||
|
|||||||
@@ -32,6 +32,10 @@
|
|||||||
android:id="@+id/feedback"
|
android:id="@+id/feedback"
|
||||||
android:icon="@drawable/ic_feedback_24dp"
|
android:icon="@drawable/ic_feedback_24dp"
|
||||||
android:title="@string/title_pref_feedback" />
|
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 -->
|
<!-- place holder for version text at the bottom -->
|
||||||
<item
|
<item
|
||||||
android:id="@+id/placeholder"
|
android:id="@+id/placeholder"
|
||||||
|
|||||||
@@ -34,6 +34,10 @@
|
|||||||
android:id="@+id/import_manually_trojan"
|
android:id="@+id/import_manually_trojan"
|
||||||
android:title="@string/menu_item_import_config_manually_trojan"
|
android:title="@string/menu_item_import_config_manually_trojan"
|
||||||
app:showAsAction="never" />
|
app:showAsAction="never" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/import_manually_wireguard"
|
||||||
|
android:title="@string/menu_item_import_config_manually_wireguard"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:title="@string/menu_item_import_config_custom"
|
android:title="@string/menu_item_import_config_custom"
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name" translatable="false">v2rayNG</string>
|
|
||||||
<string name="app_widget_name">التبديل</string>
|
<string name="app_widget_name">التبديل</string>
|
||||||
<string name="app_tile_name">التبديل</string>
|
<string name="app_tile_name">التبديل</string>
|
||||||
<string name="app_tile_first_use">أول استخدام لهذه الميزة، يرجى استخدام التطبيق لإضافة خادم</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_ss">الكتابة يدويا [Shadowsocks]</string>
|
||||||
<string name="menu_item_import_config_manually_socks">الكتابة يدويا [Socks]</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_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">تكوين مخصص</string>
|
||||||
<string name="menu_item_import_config_custom_clipboard">استيراد التكوين المخصص من الحافظة</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_local">استيراد التكوين المخصص من ملف محلي</string>
|
||||||
@@ -47,8 +47,6 @@
|
|||||||
<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</string>
|
||||||
<string name="server_lab_path">المسار (ws path/h2 path)/QUIC key/kcp seed/gRPC serviceName</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_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_allow_insecure">allowInsecure</string>
|
||||||
<string name="server_lab_sni">SNI</string>
|
<string name="server_lab_sni">SNI</string>
|
||||||
<string name="server_lab_address3">العنوان</string>
|
<string name="server_lab_address3">العنوان</string>
|
||||||
@@ -59,9 +57,9 @@
|
|||||||
<string name="server_lab_security4">المستخدم (اختياري)</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_flow">التدفق</string>
|
||||||
<string name="server_lab_public_key" translatable="false">مفتاح عام</string>
|
<string name="server_lab_reserved">Reserved (اختياري)</string>
|
||||||
<string name="server_lab_short_id" translatable="false">ShortId</string>
|
<string name="server_lab_local_address">العنوان المحلي IPv4(اختياري)</string>
|
||||||
<string name="server_lab_spider_x" translatable="false">SpiderX</string>
|
<string name="server_lab_local_mtu">Mtu(optional, default 1420)</string>
|
||||||
<string name="toast_success">نجاح</string>
|
<string name="toast_success">نجاح</string>
|
||||||
<string name="toast_failure">فشل</string>
|
<string name="toast_failure">فشل</string>
|
||||||
<string name="toast_none_data">لا يوجد شيء</string>
|
<string name="toast_none_data">لا يوجد شيء</string>
|
||||||
@@ -81,6 +79,9 @@
|
|||||||
<string name="menu_item_add_file">إضافة ملفات</string>
|
<string name="menu_item_add_file">إضافة ملفات</string>
|
||||||
<string name="menu_item_download_file">تحميل الملفات</string>
|
<string name="menu_item_download_file">تحميل الملفات</string>
|
||||||
<!-- PerAppProxyActivity -->
|
<!-- 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="msg_dialog_progress">جار التحميل</string>
|
||||||
<string name="menu_item_search">بحث</string>
|
<string name="menu_item_search">بحث</string>
|
||||||
<string name="menu_item_select_all">تحديد الكل</string>
|
<string name="menu_item_select_all">تحديد الكل</string>
|
||||||
@@ -97,7 +98,15 @@
|
|||||||
<string name="title_pref_per_app_proxy">وكيل لكل تطبيق</string>
|
<string name="title_pref_per_app_proxy">وكيل لكل تطبيق</string>
|
||||||
<string name="summary_pref_per_app_proxy">عام: التطبيق المحدد هو الوكيل، الاتصال غير المحدد مباشر؛ \nوضع التجاوز: التطبيق المحدد متصل مباشرة، الوكيل غير المحدد. \nالخيار لتحديد التطبيق الوكيل تلقائيا في القائمة</string>
|
<string name="summary_pref_per_app_proxy">عام: التطبيق المحدد هو الوكيل، الاتصال غير المحدد مباشر؛ \nوضع التجاوز: التطبيق المحدد متصل مباشرة، الوكيل غير المحدد. \nالخيار لتحديد التطبيق الوكيل تلقائيا في القائمة</string>
|
||||||
<string name="title_pref_mux_enabled">تمكين Mux</string>
|
<string name="title_pref_mux_enabled">تمكين Mux</string>
|
||||||
<string name="summary_pref_mux_enabled">التمكين قد يسرع الشبكة والتبديل بين الشبكات قد يكون فلاش</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">
|
||||||
|
<item>يرفض</item>
|
||||||
|
<item>مسموح</item>
|
||||||
|
<item>يتخطى</item>
|
||||||
|
</string-array>
|
||||||
<string name="title_pref_speed_enabled">تمكين عرض السرعة</string>
|
<string name="title_pref_speed_enabled">تمكين عرض السرعة</string>
|
||||||
<string name="summary_pref_speed_enabled">عرض السرعة الحالية في الإشعار.\nسيتغير رمز الإشعار استنادًا إلى الاستخدام.</string>
|
<string name="summary_pref_speed_enabled">عرض السرعة الحالية في الإشعار.\nسيتغير رمز الإشعار استنادًا إلى الاستخدام.</string>
|
||||||
<string name="title_pref_sniffing_enabled">تمكين Sniffing</string>
|
<string name="title_pref_sniffing_enabled">تمكين Sniffing</string>
|
||||||
@@ -136,8 +145,12 @@
|
|||||||
<string name="summary_pref_feedback">إرسال ملاحظات عن التحسينات أو الأخطاء إلى GitHub</string>
|
<string name="summary_pref_feedback">إرسال ملاحظات عن التحسينات أو الأخطاء إلى GitHub</string>
|
||||||
<string name="summary_pref_tg_group">الانضمام إلى مجموعة Telegram</string>
|
<string name="summary_pref_tg_group">الانضمام إلى مجموعة Telegram</string>
|
||||||
<string name="toast_tg_app_not_found">لم يتم العثور على تطبيق 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="title_pref_promotion">ترقية</string>
|
||||||
<string name="summary_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_core_loglevel">مستوى السجل</string>
|
||||||
<string name="title_mode">الوضع</string>
|
<string name="title_mode">الوضع</string>
|
||||||
<string name="title_mode_help">انقر علي للمزيد من المساعدة</string>
|
<string name="title_mode_help">انقر علي للمزيد من المساعدة</string>
|
||||||
@@ -147,19 +160,20 @@
|
|||||||
<string name="logcat_copy">نسخ</string>
|
<string name="logcat_copy">نسخ</string>
|
||||||
<string name="logcat_clear">مسح</string>
|
<string name="logcat_clear">مسح</string>
|
||||||
<string name="title_service_restart">إعادة تشغيل الخدمة</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_duplicate_config">حذف المكررات (2)</string>
|
||||||
<string name="title_del_invalid_config">حذف التكوين غير الصالح (اختبر أولا)</string>
|
<string name="title_del_invalid_config">حذف العناوين العاطلة (بعد الاختبار؛ 4)</string>
|
||||||
<string name="title_export_all">تصدير التكوينات غير المخصصة إلى الحافظة</string>
|
<string name="title_export_all">تصدير التكوينات غير المخصصة إلى الحافظة</string>
|
||||||
<string name="title_sub_setting">إعدادات مجموعة الاشتراك</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_url">URL اختياري</string>
|
||||||
<string name="sub_setting_enable">تمكين التحديث</string>
|
<string name="sub_setting_enable">تمكين التحديث</string>
|
||||||
<string name="title_sub_update">تحديث الاشتراك</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_ping_all_server">Tcping كل التكوين</string>
|
||||||
<string name="title_real_ping_all_server">تأخير الحقيقي لكل التكوين</string>
|
<string name="title_real_ping_all_server">اختبر كل العناوين (3)</string>
|
||||||
<string name="title_user_asset_setting">ملفات الأصول الجغرافية</string>
|
<string name="title_user_asset_setting">ملفات الأصول الجغرافية</string>
|
||||||
<string name="title_sort_by_test_results">الترتيب بناءً على نتائج الاختبار</string>
|
<string name="title_sort_by_test_results">ترتيب العناوين حسب نتائج الاختبار (5)</string>
|
||||||
<string name="title_filter_config">تصفية ملف التكوين</string>
|
<string name="title_filter_config">تصفية ملف التكوين</string>
|
||||||
<string name="filter_config_all">جميع مجموعات الاشتراك</string>
|
<string name="filter_config_all">جميع مجموعات الاشتراك</string>
|
||||||
<string name="title_del_duplicate_config_count">حذف %d من التكوينات المكررة</string>
|
<string name="title_del_duplicate_config_count">حذف %d من التكوينات المكررة</string>
|
||||||
@@ -192,9 +206,9 @@
|
|||||||
<item>تصدير إلى الحافظة</item>
|
<item>تصدير إلى الحافظة</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
<string-array name="routing_tag">
|
<string-array name="routing_tag">
|
||||||
<item>URL الوكيل أو IP</item>
|
<item>مسار وكيل أو IP</item>
|
||||||
<item>URL المباشر أو IP</item>
|
<item>مسار مباشر أو IP</item>
|
||||||
<item>URL المحظور أو IP</item>
|
<item>مسار محظور أو IP</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
<string-array name="routing_mode">
|
<string-array name="routing_mode">
|
||||||
<item>وكيل عالمي</item>
|
<item>وكيل عالمي</item>
|
||||||
@@ -207,4 +221,6 @@
|
|||||||
<item>VPN</item>
|
<item>VPN</item>
|
||||||
<item>الوكيل فقط</item>
|
<item>الوكيل فقط</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
<string name="menu_item_add_asset">يضيف</string>
|
||||||
|
<string name="menu_item_add_url">إضافة رابط</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name" translatable="false">v2rayNG</string>
|
|
||||||
<string name="app_widget_name">تعویض</string>
|
<string name="app_widget_name">تعویض</string>
|
||||||
<string name="app_tile_name">تعویض</string>
|
<string name="app_tile_name">تعویض</string>
|
||||||
<string name="app_tile_first_use">برای اولین بار از این ویژگی استفاده میکنید، لطفا از برنامه برای افزودن سرور استفاده کنید</string>
|
<string name="app_tile_first_use">برای اولین بار از این ویژگی استفاده میکنید، لطفا از برنامه برای افزودن سرور استفاده کنید</string>
|
||||||
<string name="navigation_drawer_open">باز کردن منو کشویی</string>
|
<string name="navigation_drawer_open">باز کردن منو کشویی</string>
|
||||||
<string name="navigation_drawer_close">بستن منو کشویی</string>
|
<string name="navigation_drawer_close">بستن منو کشویی</string>
|
||||||
<string name="migration_success">موفقیت در انتقال داده!</string>
|
<string name="migration_success">موفقیت در انتقال داده</string>
|
||||||
<string name="migration_fail">انتقال داده انجام نشد!</string>
|
<string name="migration_fail">انتقال داده انجام نشد!</string>
|
||||||
|
|
||||||
<!-- Notifications -->
|
<!-- Notifications -->
|
||||||
@@ -16,25 +15,26 @@
|
|||||||
<string name="toast_services_start">شروع خدمات</string>
|
<string name="toast_services_start">شروع خدمات</string>
|
||||||
<string name="toast_services_stop">توقف خدمات</string>
|
<string name="toast_services_stop">توقف خدمات</string>
|
||||||
<string name="toast_services_success">خدمات با موفقیت شروع شد</string>
|
<string name="toast_services_success">خدمات با موفقیت شروع شد</string>
|
||||||
<string name="toast_services_failure">شروع خدمات انجام نشد</string>
|
<string name="toast_services_failure">شروع خدمات انجام نشد!</string>
|
||||||
|
|
||||||
<!--ServerActivity-->
|
<!--ServerActivity-->
|
||||||
<string name="title_server">پرونده پیکربندی</string>
|
<string name="title_server">فایل کانفیگ</string>
|
||||||
<string name="menu_item_add_config">افزودن پیکربندی</string>
|
<string name="menu_item_add_config">افزودن کانفیگ</string>
|
||||||
<string name="menu_item_save_config">ذخیره پیکربندی</string>
|
<string name="menu_item_save_config">ذخیره کانفیگ</string>
|
||||||
<string name="menu_item_del_config">حذف پیکربندی</string>
|
<string name="menu_item_del_config">حذف کانفیگ</string>
|
||||||
<string name="menu_item_import_config_qrcode">پیکربندی را از QRcode وارد کنید</string>
|
<string name="menu_item_import_config_qrcode">کانفیگ را از QRcode وارد کنید</string>
|
||||||
<string name="menu_item_import_config_clipboard">پیکربندی را از کلیپبورد وارد کنید</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_vmess">تایپ دستی[Vmess]</string>
|
||||||
<string name="menu_item_import_config_manually_vless">تایپ دستی[VLESS]</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_ss">تایپ دستی[Shadowsocks]</string>
|
||||||
<string name="menu_item_import_config_manually_socks">تایپ دستی[Socks]</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_trojan">تایپ دستی[Trojan]</string>
|
||||||
<string name="menu_item_import_config_custom">پیکربندی سفارشی</string>
|
<string name="menu_item_import_config_manually_wireguard">[Wireguard]تایپ دستی</string>
|
||||||
<string name="menu_item_import_config_custom_clipboard">پیکربندی سفارشی را از کلیپبورد وارد کنید</string>
|
<string name="menu_item_import_config_custom">کانفیگ سفارشی</string>
|
||||||
<string name="menu_item_import_config_custom_local">پیکربندی سفارشی را به صورت محلی وارد کنید</string>
|
<string name="menu_item_import_config_custom_clipboard">کانفیگ سفارشی را از کلیپبورد وارد کنید</string>
|
||||||
<string name="menu_item_import_config_custom_url">پیکربندی سفارشی را از طریق نشانی اینترنتی وارد کنید</string>
|
<string name="menu_item_import_config_custom_local">کانفیگ سفارشی را به صورت محلی وارد کنید</string>
|
||||||
<string name="menu_item_import_config_custom_url_scan">نشانی اینترنتی اسکن پیکربندی سفارشی را وارد کنید</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="del_config_comfirm">حذف شود؟</string>
|
||||||
<string name="server_lab_remarks">ملاحظات</string>
|
<string name="server_lab_remarks">ملاحظات</string>
|
||||||
<string name="server_lab_address">نشانی</string>
|
<string name="server_lab_address">نشانی</string>
|
||||||
@@ -46,10 +46,10 @@
|
|||||||
<string name="server_lab_more_function">انتقال</string>
|
<string name="server_lab_more_function">انتقال</string>
|
||||||
<string name="server_lab_head_type">نوع head</string>
|
<string name="server_lab_head_type">نوع head</string>
|
||||||
<string name="server_lab_mode_type">حالت gRPC</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">میزبان درخواست (host/host ws/host h2)/امنیت QUIC</string>
|
||||||
<string name="server_lab_path">مسیر (مسیر ws/ مسیر h2) کلید QUIC/دانه kcp/نامخدمات gRPC</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_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_sni">SNI</string>
|
||||||
<string name="server_lab_address3">نشانی</string>
|
<string name="server_lab_address3">نشانی</string>
|
||||||
<string name="server_lab_port3">پورت</string>
|
<string name="server_lab_port3">پورت</string>
|
||||||
@@ -57,28 +57,34 @@
|
|||||||
<string name="server_lab_security3">امنیت</string>
|
<string name="server_lab_security3">امنیت</string>
|
||||||
<string name="server_lab_id4">رمز عبور (اختیاری)</string>
|
<string name="server_lab_id4">رمز عبور (اختیاری)</string>
|
||||||
<string name="server_lab_security4">نامکاربری (اختیاری)</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_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_success">موفقیت</string>
|
||||||
<string name="toast_failure">شکست</string>
|
<string name="toast_failure">شکست</string>
|
||||||
<string name="toast_none_data">چیزی نیست</string>
|
<string name="toast_none_data">چیزی نیست</string>
|
||||||
<string name="toast_incorrect_protocol">پروتکل نادرست</string>
|
<string name="toast_incorrect_protocol">پروتکل نادرست</string>
|
||||||
<string name="toast_decoding_failed">رمزگشایی انجام نشد</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="toast_require_file_manager">لطفا یک برنامه مدیریت فایل نصب کنید.</string>
|
||||||
<string name="server_customize_config">سفارشیسازی پیکربندی</string>
|
<string name="server_customize_config">سفارشیسازی کانفیگ</string>
|
||||||
<string name="toast_config_file_invalid">پیکربندی معتبر نیست</string>
|
<string name="toast_config_file_invalid">کانفیگ معتبر نیست</string>
|
||||||
<string name="server_lab_content">محتوا</string>
|
<string name="server_lab_content">محتوا</string>
|
||||||
<string name="toast_none_data_clipboard">هیچ دادهای در کلیپبورد وجود ندارد</string>
|
<string name="toast_none_data_clipboard">هیچ دادهای در کلیپبورد وجود ندارد</string>
|
||||||
<string name="toast_invalid_url">نشانی اینترنتی معتبر نیست</string>
|
<string name="toast_invalid_url">نشانی اینترنتی معتبر نیست</string>
|
||||||
<string name="server_lab_need_inbound">اطمینان حاصل کنید که پورت ورودی با تنظیمات مطابقت دارد</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="server_lab_request_host6">میزبان (SNI) (اختیاری)</string>
|
||||||
<string name="toast_asset_copy_failed">کپی پرونده انجام نشد، لطفا از مدیر پرونده استفاده کنید</string>
|
<string name="toast_asset_copy_failed">کپی فایل انجام نشد، لطفا از برنامه مدیریت فایل استفاده کنید</string>
|
||||||
<string name="menu_item_add_file">افزودن پروندهها</string>
|
<string name="menu_item_add_file">افزودن فایلها</string>
|
||||||
<string name="menu_item_download_file">دانلود پروندهها</string>
|
<string name="menu_item_download_file">دانلود فایلها</string>
|
||||||
|
|
||||||
<!-- PerAppProxyActivity -->
|
<!-- 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="msg_dialog_progress">بارگذاری</string>
|
||||||
<string name="menu_item_search">جستجو</string>
|
<string name="menu_item_search">جستجو</string>
|
||||||
<string name="menu_item_select_all">انتخاب همه</string>
|
<string name="menu_item_select_all">انتخاب همه</string>
|
||||||
@@ -94,11 +100,19 @@
|
|||||||
<string name="title_settings">تنظیمات</string>
|
<string name="title_settings">تنظیمات</string>
|
||||||
<string name="title_advanced">تنظیمات پیشرفته</string>
|
<string name="title_advanced">تنظیمات پیشرفته</string>
|
||||||
<string name="title_vpn_settings">تنظیمات VPN</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="summary_pref_per_app_proxy">عمومی: برنامه بررسی شده پروکسی است، اتصال مستقیم بدون بررسی است. \nحالت bypass: برنامه بررسی شده مستقیما متصل است، پراکسی بررسی نشده است. \nگزینهای برای انتخاب خودکار پروکسی برنامه در منو است</string>
|
||||||
|
|
||||||
<string name="title_pref_mux_enabled">فعال کردن Mux</string>
|
<string name="title_pref_mux_enabled">فعال کردن Mux</string>
|
||||||
<string name="summary_pref_mux_enabled">فعال کردن شاید سرعت بخشیدن به شبکه و تغییر شبکه شاید فلش، بهبود کند</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>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
<string name="title_pref_speed_enabled">فعال کردن نمایش سرعت</string>
|
<string name="title_pref_speed_enabled">فعال کردن نمایش سرعت</string>
|
||||||
<string name="summary_pref_speed_enabled">نمایش سرعت فعلی در قسمت آگاهسازی. \nآیکون آگاهسازی بر اساس استفاده تغییر میکند.</string>
|
<string name="summary_pref_speed_enabled">نمایش سرعت فعلی در قسمت آگاهسازی. \nآیکون آگاهسازی بر اساس استفاده تغییر میکند.</string>
|
||||||
@@ -144,8 +158,8 @@
|
|||||||
<string name="title_pref_local_dns_port">پورت DNS محلی</string>
|
<string name="title_pref_local_dns_port">پورت DNS محلی</string>
|
||||||
<string name="summary_pref_local_dns_port">پورت DNS محلی</string>
|
<string name="summary_pref_local_dns_port">پورت DNS محلی</string>
|
||||||
|
|
||||||
<string name="title_pref_confirm_remove">تایید حذف پرونده پیکربندی</string>
|
<string name="title_pref_confirm_remove">تایید حذف فایل کانفیگ</string>
|
||||||
<string name="summary_pref_confirm_remove">آیا برای حذف پرونده پیکربندی نیاز به تایید دوم توسط کاربر است</string>
|
<string name="summary_pref_confirm_remove">آیا برای حذف فایل کانفیگ نیاز به تایید دوم توسط کاربر است</string>
|
||||||
|
|
||||||
<string name="title_pref_start_scan_immediate">فورا اسکن را شروع کن</string>
|
<string name="title_pref_start_scan_immediate">فورا اسکن را شروع کن</string>
|
||||||
<string name="summary_pref_start_scan_immediate">دوربین را برای اسکن بلافاصله در هنگام راه اندازی باز کنید، در غیر این صورت می توانید کد را اسکن کنید یا عکسی را در نوار ابزار انتخاب کنید.</string>
|
<string name="summary_pref_start_scan_immediate">دوربین را برای اسکن بلافاصله در هنگام راه اندازی باز کنید، در غیر این صورت می توانید کد را اسکن کنید یا عکسی را در نوار ابزار انتخاب کنید.</string>
|
||||||
@@ -155,9 +169,13 @@
|
|||||||
<string name="summary_pref_tg_group">عضویت در گروه تلگرام</string>
|
<string name="summary_pref_tg_group">عضویت در گروه تلگرام</string>
|
||||||
<string name="toast_tg_app_not_found">برنامه تلگرام پیدا نشد</string>
|
<string name="toast_tg_app_not_found">برنامه تلگرام پیدا نشد</string>
|
||||||
|
|
||||||
|
<string name="title_privacy_policy">حریم خصوصی</string>
|
||||||
<string name="title_pref_promotion">تبلیغات</string>
|
<string name="title_pref_promotion">تبلیغات</string>
|
||||||
<string name="summary_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_core_loglevel">سطح گزارشات</string>
|
||||||
<string name="title_mode">حالت</string>
|
<string name="title_mode">حالت</string>
|
||||||
<string name="title_mode_help">برای راهنمایی بیشتر روی این متن، کلیک کنید</string>
|
<string name="title_mode_help">برای راهنمایی بیشتر روی این متن، کلیک کنید</string>
|
||||||
@@ -168,20 +186,21 @@
|
|||||||
<string name="logcat_copy">کپی</string>
|
<string name="logcat_copy">کپی</string>
|
||||||
<string name="logcat_clear">پاک کردن</string>
|
<string name="logcat_clear">پاک کردن</string>
|
||||||
<string name="title_service_restart">راهاندازی مجدد خدمات</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_duplicate_config">حذف کانفیگ های تکراری</string>
|
||||||
<string name="title_del_invalid_config">تنظیمات نامعتبر را حذف کنید (ابتدا آزمایش کنید)</string>
|
<string name="title_del_invalid_config">حذف کانفیگهای نامعتبر (ابتدا آزمایش کنید)</string>
|
||||||
<string name="title_export_all">خروجی گرفتن پیکربندیهای غیرسفارشی در کلیپبورد</string>
|
<string name="title_export_all">خروجی گرفتن کانفیگهای غیرسفارشی در کلیپبورد</string>
|
||||||
<string name="title_sub_setting">تنظیمات گروهی اشتراک</string>
|
<string name="title_sub_setting">تنظیمات گروهی اشتراک</string>
|
||||||
<string name="sub_setting_remarks">ملاحظات</string>
|
<string name="sub_setting_remarks">ملاحظات</string>
|
||||||
<string name="sub_setting_url">نشانی اینترنتی اختیاری</string>
|
<string name="sub_setting_url">نشانی اینترنتی اختیاری</string>
|
||||||
<string name="sub_setting_enable">فعال کردن بهروزرسانی</string>
|
<string name="sub_setting_enable">فعال کردن بهروزرسانی</string>
|
||||||
|
<string name="sub_auto_update">فعال سازی بهروزرسانی خودکار</string>
|
||||||
<string name="title_sub_update">بهروزرسانی اشتراک</string>
|
<string name="title_sub_update">بهروزرسانی اشتراک</string>
|
||||||
<string name="title_ping_all_server">Tcping همه پیکربندی</string>
|
<string name="title_ping_all_server">Tcping همه کانفیگ</string>
|
||||||
<string name="title_real_ping_all_server">تاخیر واقعی همه پیکربندی</string>
|
<string name="title_real_ping_all_server">تاخیر واقعی همه کانفیگ</string>
|
||||||
<string name="title_user_asset_setting">پروندههای دارایی جغرافیا</string>
|
<string name="title_user_asset_setting">فایلهای دارایی جغرافیا</string>
|
||||||
<string name="title_sort_by_test_results">مرتبسازی بر اساس نتایج آزمایش</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="filter_config_all">همه گروههای اشتراک</string>
|
||||||
<string name="title_del_duplicate_config_count">حذف %d کانفیگ تکراری</string>
|
<string name="title_del_duplicate_config_count">حذف %d کانفیگ تکراری</string>
|
||||||
|
|
||||||
@@ -189,12 +208,12 @@
|
|||||||
<string name="tasker_setting_confirm">تایید</string>
|
<string name="tasker_setting_confirm">تایید</string>
|
||||||
|
|
||||||
<string name="routing_settings_title">تنظیمات مسیریابی</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_save">ذخیره</string>
|
||||||
<string name="routing_settings_delete">پاک کردن</string>
|
<string name="routing_settings_delete">پاک کردن</string>
|
||||||
<string name="routing_settings_scan_replace">اسکن و جایگزین کنید</string>
|
<string name="routing_settings_scan_replace">اسکن و جایگزین کنید</string>
|
||||||
<string name="routing_settings_scan_append">اسکن و اضافه کنید</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_pending">اتصال را بررسی کنید</string>
|
||||||
<string name="connection_test_testing">در حال آزمایش...</string>
|
<string name="connection_test_testing">در حال آزمایش...</string>
|
||||||
@@ -205,10 +224,13 @@
|
|||||||
<string name="connection_connected">متصل است، برای بررسی اتصال ضربه بزنید</string>
|
<string name="connection_connected">متصل است، برای بررسی اتصال ضربه بزنید</string>
|
||||||
<string name="connection_not_connected">متصل نیست</string>
|
<string name="connection_not_connected">متصل نیست</string>
|
||||||
|
|
||||||
|
<string name="import_subscription_success">اشتراک با موفقیت ذخیره شد</string>
|
||||||
|
<string name="import_subscription_failure">ذخیره اشتراک ناموفق بود</string>
|
||||||
|
|
||||||
<string-array name="share_method">
|
<string-array name="share_method">
|
||||||
<item>QRcode</item>
|
<item>QRcode</item>
|
||||||
<item>خروجی گرفتن در کلیپبورد</item>
|
<item>خروجی گرفتن در کلیپبورد</item>
|
||||||
<item>خروجی گرفتن پیکربندی کامل در کلیپبورد</item>
|
<item>خروجی گرفتن کانفیگ کامل در کلیپبورد</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="share_sub_method">
|
<string-array name="share_sub_method">
|
||||||
@@ -234,7 +256,7 @@
|
|||||||
<item>VPN</item>
|
<item>VPN</item>
|
||||||
<item>فقط پروکسی</item>
|
<item>فقط پروکسی</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
<string name="import_subscription_success">اشتراک با موفقیت ذخیره شد</string>
|
<string name="menu_item_add_asset">افزودن</string>
|
||||||
<string name="import_subscription_failure">ذخیره اشتراک ناموفق بود</string>
|
<string name="menu_item_add_url">افزودن لینک</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -11,4 +11,5 @@
|
|||||||
<color name="colorAccent">#FFFFFF</color>
|
<color name="colorAccent">#FFFFFF</color>
|
||||||
<color name="colorBg">#000000</color>
|
<color name="colorBg">#000000</color>
|
||||||
<color name="colorText">#FFFFFF</color>
|
<color name="colorText">#FFFFFF</color>
|
||||||
|
<color name="colorMainTestBg">#222222</color>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name" translatable="false">v2rayNG</string>
|
|
||||||
<string name="app_widget_name">Переключить</string>
|
<string name="app_widget_name">Переключить</string>
|
||||||
<string name="app_tile_name">Переключить</string>
|
<string name="app_tile_name">Переключить</string>
|
||||||
<string name="app_tile_first_use">Первое использование этой функции, пожалуйста, используйте приложение, чтобы добавить сервер</string>
|
<string name="app_tile_first_use">Первое использование этой функции, пожалуйста, используйте приложение, чтобы добавить сервер</string>
|
||||||
@@ -23,16 +22,17 @@
|
|||||||
<string name="menu_item_add_config">Добавить профиль</string>
|
<string name="menu_item_add_config">Добавить профиль</string>
|
||||||
<string name="menu_item_save_config">Сохранить профиль</string>
|
<string name="menu_item_save_config">Сохранить профиль</string>
|
||||||
<string name="menu_item_del_config">Удалить профиль</string>
|
<string name="menu_item_del_config">Удалить профиль</string>
|
||||||
<string name="menu_item_import_config_qrcode">Импорт профиля из QR-кода</string>
|
<string name="menu_item_import_config_qrcode">Импорт из QR-кода</string>
|
||||||
<string name="menu_item_import_config_clipboard">Импорт профиля из буфера обмена</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_vmess">Ручной ввод Vmess</string>
|
||||||
<string name="menu_item_import_config_manually_vless">Ручной ввод профиля VLESS</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_ss">Ручной ввод Shadowsocks</string>
|
||||||
<string name="menu_item_import_config_manually_socks">Ручной ввод профиля Socks</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_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">Пользовательский профиль</string>
|
||||||
<string name="menu_item_import_config_custom_clipboard">Импорт из буфера обмена</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_local">Импорт из файла</string>
|
||||||
<string name="menu_item_import_config_custom_url">Импорт из URL</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_custom_url_scan">Импорт сканированием URL</string>
|
||||||
<string name="del_config_comfirm">Подтверждаете удаление?</string>
|
<string name="del_config_comfirm">Подтверждаете удаление?</string>
|
||||||
@@ -49,8 +49,6 @@
|
|||||||
<string name="server_lab_request_host">Запрос узла (WS/H2) / Шифрование QUIC</string>
|
<string name="server_lab_request_host">Запрос узла (WS/H2) / Шифрование QUIC</string>
|
||||||
<string name="server_lab_path">Путь (WS/H2) / Ключ QUIC / Сид KCP / Сервис gRPC</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_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_allow_insecure">Разрешать небезопасные</string>
|
||||||
<string name="server_lab_sni">SNI</string>
|
<string name="server_lab_sni">SNI</string>
|
||||||
<string name="server_lab_address3">Адрес</string>
|
<string name="server_lab_address3">Адрес</string>
|
||||||
@@ -61,9 +59,9 @@
|
|||||||
<string name="server_lab_security4">Пользователь (необязательно)</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_flow">Поток</string>
|
||||||
<string name="server_lab_public_key" translatable="false">PublicKey</string>
|
<string name="server_lab_reserved">Reserved (необязательно)</string>
|
||||||
<string name="server_lab_short_id" translatable="false">ShortId</string>
|
<string name="server_lab_local_address">Локальный адрес (необязательно, IPv4/IPv6 через запятую)</string>
|
||||||
<string name="server_lab_spider_x" translatable="false">SpiderX</string>
|
<string name="server_lab_local_mtu">MTU (необязательно, по умолчанию 1420)</string>
|
||||||
<string name="toast_success">Успешно</string>
|
<string name="toast_success">Успешно</string>
|
||||||
<string name="toast_failure">Ошибка</string>
|
<string name="toast_failure">Ошибка</string>
|
||||||
<string name="toast_none_data">Ничего нет</string>
|
<string name="toast_none_data">Ничего нет</string>
|
||||||
@@ -80,8 +78,13 @@
|
|||||||
<string name="toast_malformed_josn">Профиль повреждён</string>
|
<string name="toast_malformed_josn">Профиль повреждён</string>
|
||||||
<string name="server_lab_request_host6">Узел (SNI) (необязательно)</string>
|
<string name="server_lab_request_host6">Узел (SNI) (необязательно)</string>
|
||||||
<string name="toast_asset_copy_failed">Невозможно скопировать файл, используйте файловый менеджер</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_file">Добавить файлы</string>
|
||||||
|
<string name="menu_item_add_url">Добавить URL</string>
|
||||||
<string name="menu_item_download_file">Загрузить файлы</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 -->
|
<!-- PerAppProxyActivity -->
|
||||||
<string name="msg_dialog_progress">Загрузка…</string>
|
<string name="msg_dialog_progress">Загрузка…</string>
|
||||||
@@ -100,10 +103,19 @@
|
|||||||
<string name="title_advanced">Расширенные настройки</string>
|
<string name="title_advanced">Расширенные настройки</string>
|
||||||
<string name="title_vpn_settings">Настройки VPN</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\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="title_pref_mux_enabled">Использовать мультиплексирование</string>
|
||||||
<string name="summary_pref_mux_enabled">Включение может ускорить работу и переключение сети</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">
|
||||||
|
<item>отклонять</item>
|
||||||
|
<item>разрешать</item>
|
||||||
|
<item>пропускать</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
<string name="title_pref_speed_enabled">Отображение скорости</string>
|
<string name="title_pref_speed_enabled">Отображение скорости</string>
|
||||||
<string name="summary_pref_speed_enabled">Показывать текущую скорость в уведомлении.\nЗначок будет меняться в зависимости от использования.</string>
|
<string name="summary_pref_speed_enabled">Показывать текущую скорость в уведомлении.\nЗначок будет меняться в зависимости от использования.</string>
|
||||||
@@ -159,10 +171,15 @@
|
|||||||
<string name="summary_pref_feedback">Предложить улучшение или сообщить об ошибке на GitHub</string>
|
<string name="summary_pref_feedback">Предложить улучшение или сообщить об ошибке на GitHub</string>
|
||||||
<string name="summary_pref_tg_group">Присоединиться к группе в Telegram</string>
|
<string name="summary_pref_tg_group">Присоединиться к группе в Telegram</string>
|
||||||
<string name="toast_tg_app_not_found">Приложение 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="title_pref_promotion">Содействие</string>
|
||||||
<string name="summary_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_core_loglevel">Подробность ведения журнала</string>
|
||||||
<string name="title_mode">Режим</string>
|
<string name="title_mode">Режим</string>
|
||||||
<string name="title_mode_help">Нажмите для получения дополнительной информации</string>
|
<string name="title_mode_help">Нажмите для получения дополнительной информации</string>
|
||||||
@@ -175,12 +192,13 @@
|
|||||||
<string name="title_service_restart">Перезапуск службы</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_duplicate_config">Удалить дубликаты профилей</string>
|
||||||
<string name="title_del_invalid_config">Удалить сбойные профили (после проверки)</string>
|
<string name="title_del_invalid_config">Удалить нерабочие профили (после проверки)</string>
|
||||||
<string name="title_export_all">Экспорт всех профилей в буфер обмена</string>
|
<string name="title_export_all">Экспорт всех профилей в буфер обмена</string>
|
||||||
<string name="title_sub_setting">Группы</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_url">URL (необязательно)</string>
|
||||||
<string name="sub_setting_enable">использовать обновление</string>
|
<string name="sub_setting_enable">Использовать обновление</string>
|
||||||
|
<string name="sub_auto_update">Использовать автоматическое обновление</string>
|
||||||
<string name="title_sub_update">Обновить подписку</string>
|
<string name="title_sub_update">Обновить подписку</string>
|
||||||
<string name="title_ping_all_server">Проверка доступности профилей</string>
|
<string name="title_ping_all_server">Проверка доступности профилей</string>
|
||||||
<string name="title_real_ping_all_server">Время отклика профилей</string>
|
<string name="title_real_ping_all_server">Время отклика профилей</string>
|
||||||
@@ -199,7 +217,7 @@
|
|||||||
<string name="routing_settings_delete">Очистить</string>
|
<string name="routing_settings_delete">Очистить</string>
|
||||||
<string name="routing_settings_scan_replace">Сканировать и заменить</string>
|
<string name="routing_settings_scan_replace">Сканировать и заменить</string>
|
||||||
<string name="routing_settings_scan_append">Сканировать и добавить</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_pending">Проверить подключение</string>
|
||||||
<string name="connection_test_testing">Проверка…</string>
|
<string name="connection_test_testing">Проверка…</string>
|
||||||
@@ -210,6 +228,14 @@
|
|||||||
<string name="connection_connected">Подключено, нажмите для проверки</string>
|
<string name="connection_connected">Подключено, нажмите для проверки</string>
|
||||||
<string name="connection_not_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">Настройки фрагментирования</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">
|
<string-array name="share_method">
|
||||||
<item>QR-код</item>
|
<item>QR-код</item>
|
||||||
<item>Экспорт в буфер обмена</item>
|
<item>Экспорт в буфер обмена</item>
|
||||||
@@ -239,7 +265,5 @@
|
|||||||
<item>VPN</item>
|
<item>VPN</item>
|
||||||
<item>Только прокси</item>
|
<item>Только прокси</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
<string name="import_subscription_success">Подписка импортирована</string>
|
|
||||||
<string name="import_subscription_failure">Подписка не импортирована</string>
|
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -12,10 +12,10 @@
|
|||||||
<string name="notification_action_stop_v2ray">Ngắt kết nối v2rayNG</string>
|
<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="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="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_stop">Đã dừng v2rayNG!</string>
|
||||||
<string name="toast_services_success">Đã bắt đầu v2rayNG!</string>
|
<string name="toast_services_success">Đã khởi động 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_failure">Không thể khởi động v2rayNG, kiểm tra lại cấu hình.</string>
|
||||||
|
|
||||||
<!--ServerActivity-->
|
<!--ServerActivity-->
|
||||||
<string name="title_server">v2rayNG</string>
|
<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_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_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_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">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_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_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">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="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_remarks">Tên cấu hình</string>
|
||||||
<string name="server_lab_address">Địa chỉ</string>
|
<string name="server_lab_address">Địa chỉ</string>
|
||||||
<string name="server_lab_port">Cổng</string>
|
<string name="server_lab_port">Cổng</string>
|
||||||
<string name="server_lab_id">ID</string>
|
<string name="server_lab_id">ID</string>
|
||||||
<string name="server_lab_alterid">ID thay thế</string>
|
<string name="server_lab_alterid">alterId</string>
|
||||||
<string name="server_lab_security">Bảo mật</string>
|
<string name="server_lab_security">Thuật toán mã hóa</string>
|
||||||
<string name="server_lab_network">Kiểu kết nối</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_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_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_request_host">Yêu cầu host(host/ws host/h2 host)/QUIC security</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_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_stream_security">TLS</string>
|
||||||
<string name="server_lab_allow_insecure">Bỏ qua xác minh chứng chỉ</string>
|
<string name="server_lab_allow_insecure">allowInsecure</string>
|
||||||
<string name="server_lab_sni">Địa chỉ SNI</string>
|
<string name="server_lab_sni">SNI</string>
|
||||||
<string name="server_lab_address3">Địa chỉ</string>
|
<string name="server_lab_address3">Địa chỉ</string>
|
||||||
<string name="server_lab_port3">Cổng</string>
|
<string name="server_lab_port3">Cổng</string>
|
||||||
<string name="server_lab_id3">Mật khẩu</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_security3">Thuật toán mã hóa</string>
|
||||||
<string name="server_lab_id4">Mật khẩu (Bổ sung)</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 (Bổ sung)</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ã hoá</string>
|
<string name="server_lab_encryption">Mã hóa</string>
|
||||||
<string name="server_lab_flow">Kiểm soát lưu lượng</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_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_failure">Đã xảy ra lỗi, vui lòng thử lại!</string>
|
||||||
<string name="toast_none_data">Không có gì ở đây</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="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="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="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="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_add_file">Thêm tệp</string>
|
||||||
<string name="menu_item_download_file">Tải xuống tệp tin</string>
|
<string name="menu_item_download_file">Tải xuống tệp tin</string>
|
||||||
|
|
||||||
<!-- PerAppProxyActivity -->
|
<!-- 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="msg_dialog_progress">Đang tải...</string>
|
||||||
<string name="menu_item_search">Tìm kiếm</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="menu_item_select_all">Chọn tất cả</string>
|
||||||
<string name="msg_enter_keywords">Nhập từ khoá</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="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="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>
|
<string name="menu_item_export_proxy_app">Xuất và sao chép</string>
|
||||||
@@ -93,46 +100,56 @@
|
|||||||
<string name="title_settings">Cài đặt</string>
|
<string name="title_settings">Cài đặt</string>
|
||||||
<string name="title_advanced">Cài đặt nâng cao</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_vpn_settings">Cài đặt VPN</string>
|
||||||
<string name="title_pref_per_app_proxy">Proxy ứng dụng</string>
|
<string name="title_pref_per_app_proxy">Proxy Theo Ứ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="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="title_mux_settings">Mux Settings</string>
|
||||||
<string name="summary_pref_mux_enabled">Bật lên có thể làm tăng tốc độ mạng và chuyển mạng nhanh hơn.</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>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_sniffing_enabled">Bật tính năng phát hiện luồng</string>
|
<string name="title_pref_speed_enabled">Bật Hiển thị tốc độ mạ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="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_local_dns_enabled">Cho phép DNS cục bộ</string>
|
<string name="title_pref_sniffing_enabled">Bật Sniffing</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="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_fake_dns_enabled">Cho phép DNS giả</string>
|
<string name="title_pref_local_dns_enabled">Bật Local DNS</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="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">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="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">Định tuyến</string>
|
||||||
<string name="title_pref_routing_domain_strategy">Tùy chọn tên miề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">Tùy chỉnh quy tắc lộ trình</string>
|
<string name="title_pref_routing_mode">Quy tắc được định nghĩa trước</string>
|
||||||
<string name="title_pref_routing_custom">Tùy chỉnh lộ trình</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="summary_pref_remote_dns">DNS</string>
|
||||||
|
|
||||||
<string name="title_pref_vpn_dns">VPN DNS (Chỉ IPv4/IPv6)</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="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="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="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">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="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="title_pref_allow_insecure">allowInsecure</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="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="title_pref_socks_port">Cổng Proxy SOCKS5</string>
|
||||||
<string name="summary_pref_socks_port">Cổng Proxy SOCKS5</string>
|
<string name="summary_pref_socks_port">Cổng Proxy SOCKS5</string>
|
||||||
@@ -140,13 +157,13 @@
|
|||||||
<string name="title_pref_http_port">Cổng Proxy HTTP</string>
|
<string name="title_pref_http_port">Cổng Proxy HTTP</string>
|
||||||
<string name="summary_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="title_pref_local_dns_port">Cổng Local DNS</string>
|
||||||
<string name="summary_pref_local_dns_port">Cổng DNS cục bộ</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="title_pref_confirm_remove">Xác nhận xóa tệp 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="summary_pref_confirm_remove">Yêu cầu xác nhận từ người dùng khi thực hiện xóa tệp cấu hình.</string>
|
||||||
|
|
||||||
<string name="title_pref_start_scan_immediate">Bắt đầu quét mã QR ngay lập tức</string>
|
<string name="title_pref_start_scan_immediate">Quét mã QR ngay lập tức</string>
|
||||||
<string name="summary_pref_start_scan_immediate">Mở camera để quét QR ngay khi khởi động, nếu không bạn có thể chọn quét mã hoặc chọn ảnh trên thanh công cụ.</string>
|
<string name="summary_pref_start_scan_immediate">Mở camera để quét QR ngay khi khởi động, nếu không bạn có thể chọn quét mã hoặc chọn ảnh trên thanh công cụ.</string>
|
||||||
|
|
||||||
<string name="title_pref_feedback">Phản hồi lỗi</string>
|
<string name="title_pref_feedback">Phản hồi lỗi</string>
|
||||||
@@ -154,81 +171,87 @@
|
|||||||
<string name="summary_pref_tg_group">Tham gia nhóm Telegram</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="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="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">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_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_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="title_logcat">Logcat</string>
|
||||||
<string name="logcat_copy">Sao chép nhật ký</string>
|
<string name="logcat_copy">Sao chép</string>
|
||||||
<string name="logcat_clear">Xoá nhật ký</string>
|
<string name="logcat_clear">Xoá</string>
|
||||||
<string name="title_service_restart">Kết nối lại v2rayNG</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_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_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_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="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_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_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_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_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_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_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="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="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="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="tasker_setting_confirm">Xác nhận</string>
|
||||||
|
|
||||||
<string name="routing_settings_title">Cài đặt lộ trình</string>
|
<string name="routing_settings_title">Cài đặt định tuyến</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_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_save">Lưu lại</string>
|
||||||
<string name="routing_settings_delete">Xoá</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_replace">Quét QR và thay thế</string>
|
||||||
<string name="routing_settings_scan_append">Quét và nối</string>
|
<string name="routing_settings_scan_append">Quét QR và nối thêm</string>
|
||||||
<string name="routing_settings_default_rules">Đặt luật cho lộ trình mặc định</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_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_testing">Đang kiểm tra kết nối mạng...</string>
|
||||||
<string name="connection_test_available">Kiểm tra kết nối mạng thành công! Ping hiện tại là %d</string>
|
<string name="connection_test_available">Test thành công: truy cập www.Google.com mất %d ms</string>
|
||||||
<string name="connection_test_error">Lỗi kết nối mạng, hãy thử đổi cấu hình hoặc kiểm tra lại! Mã lỗi: %s</string>
|
<string name="connection_test_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_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_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_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, hãy chọn một cấu hình để kết nối!</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_success">Nhập gói đăng ký 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_failure">Nhập gói đăng ký không thành công!</string>
|
||||||
|
|
||||||
<string-array name="share_method">
|
<string-array name="share_method">
|
||||||
<item>Xuất ra mã QR (Chụp màn hình để lưu)</item>
|
<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>
|
<item>Sao chép thành cấu hình tùy chỉnh</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="share_sub_method">
|
<string-array name="share_sub_method">
|
||||||
<item>Xuất ra mã QR (Chụp màn hình để lưu)</item>
|
<item>Xuất gói 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 đăng ký vào bảng nhớ tạm</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="routing_tag">
|
<string-array name="routing_tag">
|
||||||
<item>Proxy URL hoặc IP</item>
|
<item>Proxy</item>
|
||||||
<item>Direct URL hoặc IP</item>
|
<item>Direct</item>
|
||||||
<item>URL đã chặn hoặc IP</item>
|
<item>Blocked</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="routing_mode">
|
<string-array name="routing_mode">
|
||||||
<item>Proxy toàn cầu</item>
|
<item>Proxy toàn cầu</item>
|
||||||
<item>Bỏ qua địa chỉ LAN rồi Proxy</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 địa chỉ nội địa rồi Proxy</item>
|
||||||
<item>Bỏ qua LAN và địa chỉ đất liền 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>
|
<item>Kết nối trực tiếp toàn cầu</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
@@ -236,5 +259,7 @@
|
|||||||
<item>Chế độ VPN</item>
|
<item>Chế độ VPN</item>
|
||||||
<item>Chế độ Proxy</item>
|
<item>Chế độ Proxy</item>
|
||||||
</string-array>
|
</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>
|
</resources>
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
<string name="menu_item_import_config_manually_ss">手动输入[Shadowsocks]</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_socks">手动输入[Socks]</string>
|
||||||
<string name="menu_item_import_config_manually_trojan">手动输入[Trojan]</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">自定义配置</string>
|
||||||
<string name="menu_item_import_config_custom_clipboard">从剪贴板导入自定义配置</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_local">从本地导入自定义配置</string>
|
||||||
@@ -58,6 +59,9 @@
|
|||||||
<string name="server_lab_security4">用户名(可选)</string>
|
<string name="server_lab_security4">用户名(可选)</string>
|
||||||
<string name="server_lab_encryption">加密方式(encryption)</string>
|
<string name="server_lab_encryption">加密方式(encryption)</string>
|
||||||
<string name="server_lab_flow">流控(flow)</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_success">成功</string>
|
||||||
<string name="toast_failure">失败</string>
|
<string name="toast_failure">失败</string>
|
||||||
<string name="toast_none_data">没有数据</string>
|
<string name="toast_none_data">没有数据</string>
|
||||||
@@ -79,6 +83,9 @@
|
|||||||
|
|
||||||
|
|
||||||
<!-- PerAppProxyActivity -->
|
<!-- 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="msg_dialog_progress">正在加载</string>
|
||||||
<string name="menu_item_search">搜索</string>
|
<string name="menu_item_search">搜索</string>
|
||||||
<string name="menu_item_select_all">全选</string>
|
<string name="menu_item_select_all">全选</string>
|
||||||
@@ -96,8 +103,17 @@
|
|||||||
<string name="title_pref_per_app_proxy">分应用代理</string>
|
<string name="title_pref_per_app_proxy">分应用代理</string>
|
||||||
<string name="summary_pref_per_app_proxy">常规:勾选的App被代理,未勾选的直连;\n绕行模式:勾选的App直连,未勾选的被代理.\n不明白者在菜单中选择自动选中需代理应用</string>
|
<string name="summary_pref_per_app_proxy">常规:勾选的App被代理,未勾选的直连;\n绕行模式:勾选的App直连,未勾选的被代理.\n不明白者在菜单中选择自动选中需代理应用</string>
|
||||||
|
|
||||||
<string name="title_pref_mux_enabled">启用Mux多路复用</string>
|
<string name="title_mux_settings">Mux 多路复用 设置</string>
|
||||||
<string name="summary_pref_mux_enabled">开启可能会加速,关闭可能会减少断流</string>
|
<string name="title_pref_mux_enabled">启用 Mux 多路复用</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">
|
||||||
|
<item>不代理</item>
|
||||||
|
<item>多路复用</item>
|
||||||
|
<item>原生</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
<string name="title_pref_speed_enabled">启用速度显示</string>
|
<string name="title_pref_speed_enabled">启用速度显示</string>
|
||||||
<string name="summary_pref_speed_enabled">在通知中显示当前速度\n小图标显示流量的路由情况</string>
|
<string name="summary_pref_speed_enabled">在通知中显示当前速度\n小图标显示流量的路由情况</string>
|
||||||
@@ -153,10 +169,15 @@
|
|||||||
<string name="summary_pref_feedback">反馈改进或漏洞至 GitHub</string>
|
<string name="summary_pref_feedback">反馈改进或漏洞至 GitHub</string>
|
||||||
<string name="summary_pref_tg_group">加入Telegram Group</string>
|
<string name="summary_pref_tg_group">加入Telegram Group</string>
|
||||||
<string name="toast_tg_app_not_found">未找到Telegram app</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="title_pref_promotion">推广</string>
|
||||||
<string name="summary_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_core_loglevel">日志级别</string>
|
||||||
<string name="title_mode">模式</string>
|
<string name="title_mode">模式</string>
|
||||||
<string name="title_mode_help">点此查看更多帮助</string>
|
<string name="title_mode_help">点此查看更多帮助</string>
|
||||||
@@ -175,6 +196,7 @@
|
|||||||
<string name="sub_setting_remarks">备注</string>
|
<string name="sub_setting_remarks">备注</string>
|
||||||
<string name="sub_setting_url">可选地址(url)</string>
|
<string name="sub_setting_url">可选地址(url)</string>
|
||||||
<string name="sub_setting_enable">启用更新</string>
|
<string name="sub_setting_enable">启用更新</string>
|
||||||
|
<string name="sub_auto_update">启用自动更新</string>
|
||||||
<string name="title_sub_update">更新订阅</string>
|
<string name="title_sub_update">更新订阅</string>
|
||||||
<string name="title_ping_all_server">测试全部配置Tcping</string>
|
<string name="title_ping_all_server">测试全部配置Tcping</string>
|
||||||
<string name="title_real_ping_all_server">测试全部配置真连接</string>
|
<string name="title_real_ping_all_server">测试全部配置真连接</string>
|
||||||
@@ -204,6 +226,16 @@
|
|||||||
<string name="connection_connected">"已连接,点击测试连接"</string>
|
<string name="connection_connected">"已连接,点击测试连接"</string>
|
||||||
<string name="connection_not_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">
|
<string-array name="share_method">
|
||||||
<item>二维码</item>
|
<item>二维码</item>
|
||||||
<item>导出至剪贴板</item>
|
<item>导出至剪贴板</item>
|
||||||
@@ -234,7 +266,5 @@
|
|||||||
<item>VPN</item>
|
<item>VPN</item>
|
||||||
<item>仅代理</item>
|
<item>仅代理</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
<string name="import_subscription_success">订阅导入成功</string>
|
|
||||||
<string name="import_subscription_failure">导入订阅失败</string>
|
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
<string name="menu_item_import_config_manually_ss">手動鍵入 [Shadowsocks]</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_socks">手動鍵入 [Socks]</string>
|
||||||
<string name="menu_item_import_config_manually_trojan">手動鍵入 [Trojan]</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">自訂組態</string>
|
||||||
<string name="menu_item_import_config_custom_clipboard">從剪貼簿匯入自訂組態</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_local">從本地匯入自訂組態</string>
|
||||||
@@ -58,6 +59,9 @@
|
|||||||
<string name="server_lab_security4">使用者名稱 (可選)</string>
|
<string name="server_lab_security4">使用者名稱 (可選)</string>
|
||||||
<string name="server_lab_encryption">加密 (encryption)</string>
|
<string name="server_lab_encryption">加密 (encryption)</string>
|
||||||
<string name="server_lab_flow">流程 (flow)</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_success">成功</string>
|
||||||
<string name="toast_failure">失敗</string>
|
<string name="toast_failure">失敗</string>
|
||||||
<string name="toast_none_data">無資料</string>
|
<string name="toast_none_data">無資料</string>
|
||||||
@@ -78,6 +82,9 @@
|
|||||||
<string name="menu_item_download_file">下載檔案</string>
|
<string name="menu_item_download_file">下載檔案</string>
|
||||||
|
|
||||||
<!-- PerAppProxyActivity -->
|
<!-- 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="msg_dialog_progress">載入</string>
|
||||||
<string name="menu_item_search">搜尋</string>
|
<string name="menu_item_search">搜尋</string>
|
||||||
<string name="menu_item_select_all">全選</string>
|
<string name="menu_item_select_all">全選</string>
|
||||||
@@ -96,8 +103,17 @@
|
|||||||
<string name="title_pref_per_app_proxy">Proxy 個別應用程式</string>
|
<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="summary_pref_per_app_proxy">常規:勾選的 App 啟用 Proxy,未勾選的直接連線;\n繞行模式:勾選的 App 直接連線,未勾選的啟用 Proxy。\n可在選單中選擇自動選中需 Proxy 應用</string>
|
||||||
|
|
||||||
<string name="title_pref_mux_enabled">啟用 Mux</string>
|
<string name="title_mux_settings">Mux 設定</string>
|
||||||
<string name="summary_pref_mux_enabled">啟用或許會加快網路速度,切換或許會閃爍</string>
|
<string name="title_pref_mux_enabled">啟用 Mux 多路復用</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">
|
||||||
|
<item>不代理</item>
|
||||||
|
<item>多路復用</item>
|
||||||
|
<item>原生</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
<string name="title_pref_speed_enabled">啟用速度顯示</string>
|
<string name="title_pref_speed_enabled">啟用速度顯示</string>
|
||||||
<string name="summary_pref_speed_enabled">在通知中顯示當前速度\n小圖示顯示流量的轉送狀況</string>
|
<string name="summary_pref_speed_enabled">在通知中顯示當前速度\n小圖示顯示流量的轉送狀況</string>
|
||||||
@@ -153,10 +169,15 @@
|
|||||||
<string name="summary_pref_feedback">前往 GitHub 回報錯誤</string>
|
<string name="summary_pref_feedback">前往 GitHub 回報錯誤</string>
|
||||||
<string name="summary_pref_tg_group">加入 Telegram 群組</string>
|
<string name="summary_pref_tg_group">加入 Telegram 群組</string>
|
||||||
<string name="toast_tg_app_not_found">未找到 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="title_pref_promotion">推廣</string>
|
||||||
<string name="summary_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_core_loglevel">記錄層級</string>
|
||||||
<string name="title_mode">模式</string>
|
<string name="title_mode">模式</string>
|
||||||
<string name="title_mode_help">輕觸以檢視說明</string>
|
<string name="title_mode_help">輕觸以檢視說明</string>
|
||||||
@@ -175,6 +196,7 @@
|
|||||||
<string name="sub_setting_remarks">備註</string>
|
<string name="sub_setting_remarks">備註</string>
|
||||||
<string name="sub_setting_url">Optional URL</string>
|
<string name="sub_setting_url">Optional URL</string>
|
||||||
<string name="sub_setting_enable">啟用更新</string>
|
<string name="sub_setting_enable">啟用更新</string>
|
||||||
|
<string name="sub_auto_update">啟用自動更新</string>
|
||||||
<string name="title_sub_update">更新訂閱</string>
|
<string name="title_sub_update">更新訂閱</string>
|
||||||
<string name="title_ping_all_server">偵測所有組態 Tcping</string>
|
<string name="title_ping_all_server">偵測所有組態 Tcping</string>
|
||||||
<string name="title_real_ping_all_server">偵測所有組態真延遲</string>
|
<string name="title_real_ping_all_server">偵測所有組態真延遲</string>
|
||||||
@@ -204,6 +226,16 @@
|
|||||||
<string name="connection_connected">"已連線,輕觸以檢查連線能力"</string>
|
<string name="connection_connected">"已連線,輕觸以檢查連線能力"</string>
|
||||||
<string name="connection_not_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">
|
<string-array name="share_method">
|
||||||
<item>QR Code</item>
|
<item>QR Code</item>
|
||||||
<item>匯出至剪貼簿</item>
|
<item>匯出至剪貼簿</item>
|
||||||
|
|||||||
@@ -60,6 +60,10 @@
|
|||||||
<item>reality</item>
|
<item>reality</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="fragment_packets" translatable="false">
|
||||||
|
<item>tlshello</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
<string-array name="streamsecurity_utls" translatable="false">
|
<string-array name="streamsecurity_utls" translatable="false">
|
||||||
<item></item>
|
<item></item>
|
||||||
<item>chrome</item>
|
<item>chrome</item>
|
||||||
@@ -76,8 +80,11 @@
|
|||||||
|
|
||||||
<string-array name="streamsecurity_alpn" translatable="false">
|
<string-array name="streamsecurity_alpn" translatable="false">
|
||||||
<item></item>
|
<item></item>
|
||||||
|
<item>h3</item>
|
||||||
<item>h2</item>
|
<item>h2</item>
|
||||||
<item>http/1.1</item>
|
<item>http/1.1</item>
|
||||||
|
<item>h3,h2,http/1.1</item>
|
||||||
|
<item>h3,h2</item>
|
||||||
<item>h2,http/1.1</item>
|
<item>h2,http/1.1</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
@@ -179,4 +186,10 @@
|
|||||||
<item>fa</item>
|
<item>fa</item>
|
||||||
<item>ar</item>
|
<item>ar</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="mux_xudp_quic_value" translatable="false">
|
||||||
|
<item>reject</item>
|
||||||
|
<item>allow</item>
|
||||||
|
<item>skip</item>
|
||||||
|
</string-array>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -15,4 +15,5 @@
|
|||||||
<color name="colorAccent">#000000</color>
|
<color name="colorAccent">#000000</color>
|
||||||
<color name="colorBg">#FFFFFF</color>
|
<color name="colorBg">#FFFFFF</color>
|
||||||
<color name="colorText">#000000</color>
|
<color name="colorText">#000000</color>
|
||||||
|
<color name="colorMainTestBg">#F0F1F6</color>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
<string name="menu_item_import_config_manually_ss">Type manually[Shadowsocks]</string>
|
<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_socks">Type manually[Socks]</string>
|
||||||
<string name="menu_item_import_config_manually_trojan">Type manually[Trojan]</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">Custom config</string>
|
||||||
<string name="menu_item_import_config_custom_clipboard">Import custom config from Clipboard</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>
|
<string name="menu_item_import_config_custom_local">Import custom config from locally</string>
|
||||||
@@ -64,6 +65,10 @@
|
|||||||
<string name="server_lab_public_key" translatable="false">PublicKey</string>
|
<string name="server_lab_public_key" translatable="false">PublicKey</string>
|
||||||
<string name="server_lab_short_id" translatable="false">ShortId</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_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_success">Success</string>
|
||||||
<string name="toast_failure">Failure</string>
|
<string name="toast_failure">Failure</string>
|
||||||
<string name="toast_none_data">There is nothing</string>
|
<string name="toast_none_data">There is nothing</string>
|
||||||
@@ -80,8 +85,15 @@
|
|||||||
<string name="toast_malformed_josn">Config malformed</string>
|
<string name="toast_malformed_josn">Config malformed</string>
|
||||||
<string name="server_lab_request_host6">Host(SNI)(Optional)</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="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_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="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 -->
|
<!-- PerAppProxyActivity -->
|
||||||
<string name="msg_dialog_progress">Loading</string>
|
<string name="msg_dialog_progress">Loading</string>
|
||||||
@@ -102,8 +114,17 @@
|
|||||||
<string name="title_pref_per_app_proxy">Per-app proxy</string>
|
<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="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="title_pref_mux_enabled">Enable Mux</string>
|
||||||
<string name="summary_pref_mux_enabled">Enable maybe speed up network and switch network maybe flash</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">
|
||||||
|
<item>reject</item>
|
||||||
|
<item>allow</item>
|
||||||
|
<item>skip</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
<string name="title_pref_speed_enabled">Enable speed display</string>
|
<string name="title_pref_speed_enabled">Enable speed display</string>
|
||||||
<string name="summary_pref_speed_enabled">Display current speed in the notification.\nNotification icon would change based on
|
<string name="summary_pref_speed_enabled">Display current speed in the notification.\nNotification icon would change based on
|
||||||
@@ -161,10 +182,15 @@
|
|||||||
<string name="summary_pref_feedback">Feedback enhancements or bugs to GitHub</string>
|
<string name="summary_pref_feedback">Feedback enhancements or bugs to GitHub</string>
|
||||||
<string name="summary_pref_tg_group">Join Telegram Group</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="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="title_pref_promotion">Promotion</string>
|
||||||
<string name="summary_pref_promotion">Promotion,click for details(Donation can be removed)</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">Auto Update Interval (Minutes, Min value 15)</string>
|
||||||
|
|
||||||
<string name="title_core_loglevel">Log Level</string>
|
<string name="title_core_loglevel">Log Level</string>
|
||||||
<string name="title_mode">Mode</string>
|
<string name="title_mode">Mode</string>
|
||||||
<string name="title_mode_help">Click me for more help</string>
|
<string name="title_mode_help">Click me for more help</string>
|
||||||
@@ -182,7 +208,8 @@
|
|||||||
<string name="title_sub_setting">Subscription group setting</string>
|
<string name="title_sub_setting">Subscription group setting</string>
|
||||||
<string name="sub_setting_remarks">remarks</string>
|
<string name="sub_setting_remarks">remarks</string>
|
||||||
<string name="sub_setting_url">Optional URL</string>
|
<string name="sub_setting_url">Optional URL</string>
|
||||||
<string name="sub_setting_enable">enable update</string>
|
<string name="sub_setting_enable">Enable update</string>
|
||||||
|
<string name="sub_auto_update">Enable automatic update</string>
|
||||||
<string name="title_sub_update">Update subscription</string>
|
<string name="title_sub_update">Update subscription</string>
|
||||||
<string name="title_ping_all_server">Tcping all configuration</string>
|
<string name="title_ping_all_server">Tcping all configuration</string>
|
||||||
<string name="title_real_ping_all_server">Real delay all configuration</string>
|
<string name="title_real_ping_all_server">Real delay all configuration</string>
|
||||||
@@ -214,6 +241,11 @@
|
|||||||
|
|
||||||
<string name="import_subscription_success">Subscription imported Successfully</string>
|
<string name="import_subscription_success">Subscription imported Successfully</string>
|
||||||
<string name="import_subscription_failure">Import subscription failed</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">
|
<string-array name="share_method">
|
||||||
<item>QRcode</item>
|
<item>QRcode</item>
|
||||||
|
|||||||
@@ -1,15 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<CheckBoxPreference
|
|
||||||
android:key="pref_speed_enabled"
|
|
||||||
android:summary="@string/summary_pref_speed_enabled"
|
|
||||||
android:title="@string/title_pref_speed_enabled" />
|
|
||||||
|
|
||||||
<CheckBoxPreference
|
<CheckBoxPreference
|
||||||
android:defaultValue="true"
|
android:defaultValue="true"
|
||||||
android:key="pref_sniffing_enabled"
|
android:key="pref_sniffing_enabled"
|
||||||
android:summary="@string/summary_pref_sniffing_enabled"
|
android:summary="@string/summary_pref_sniffing_enabled"
|
||||||
android:title="@string/title_pref_sniffing_enabled" />
|
android:title="@string/title_pref_sniffing_enabled" />
|
||||||
|
|
||||||
<PreferenceCategory android:title="@string/title_vpn_settings">
|
<PreferenceCategory android:title="@string/title_vpn_settings">
|
||||||
<CheckBoxPreference
|
<CheckBoxPreference
|
||||||
@@ -17,30 +13,26 @@
|
|||||||
android:summary="@string/summary_pref_per_app_proxy"
|
android:summary="@string/summary_pref_per_app_proxy"
|
||||||
android:title="@string/title_pref_per_app_proxy" />
|
android:title="@string/title_pref_per_app_proxy" />
|
||||||
|
|
||||||
<!--<CheckBoxPreference-->
|
|
||||||
<!--android:key="pref_mux_enabled"-->
|
|
||||||
<!--android:summary="@string/summary_pref_mux_enabled"-->
|
|
||||||
<!--android:title="@string/title_pref_mux_enabled" />-->
|
|
||||||
<CheckBoxPreference
|
<CheckBoxPreference
|
||||||
android:key="pref_local_dns_enabled"
|
android:key="pref_local_dns_enabled"
|
||||||
android:summary="@string/summary_pref_local_dns_enabled"
|
android:summary="@string/summary_pref_local_dns_enabled"
|
||||||
android:title="@string/title_pref_local_dns_enabled" />
|
android:title="@string/title_pref_local_dns_enabled" />
|
||||||
|
|
||||||
<CheckBoxPreference
|
<CheckBoxPreference
|
||||||
android:key="pref_fake_dns_enabled"
|
android:key="pref_fake_dns_enabled"
|
||||||
android:summary="@string/summary_pref_fake_dns_enabled"
|
android:summary="@string/summary_pref_fake_dns_enabled"
|
||||||
android:title="@string/title_pref_fake_dns_enabled" />
|
android:title="@string/title_pref_fake_dns_enabled" />
|
||||||
|
|
||||||
<EditTextPreference
|
<EditTextPreference
|
||||||
android:key="pref_local_dns_port"
|
android:key="pref_local_dns_port"
|
||||||
android:summary="10853"
|
android:summary="10853"
|
||||||
android:inputType="number"
|
android:inputType="number"
|
||||||
android:title="@string/title_pref_local_dns_port" />
|
android:title="@string/title_pref_local_dns_port" />
|
||||||
|
|
||||||
<EditTextPreference
|
<EditTextPreference
|
||||||
android:key="pref_vpn_dns"
|
android:key="pref_vpn_dns"
|
||||||
android:summary="@string/summary_pref_remote_dns"
|
android:summary="@string/summary_pref_remote_dns"
|
||||||
android:title="@string/title_pref_vpn_dns" />
|
android:title="@string/title_pref_vpn_dns" />
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
<PreferenceCategory android:title="@string/title_pref_routing">
|
<PreferenceCategory android:title="@string/title_pref_routing">
|
||||||
@@ -67,6 +59,95 @@
|
|||||||
|
|
||||||
</PreferenceCategory>
|
</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"
|
||||||
|
android:summary="@string/summary_pref_auto_update_subscription"
|
||||||
|
android:title="@string/title_pref_auto_update_subscription" />
|
||||||
|
<EditTextPreference
|
||||||
|
android:key="pref_auto_update_interval"
|
||||||
|
android:summary="1440"
|
||||||
|
android:inputType="number"
|
||||||
|
android:title="@string/title_pref_auto_update_interval" />
|
||||||
|
</PreferenceCategory>
|
||||||
|
|
||||||
|
<PreferenceCategory android:title="@string/title_ui_settings">
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="pref_speed_enabled"
|
||||||
|
android:summary="@string/summary_pref_speed_enabled"
|
||||||
|
android:title="@string/title_pref_speed_enabled" />
|
||||||
|
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="pref_confirm_remove"
|
||||||
|
android:summary="@string/summary_pref_confirm_remove"
|
||||||
|
android:title="@string/title_pref_confirm_remove" />
|
||||||
|
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="pref_start_scan_immediate"
|
||||||
|
android:summary="@string/summary_pref_start_scan_immediate"
|
||||||
|
android:title="@string/title_pref_start_scan_immediate" />
|
||||||
|
|
||||||
|
<ListPreference
|
||||||
|
android:defaultValue="auto"
|
||||||
|
android:entries="@array/language_select"
|
||||||
|
android:entryValues="@array/language_select_value"
|
||||||
|
android:key="pref_language"
|
||||||
|
android:summary="%s"
|
||||||
|
android:title="@string/title_language" />
|
||||||
|
|
||||||
|
</PreferenceCategory>
|
||||||
|
|
||||||
<PreferenceCategory android:title="@string/title_advanced">
|
<PreferenceCategory android:title="@string/title_advanced">
|
||||||
|
|
||||||
<CheckBoxPreference
|
<CheckBoxPreference
|
||||||
@@ -104,9 +185,9 @@
|
|||||||
android:title="@string/title_pref_remote_dns" />
|
android:title="@string/title_pref_remote_dns" />
|
||||||
|
|
||||||
<EditTextPreference
|
<EditTextPreference
|
||||||
android:key="pref_domestic_dns"
|
android:key="pref_domestic_dns"
|
||||||
android:summary="@string/summary_pref_domestic_dns"
|
android:summary="@string/summary_pref_domestic_dns"
|
||||||
android:title="@string/title_pref_domestic_dns" />
|
android:title="@string/title_pref_domestic_dns" />
|
||||||
|
|
||||||
<ListPreference
|
<ListPreference
|
||||||
android:defaultValue="warning"
|
android:defaultValue="warning"
|
||||||
@@ -117,34 +198,12 @@
|
|||||||
android:title="@string/title_core_loglevel" />
|
android:title="@string/title_core_loglevel" />
|
||||||
|
|
||||||
<ListPreference
|
<ListPreference
|
||||||
android:defaultValue="VPN"
|
android:defaultValue="VPN"
|
||||||
android:entries="@array/mode_entries"
|
android:entries="@array/mode_entries"
|
||||||
android:entryValues="@array/mode_value"
|
android:entryValues="@array/mode_value"
|
||||||
android:key="pref_mode"
|
android:key="pref_mode"
|
||||||
android:summary="%s"
|
|
||||||
android:title="@string/title_mode" />
|
|
||||||
|
|
||||||
</PreferenceCategory>
|
|
||||||
|
|
||||||
<PreferenceCategory android:title="@string/title_ui_settings">
|
|
||||||
|
|
||||||
<CheckBoxPreference
|
|
||||||
android:key="pref_confirm_remove"
|
|
||||||
android:summary="@string/summary_pref_confirm_remove"
|
|
||||||
android:title="@string/title_pref_confirm_remove" />
|
|
||||||
|
|
||||||
<CheckBoxPreference
|
|
||||||
android:key="pref_start_scan_immediate"
|
|
||||||
android:summary="@string/summary_pref_start_scan_immediate"
|
|
||||||
android:title="@string/title_pref_start_scan_immediate" />
|
|
||||||
|
|
||||||
<ListPreference
|
|
||||||
android:defaultValue="auto"
|
|
||||||
android:entries="@array/language_select"
|
|
||||||
android:entryValues="@array/language_select_value"
|
|
||||||
android:key="pref_language"
|
|
||||||
android:summary="%s"
|
android:summary="%s"
|
||||||
android:title="@string/title_language" />
|
android:title="@string/title_mode" />
|
||||||
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
||||||
@@ -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.22" apply false
|
||||||
|
}
|
||||||
@@ -1,7 +1,3 @@
|
|||||||
|
|
||||||
buildToolsVer=33.0.2
|
|
||||||
compileSdkVer=33
|
|
||||||
targetSdkVer=33
|
|
||||||
kotlin.incremental=true
|
kotlin.incremental=true
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
android.enableJetifier=true
|
android.enableJetifier=true
|
||||||
|
|||||||
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
|||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
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"
|
rootProject.name = "V2rayNG"
|
||||||
include ':app'
|
include(":app")
|
||||||
Reference in New Issue
Block a user